Squashed 'third_party/SPIRV-Tools/' changes from c3f22f7cb..8aa423930

8aa423930 Avoid pessimizing std::move (#3124)
fad92f1e7 Fix typo in validation message (#3122)
578c5ac13 Change default version for CreatInstBindlessCheckPass to 2 (#3119)
9215c1b7d Fix convert-relax-to-half invalid code (#3099) (#3106)
64f36ea52 Support OpenCL.DebugInfo.100 extended instruction set (#3080)
e01bc6d4e spirv-fuzz: Always add new globals to entry point interfaces (#3113)
dcb7169bb spirv-fuzz: Transformation to add a new function to a module (#3114)
2e41d5ece spirv-fuzz: Avoid passing access chains as parameters (#3112)
e70b009b0 Add support for SPV_KHR_non_semantic_info (#3110)
38d7fbaad spirv-fuzz: Transformations to add types, constants and variables (#3101)
fccbc00ac Make Instrumentation format version 2 the default (Step 1) (#3096)
96354f504 spirv-fuzz: Fuzzer pass to merge blocks (#3097)
5c019b592 Start SPIRV-Tools v2020.1
c413b982c Finalize SPIRV-Tools v2019.5
2afbe9051 Update CHANGES
00ca4e5bd Don't crash when folding construct of empty struct (#3092)
0a2b38d08 spirv-fuzz: function outlining fuzzer pass (#3078)
983b5b4fc spirv-fuzz: Use validator to check break/continue dominance conditions (#3089)
e82a42860 WebGPU: Array size at most max signed int + 1 (#3077)
0a5d99d02 Permit the debug instructions in WebGPU SPIR-V - remove from the optimizer (#3083)
af7410597 graphics robust access: use signed clamp (#3073)
3ed458604 Folding: perform add and sub on mismatched integer types (#3084)
47f3eb426 spirv-fuzz: Fix invalid tests (#3079)
b334829a9 Validate nested constructs (#3068)
52e9cc930 spirv-fuzz: Improve debugging facilities (#3074)
54385458c Handle unreachable block when computing register pressure (#3070)
868ca3954 Improve RegisterSizePasses (#3059)
f31f26f73 utils/vscode: Add install.bat (#3071)
03957e8a9 build: cmake: Add support for Fuchsia. (#3062)
a62012ced Add test with explicit example of stripping reflection info (#3064)
8312c523e Permit the debug instructions in WebGPU SPIR-V (#3063)
85f3e93d1 Respect CMAKE_INSTALL_LIBDIR in installed CMake files (#3054)
45dde9ad6 Add missing dealloc (#3061)
2ee9aaa28 Initialize binary for use as guard later (#3058)
0391d0823 Handle OpPhi with no in operands in value numbering (#3056)
ca703c887 Kill the id-to-func map after wrap-opkill (#3055)
57b4cb40b Convert stderr and stdout in status to strings on assignment (#3049)

git-subtree-dir: third_party/SPIRV-Tools
git-subtree-split: 8aa423930db37e37086665efcc55944d577c06e5
diff --git a/Android.mk b/Android.mk
index cb7062c..7794c76 100644
--- a/Android.mk
+++ b/Android.mk
@@ -178,73 +178,43 @@
 
 # Locations of grammar files.
 #
-# TODO(dneto): Build a single set of tables that embeds versioning differences on
-# a per-item basis.  That must happen before SPIR-V 1.4, etc.
-# https://github.com/KhronosGroup/SPIRV-Tools/issues/1195
-SPV_CORE10_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.0/spirv.core.grammar.json
-SPV_CORE11_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.1/spirv.core.grammar.json
-SPV_CORE12_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.2/spirv.core.grammar.json
 SPV_COREUNIFIED1_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/spirv.core.grammar.json
-SPV_CORELATEST_GRAMMAR=$(SPV_COREUNIFIED1_GRAMMAR)
 SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.2/extinst.glsl.std.450.grammar.json
 SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.2/extinst.opencl.std.100.grammar.json
 # TODO(dneto): I expect the DebugInfo grammar file to eventually migrate to SPIRV-Headers
 SPV_DEBUGINFO_GRAMMAR=$(LOCAL_PATH)/source/extinst.debuginfo.grammar.json
+SPV_CLDEBUGINFO100_GRAMMAR=$(LOCAL_PATH)/source/extinst.opencl.debuginfo.100.grammar.json
 
 define gen_spvtools_grammar_tables
-$(call generate-file-dir,$(1)/core.insts-1.0.inc)
-$(1)/core.insts-1.0.inc $(1)/operand.kinds-1.0.inc $(1)/glsl.std.450.insts.inc $(1)/opencl.std.insts.inc: \
+$(call generate-file-dir,$(1)/core.insts-unified1.inc)
+$(1)/core.insts-unified1.inc $(1)/operand.kinds-unified1.inc \
+$(1)/glsl.std.450.insts.inc \
+$(1)/opencl.std.insts.inc \
+: \
         $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_CORE10_GRAMMAR) \
+        $(SPV_COREUNIFIED1_GRAMMAR) \
         $(SPV_GLSL_GRAMMAR) \
-        $(SPV_OPENCL_GRAMMAR) \
-        $(SPV_DEBUGINFO_GRAMMAR)
+        $(SPV_OpenCL_GRAMMAR) \
+        $(SPV_DEBUGINFO_GRAMMAR) \
+        $(SPV_CLDEBUGINFO100_GRAMMAR)
 		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-		                --spirv-core-grammar=$(SPV_CORE10_GRAMMAR) \
+		                --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \
 		                --extinst-glsl-grammar=$(SPV_GLSL_GRAMMAR) \
 		                --extinst-opencl-grammar=$(SPV_OPENCL_GRAMMAR) \
 		                --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
-		                --core-insts-output=$(1)/core.insts-1.0.inc \
+		                --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \
+		                --core-insts-output=$(1)/core.insts-unified1.inc \
 		                --glsl-insts-output=$(1)/glsl.std.450.insts.inc \
 		                --opencl-insts-output=$(1)/opencl.std.insts.inc \
-		                --operand-kinds-output=$(1)/operand.kinds-1.0.inc
-		@echo "[$(TARGET_ARCH_ABI)] Grammar v1.0   : instructions & operands <= grammar JSON files"
-$(1)/core.insts-1.1.inc $(1)/operand.kinds-1.1.inc: \
-        $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_CORE11_GRAMMAR) \
-        $(SPV_DEBUGINFO_GRAMMAR)
-		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-		                --spirv-core-grammar=$(SPV_CORE11_GRAMMAR) \
-		                --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
-		                --core-insts-output=$(1)/core.insts-1.1.inc \
-		                --operand-kinds-output=$(1)/operand.kinds-1.1.inc
-		@echo "[$(TARGET_ARCH_ABI)] Grammar v1.1   : instructions & operands <= grammar JSON files"
-$(1)/core.insts-1.2.inc $(1)/operand.kinds-1.2.inc: \
-        $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_CORE12_GRAMMAR) \
-        $(SPV_DEBUGINFO_GRAMMAR)
-		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-		                --spirv-core-grammar=$(SPV_CORE12_GRAMMAR) \
-		                --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
-		                --core-insts-output=$(1)/core.insts-1.2.inc \
-		                --operand-kinds-output=$(1)/operand.kinds-1.2.inc
-		@echo "[$(TARGET_ARCH_ABI)] Grammar v1.2   : instructions & operands <= grammar JSON files"
-$(1)/core.insts-unified1.inc $(1)/operand.kinds-unified1.inc: \
-        $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_COREUNIFIED1_GRAMMAR) \
-        $(SPV_DEBUGINFO_GRAMMAR)
-		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-		                --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \
-		                --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
-		                --core-insts-output=$(1)/core.insts-unified1.inc \
 		                --operand-kinds-output=$(1)/operand.kinds-unified1.inc
-		@echo "[$(TARGET_ARCH_ABI)] Grammar v1.3 (from unified1)  : instructions & operands <= grammar JSON files"
-$(LOCAL_PATH)/source/opcode.cpp: $(1)/core.insts-1.0.inc $(1)/core.insts-1.1.inc $(1)/core.insts-1.2.inc $(1)/core.insts-unified1.inc
-$(LOCAL_PATH)/source/operand.cpp: $(1)/operand.kinds-1.0.inc $(1)/operand.kinds-1.1.inc $(1)/operand.kinds-1.2.inc $(1)/operand.kinds-unified1.inc
+		@echo "[$(TARGET_ARCH_ABI)] Grammar (from unified1)  : instructions & operands <= grammar JSON files"
+$(LOCAL_PATH)/source/opcode.cpp: $(1)/core.insts-unified1.inc
+$(LOCAL_PATH)/source/operand.cpp: $(1)/operand.kinds-unified1.inc
 $(LOCAL_PATH)/source/ext_inst.cpp: \
 	$(1)/glsl.std.450.insts.inc \
 	$(1)/opencl.std.insts.inc \
 	$(1)/debuginfo.insts.inc \
+	$(1)/opencl.debuginfo.100.insts.inc \
 	$(1)/spv-amd-gcn-shader.insts.inc \
 	$(1)/spv-amd-shader-ballot.insts.inc \
 	$(1)/spv-amd-shader-explicit-vertex-parameter.insts.inc \
@@ -271,8 +241,9 @@
 		@echo "[$(TARGET_ARCH_ABI)] Generate language specific header for $(2): headers <= grammar"
 $(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).h
 endef
-# We generate language-specific headers for DebugInfo
+# We generate language-specific headers for DebugInfo and OpenCL.DebugInfo.100
 $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR)))
+$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(SPV_CLDEBUGINFO100_GRAMMAR)))
 
 
 define gen_spvtools_vendor_tables
@@ -282,22 +253,28 @@
         $(LOCAL_PATH)/source/extinst.$(2).grammar.json
 		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
 		    --extinst-vendor-grammar=$(LOCAL_PATH)/source/extinst.$(2).grammar.json \
-		    --vendor-insts-output=$(1)/$(2).insts.inc
+		    --vendor-insts-output=$(1)/$(2).insts.inc \
+		    --vendor-operand-kind-prefix=$(3)
 		@echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar"
 $(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc
 endef
-# Vendor extended instruction sets, with grammars from SPIRV-Tools source tree.
-SPV_NONSTANDARD_EXTINST_GRAMMARS=$(foreach F,$(wildcard $(LOCAL_PATH)/source/extinst.*.grammar.json),$(patsubst extinst.%.grammar.json,%,$(notdir $F)))
-$(foreach E,$(SPV_NONSTANDARD_EXTINST_GRAMMARS),$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),$E)))
+# Vendor and debug extended instruction sets, with grammars from SPIRV-Tools source tree.
+$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),debuginfo,""))
+$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),opencl.debuginfo.100,"CLDEBUG100_"))
+$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-gcn-shader,""))
+$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-ballot,""))
+$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-explicit-vertex-parameter,""))
+$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-trinary-minmax,""))
 
 define gen_spvtools_enum_string_mapping
 $(call generate-file-dir,$(1)/extension_enum.inc.inc)
 $(1)/extension_enum.inc $(1)/enum_string_mapping.inc: \
         $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_CORELATEST_GRAMMAR)
+        $(SPV_COREUNIFIED1_GRAMMAR)
 		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-		                --spirv-core-grammar=$(SPV_CORELATEST_GRAMMAR) \
+		                --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \
 		                --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
+		                --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \
 		                --extension-enum-output=$(1)/extension_enum.inc \
 		                --enum-string-mapping-output=$(1)/enum_string_mapping.inc
 		@echo "[$(TARGET_ARCH_ABI)] Generate enum<->string mapping <= grammar JSON files"
diff --git a/BUILD.bazel b/BUILD.bazel
index dc1ca60..3046781 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -2,6 +2,7 @@
     ":build_defs.bzl",
     "COMMON_COPTS",
     "DEBUGINFO_GRAMMAR_JSON_FILE",
+    "CLDEBUGINFO100_GRAMMAR_JSON_FILE",
     "TEST_COPTS",
     "base_test",
     "generate_core_tables",
@@ -38,12 +39,6 @@
     srcs = ["utils/generate_language_headers.py"],
 )
 
-generate_core_tables("1.0")
-
-generate_core_tables("1.1")
-
-generate_core_tables("1.2")
-
 generate_core_tables("unified1")
 
 generate_enum_string_mapping("unified1")
@@ -62,8 +57,12 @@
 
 generate_vendor_tables("debuginfo")
 
+generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_")
+
 generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE)
 
+generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE)
+
 py_binary(
     name = "generate_registry_tables",
     srcs = ["utils/generate_registry_tables.py"],
@@ -96,16 +95,15 @@
     name = "generated_headers",
     hdrs = [
         ":gen_build_version",
-        ":gen_core_tables_1.0",
-        ":gen_core_tables_1.1",
-        ":gen_core_tables_1.2",
         ":gen_core_tables_unified1",
         ":gen_enum_string_mapping",
         ":gen_extinst_lang_headers_DebugInfo",
+        ":gen_extinst_lang_headers_OpenCLDebugInfo100",
         ":gen_glsl_tables_unified1",
         ":gen_opencl_tables_unified1",
         ":gen_registry_tables",
         ":gen_vendor_tables_debuginfo",
+        ":gen_vendor_tables_opencl_debuginfo_100",
         ":gen_vendor_tables_spv_amd_gcn_shader",
         ":gen_vendor_tables_spv_amd_shader_ballot",
         ":gen_vendor_tables_spv_amd_shader_explicit_vertex_parameter",
diff --git a/BUILD.gn b/BUILD.gn
index 5c8eb9a..3c85c4e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -32,7 +32,8 @@
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
     core_insts_file = "${target_gen_dir}/core.insts-$version.inc"
     operand_kinds_file = "${target_gen_dir}/operand.kinds-$version.inc"
-    extinst_file = "source/extinst.debuginfo.grammar.json"
+    debuginfo_insts_file = "source/extinst.debuginfo.grammar.json"
+    cldebuginfo100_insts_file = "source/extinst.opencl.debuginfo.100.grammar.json"
 
     sources = [
       core_json_file,
@@ -47,7 +48,9 @@
       "--core-insts-output",
       rebase_path(core_insts_file, root_build_dir),
       "--extinst-debuginfo-grammar",
-      rebase_path(extinst_file, root_build_dir),
+      rebase_path(debuginfo_insts_file, root_build_dir),
+      "--extinst-cldebuginfo100-grammar",
+      rebase_path(cldebuginfo100_insts_file, root_build_dir),
       "--operand-kinds-output",
       rebase_path(operand_kinds_file, root_build_dir),
     ]
@@ -64,7 +67,8 @@
 
     core_json_file =
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
-    debug_insts_file = "source/extinst.debuginfo.grammar.json"
+    debuginfo_insts_file = "source/extinst.debuginfo.grammar.json"
+    cldebuginfo100_insts_file = "source/extinst.opencl.debuginfo.100.grammar.json"
     extension_enum_file = "${target_gen_dir}/extension_enum.inc"
     extension_map_file = "${target_gen_dir}/enum_string_mapping.inc"
 
@@ -72,7 +76,9 @@
       "--spirv-core-grammar",
       rebase_path(core_json_file, root_build_dir),
       "--extinst-debuginfo-grammar",
-      rebase_path(debug_insts_file, root_build_dir),
+      rebase_path(debuginfo_insts_file, root_build_dir),
+      "--extinst-cldebuginfo100-grammar",
+      rebase_path(cldebuginfo100_insts_file, root_build_dir),
       "--extension-enum-output",
       rebase_path(extension_enum_file, root_build_dir),
       "--enum-string-mapping-output",
@@ -100,7 +106,6 @@
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
     glsl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.glsl.std.450.grammar.json"
     glsl_insts_file = "${target_gen_dir}/glsl.std.450.insts.inc"
-    debug_insts_file = "source/extinst.debuginfo.grammar.json"
 
     args = [
       "--spirv-core-grammar",
@@ -109,8 +114,6 @@
       rebase_path(glsl_json_file, root_build_dir),
       "--glsl-insts-output",
       rebase_path(glsl_insts_file, root_build_dir),
-      "--extinst-debuginfo-grammar",
-      rebase_path(debug_insts_file, root_build_dir),
     ]
     inputs = [
       core_json_file,
@@ -134,7 +137,6 @@
         "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
     opengl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json"
     opencl_insts_file = "${target_gen_dir}/opencl.std.insts.inc"
-    debug_insts_file = "source/extinst.debuginfo.grammar.json"
 
     args = [
       "--spirv-core-grammar",
@@ -143,8 +145,6 @@
       rebase_path(opengl_json_file, root_build_dir),
       "--opencl-insts-output",
       rebase_path(opencl_insts_file, root_build_dir),
-      "--extinst-debuginfo-grammar",
-      rebase_path(debug_insts_file, root_build_dir),
     ]
     inputs = [
       core_json_file,
@@ -164,13 +164,12 @@
 
     name = invoker.name
     extinst_output_base = "${target_gen_dir}/${name}"
-    debug_insts_file = "source/extinst.debuginfo.grammar.json"
 
     args = [
       "--extinst-name",
       "${name}",
       "--extinst-grammar",
-      rebase_path(debug_insts_file, root_build_dir),
+      rebase_path(invoker.grammar_file, root_build_dir),
       "--extinst-output-base",
       rebase_path(extinst_output_base, root_build_dir),
     ]
@@ -198,6 +197,8 @@
       rebase_path(extinst_vendor_grammar, root_build_dir),
       "--vendor-insts-output",
       rebase_path(extinst_file, root_build_dir),
+      "--vendor-operand-kind-prefix",
+      invoker.operand_kind_prefix
     ]
     inputs = [
       extinst_vendor_grammar,
@@ -256,21 +257,28 @@
 spvtools_opencl_tables("opencl1-0") {
   version = "1.0"
 }
-spvtools_language_header("unified1") {
+spvtools_language_header("debuginfo") {
   name = "DebugInfo"
+  grammar_file = "source/extinst.debuginfo.grammar.json"
+}
+spvtools_language_header("cldebuginfo100") {
+  name = "OpenCLDebugInfo100"
+  grammar_file = "source/extinst.opencl.debuginfo.100.grammar.json"
 }
 
 spvtools_vendor_tables = [
-  "spv-amd-shader-explicit-vertex-parameter",
-  "spv-amd-shader-trinary-minmax",
-  "spv-amd-gcn-shader",
-  "spv-amd-shader-ballot",
-  "debuginfo",
+  ["spv-amd-shader-explicit-vertex-parameter", ""],
+  ["spv-amd-shader-trinary-minmax", ""],
+  ["spv-amd-gcn-shader", ""],
+  ["spv-amd-shader-ballot", ""],
+  ["debuginfo", ""],
+  ["opencl.debuginfo.100", "CLDEBUG100_"],
 ]
 
-foreach(table, spvtools_vendor_tables) {
+foreach(table_def, spvtools_vendor_tables) {
   spvtools_vendor_table(table) {
-    name = table
+    name = table_def[0]
+    operand_kind_prefix = table_def[1]
   }
 }
 
@@ -320,10 +328,12 @@
     ":spvtools_core_tables_unified1",
     ":spvtools_generators_inc",
     ":spvtools_glsl_tables_glsl1-0",
-    ":spvtools_language_header_unified1",
+    ":spvtools_language_header_debuginfo",
+    ":spvtools_language_header_cldebuginfo100",
     ":spvtools_opencl_tables_opencl1-0",
   ]
-  foreach(target_name, spvtools_vendor_tables) {
+  foreach(table_def, spvtools_vendor_tables) {
+    target_name = table_def[0]
     deps += [ ":spvtools_vendor_tables_$target_name" ]
   }
 
@@ -818,6 +828,7 @@
       "test/ext_inst.debuginfo_test.cpp",
       "test/ext_inst.glsl_test.cpp",
       "test/ext_inst.opencl_test.cpp",
+      "test/ext_inst.cldebug100_test.cpp",
       "test/fix_word_test.cpp",
       "test/generator_magic_number_test.cpp",
       "test/hex_float_test.cpp",
@@ -864,7 +875,8 @@
 
     deps = [
       ":spvtools",
-      ":spvtools_language_header_unified1",
+      ":spvtools_language_header_debuginfo",
+      ":spvtools_language_header_cldebuginfo100",
       ":spvtools_val",
       "//testing/gmock",
       "//testing/gtest",
diff --git a/CHANGES b/CHANGES
index 5263f12..2f2968e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
 Revision history for SPIRV-Tools
 
-v2019.5-dev 2019-10-21
+v2020.1-dev 2019-12-11
+ - Start v2020.1-dev
+
+v2019.5 2019-12-11
  - General:
    - Export SPIRV-Tools targets on installation
    - SPIRV-Tools support for SPIR-V 1.5 (#2865)
@@ -12,6 +15,12 @@
    - Improved CMake install step. (#2963)
    - Add fuzzer for spirv-dis call path (#2977)
    - Ensure timestamp does not vary with timezone. (#2982)
+   - Add a vscode extension for SPIR-V disassembly files (#2987)
+   - Add iOS as a supported platform (#3001)
+   - utils/vscode: Add SPIR-V language server support
+   - Respect CMAKE_INSTALL_LIBDIR in installed CMake files (#3054)
+   - Permit the debug instructions in WebGPU SPIR-V (#3063)
+   - Add support for Fuchsia. (#3062)
  - Optimizer
    - Add descriptor array scalar replacement (#2742)
    - Add pass to wrap OpKill in a function call (#2790)
@@ -24,6 +33,10 @@
    - Better handling of OpKill in continues (#2842,#2922,#2933)
    - Enable OpTypeCooperativeMatrix specialization (#2927)
    - Support constant-folding UConvert and SConvert (#2960)
+   - Update Offset to ConstOffset bitmask if operand is constant. (#3024)
+   - Improve RegisterSizePasses (#3059)
+   - Folding: perform add and sub on mismatched integer types (#3084)
+   - Graphics robust access: use signed clamp (#3073)
    Fixes:
    - Instrument: Fix version 2 output record write for tess eval shaders. (#2782)
    - Instrument: Add support for Buffer Device Address extension (#2792)
@@ -37,10 +50,12 @@
    - Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862)
    - Relaxed bitcast with pointers (#2878)
    - Validate physical storage buffer restrictions (#2930)
-   - Add SPV_KHR_shader_clock validation (#2879)
+   - Add SPV_KHR_shader_clock validation (#2879, #3013)
    - Validate that selections are structured (#2962)
    - Disallow use of OpCompositeExtract/OpCompositeInsert with no indices (#2980)
    - Check that derivatives operate on 32-bit values (#2983)
+   - Validate array stride does not cause overlap (#3028)
+   - Validate nested constructs (#3068)
    Fixes:
    - Fix validation of constant matrices (#2794)
    - Update "remquor" validation
@@ -63,6 +78,12 @@
    - Add fuzzer pass to change loop controls (#2949)
    - Add fuzzer pass to change function controls (#2951)
    - Add fuzzer pass to add NoContraction decorations (#2950)
+   - Add missing functionality for matrix composites (#2974)
+   - Fuzzer pass to adjust memory access operands (#2968)
+   - Transformation to extract from a composite object (#2991)
+   - Vector shuffle transformation (#3015)
+   - Improve debugging facilities (#3074)
+   - Function outlining fuzzer pass (#3078)
 
 
 v2019.4 2019-08-08
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 19108f3..6ed56a8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,7 +29,6 @@
 set(SPIRV_TOOLS "SPIRV-Tools")
 
 include(GNUInstallDirs)
-include(cmake/setup_build.cmake)
 
 set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 set(CMAKE_CXX_STANDARD 11)
@@ -54,6 +53,8 @@
   set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
   add_definitions(-DSPIRV_FREEBSD)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia")
+  add_definitions(-DSPIRV_FUCHSIA)
 else()
   message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!")
 endif()
@@ -215,7 +216,7 @@
     endmacro()
   else()
     macro(spvtools_config_package_dir TARGET PATH)
-      set(${PATH} lib/cmake/${TARGET})
+      set(${PATH} ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET})
     endmacro()
   endif()
 
diff --git a/build_defs.bzl b/build_defs.bzl
index 483fd2a..5d913a1 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -40,6 +40,7 @@
 })
 
 DEBUGINFO_GRAMMAR_JSON_FILE = "source/extinst.debuginfo.grammar.json"
+CLDEBUGINFO100_GRAMMAR_JSON_FILE = "source/extinst.opencl.debuginfo.100.grammar.json"
 
 def generate_core_tables(version = None):
     if not version:
@@ -47,6 +48,7 @@
     grammars = [
         "@spirv_headers//:spirv_core_grammar_" + version,
         DEBUGINFO_GRAMMAR_JSON_FILE,
+        CLDEBUGINFO100_GRAMMAR_JSON_FILE,
     ]
     outs = [
         "core.insts-{}.inc".format(version),
@@ -61,8 +63,9 @@
             "$(location :generate_grammar_tables) " +
             "--spirv-core-grammar=$(location {0}) " +
             "--extinst-debuginfo-grammar=$(location {1}) " +
-            "--core-insts-output=$(location {2}) " +
-            "--operand-kinds-output=$(location {3})"
+            "--extinst-cldebuginfo100-grammar=$(location {2}) " +
+            "--core-insts-output=$(location {3}) " +
+            "--operand-kinds-output=$(location {4})"
         ).format(*fmtargs),
         tools = [":generate_grammar_tables"],
         visibility = ["//visibility:private"],
@@ -74,6 +77,7 @@
     grammars = [
         "@spirv_headers//:spirv_core_grammar_" + version,
         DEBUGINFO_GRAMMAR_JSON_FILE,
+        CLDEBUGINFO100_GRAMMAR_JSON_FILE,
     ]
     outs = [
         "extension_enum.inc",
@@ -88,8 +92,9 @@
             "$(location :generate_grammar_tables) " +
             "--spirv-core-grammar=$(location {0}) " +
             "--extinst-debuginfo-grammar=$(location {1}) " +
-            "--extension-enum-output=$(location {2}) " +
-            "--enum-string-mapping-output=$(location {3})"
+            "--extinst-cldebuginfo100-grammar=$(location {2}) " +
+            "--extension-enum-output=$(location {3}) " +
+            "--enum-string-mapping-output=$(location {4})"
         ).format(*fmtargs),
         tools = [":generate_grammar_tables"],
         visibility = ["//visibility:private"],
@@ -137,13 +142,14 @@
         visibility = ["//visibility:private"],
     )
 
-def generate_vendor_tables(extension = None):
+def generate_vendor_tables(extension, operand_kind_prefix = ""):
     if not extension:
         fail("Must specify extension", "extension")
-    extension_rule = extension.replace("-", "_")
+    extension_rule = extension.replace("-", "_").replace(".", "_")
     grammars = ["source/extinst.{}.grammar.json".format(extension)]
     outs = ["{}.insts.inc".format(extension)]
-    fmtargs = grammars + outs
+    prefices = [operand_kind_prefix]
+    fmtargs = grammars + outs + prefices
     native.genrule(
         name = "gen_vendor_tables_" + extension_rule,
         srcs = grammars,
@@ -151,7 +157,8 @@
         cmd = (
             "$(location :generate_grammar_tables) " +
             "--extinst-vendor-grammar=$(location {0}) " +
-            "--vendor-insts-output=$(location {1})"
+            "--vendor-insts-output=$(location {1}) " +
+            "--vendor-operand-kind-prefix={2}"
         ).format(*fmtargs),
         tools = [":generate_grammar_tables"],
         visibility = ["//visibility:private"],
diff --git a/cmake/setup_build.cmake b/cmake/setup_build.cmake
deleted file mode 100644
index 6ba4c53..0000000
--- a/cmake/setup_build.cmake
+++ /dev/null
@@ -1,20 +0,0 @@
-# Find nosetests; see spirv_add_nosetests() for opting in to nosetests in a
-# specific directory.
-find_program(NOSETESTS_EXE NAMES nosetests PATHS $ENV{PYTHON_PACKAGE_PATH})
-if (NOT NOSETESTS_EXE)
-    message(STATUS "SPIRV-Tools: nosetests was not found - python support code will not be tested")
-else()
-    message(STATUS "SPIRV-Tools: nosetests found - python support code will be tested")
-endif()
-
-# Run nosetests on file ${PREFIX}_nosetest.py. Nosetests will look for classes
-# and functions whose names start with "nosetest". The test name will be
-# ${PREFIX}_nosetests.
-function(spirv_add_nosetests PREFIX)
-  if(NOT "${SPIRV_SKIP_TESTS}" AND NOSETESTS_EXE)
-    add_test(
-      NAME ${PREFIX}_nosetests
-      COMMAND ${NOSETESTS_EXE} -m "^[Nn]ose[Tt]est" -v
-        ${CMAKE_CURRENT_SOURCE_DIR}/${PREFIX}_nosetest.py)
-  endif()
-endfunction()
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 3190f4b..8bde13c 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -108,6 +108,9 @@
   set(protobuf_BUILD_TESTS OFF CACHE BOOL "Disable protobuf tests")
   set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Do not build protobuf static runtime")
   if (IS_DIRECTORY ${PROTOBUF_DIR})
+    if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
+      add_definitions(-Wno-inconsistent-missing-override)
+    endif()
     add_subdirectory(${PROTOBUF_DIR} EXCLUDE_FROM_ALL)
   else()
     message(FATAL_ERROR
diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp
index 681d008..2dcb333 100644
--- a/include/spirv-tools/instrument.hpp
+++ b/include/spirv-tools/instrument.hpp
@@ -35,10 +35,9 @@
 // generated by InstrumentPass::GenDebugStreamWrite. This method is utilized
 // by InstBindlessCheckPass.
 //
-// kInst2* values support version 2 of the output record format. These should
-// be used if available and version 2 is enabled. Version 1 is DEPRECATED.
-// Specifically, version 1 uses two words for the stage-specific section of
-// the output record; version 2 uses three words.
+// kInst2* values support version 2 of the output record format and were used
+// for the transition to this format. These values have now been transferred
+// to the original kInst* values. The kInst2* values are therefore DEPRECATED.
 //
 // The first member of the debug output buffer contains the next available word
 // in the data stream to be written. Shaders will atomically read and update
@@ -124,7 +123,7 @@
 static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
 
 // Size of Common and Stage-specific Members
-static const int kInstStageOutCnt = kInstCommonOutCnt + 2;
+static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
 static const int kInst2StageOutCnt = kInstCommonOutCnt + 3;
 
 // Validation Error Code Offset
@@ -160,6 +159,10 @@
 
 // A buffer address unalloc error will output the 64-bit pointer in
 // two 32-bit pieces, lower bits first.
+static const int kInstBuffAddrUnallocOutDescPtrLo = kInstStageOutCnt + 1;
+static const int kInstBuffAddrUnallocOutDescPtrHi = kInstStageOutCnt + 2;
+static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3;
+
 static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1;
 static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2;
 static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3;
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index dd2526b..5dcb81a 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -225,13 +225,23 @@
   // A sequence of zero or more pairs of (Id, Literal integer)
   LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER),
 
-  // The following are concrete enum types.
+  // The following are concrete enum types from the DebugInfo extended
+  // instruction set.
   SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS,  // DebugInfo Sec 3.2.  A mask.
   SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING,  // DebugInfo Sec 3.3
   SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE,                // DebugInfo Sec 3.4
   SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER,                // DebugInfo Sec 3.5
   SPV_OPERAND_TYPE_DEBUG_OPERATION,                     // DebugInfo Sec 3.6
 
+  // The following are concrete enum types from the OpenCL.DebugInfo.100
+  // extended instruction set.
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS,  // Sec 3.2. A Mask
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING,  // Sec 3.3
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE,                // Sec 3.4
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER,                // Sec 3.5
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,                     // Sec 3.6
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY,               // Sec 3.7
+
   // This is a sentinel value, and does not represent an operand type.
   // It should come last.
   SPV_OPERAND_TYPE_NUM_OPERAND_TYPES,
@@ -248,6 +258,12 @@
   SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER,
   SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
   SPV_EXT_INST_TYPE_DEBUGINFO,
+  SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+
+  // Multiple distinct extended instruction set types could return this
+  // value, if they are prefixed with NonSemantic. and are otherwise
+  // unrecognised
+  SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN,
 
   SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t)
 } spv_ext_inst_type_t;
@@ -627,6 +643,11 @@
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
     spv_fuzzer_options options, uint32_t shrinker_step_limit);
 
+// Enables running the validator after every pass is applied during a fuzzing
+// run.
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
+    spv_fuzzer_options options);
+
 // Encodes the given SPIR-V assembly text to its binary representation. The
 // length parameter specifies the number of bytes for text. Encoded binary will
 // be stored into *binary. Any error will be written into *diagnostic if
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index b09fdd4..ceadef8 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -229,6 +229,11 @@
     spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit);
   }
 
+  // See spvFuzzerOptionsEnableFuzzerPassValidation.
+  void enable_fuzzer_pass_validation() {
+    spvFuzzerOptionsEnableFuzzerPassValidation(options_);
+  }
+
  private:
   spv_fuzzer_options options_;
 };
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index 509051d..ba8cbaf 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -685,9 +685,9 @@
 // any resulting half precision values back to float32 as needed. No variables
 // are changed. No image operations are changed.
 //
-// Best if run late since it will generate better code with unneeded function
-// scope loads and stores and composite inserts and extracts removed. Also best
-// if followed by instruction simplification, redundancy elimination and DCE.
+// Best if run after function scope store/load and composite operation
+// eliminations are run. Also best if followed by instruction simplification,
+// redundancy elimination and DCE.
 Optimizer::PassToken CreateConvertRelaxedToHalfPass();
 
 // Create relax float ops pass.
@@ -748,7 +748,7 @@
 // |version| specifies the buffer record format.
 Optimizer::PassToken CreateInstBindlessCheckPass(
     uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false,
-    bool input_init_enable = false, uint32_t version = 1);
+    bool input_init_enable = false, uint32_t version = 2);
 
 // Create a pass to instrument physical buffer address checking
 // This pass instruments all physical buffer address references to check that
@@ -827,6 +827,16 @@
 //   of range. (The module is already invalid if that is the case.)
 // - TODO(dneto): The OpImageTexelPointer coordinate component is not 32-bits
 // wide.
+//
+// NOTE: Access chain indices are always treated as signed integers.  So
+//   if an array has a fixed size of more than 2^31 elements, then elements
+//   from 2^31 and above are never accessible with a 32-bit index,
+//   signed or unsigned.  For this case, this pass will clamp the index
+//   between 0 and at 2^31-1, inclusive.
+//   Similarly, if an array has more then 2^15 element and is accessed with
+//   a 16-bit index, then elements from 2^15 and above are not accessible.
+//   In this case, the pass will clamp the index between 0 and 2^15-1
+//   inclusive.
 Optimizer::PassToken CreateGraphicsRobustAccessPass();
 
 // Create descriptor scalar replacement pass.
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index f3b5942..4e7e10c 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -20,6 +20,7 @@
 # For now, assume the DebugInfo grammar file is in the current directory.
 # It might migrate to SPIRV-Headers.
 set(DEBUGINFO_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.debuginfo.grammar.json")
+set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.opencl.debuginfo.100.grammar.json")
 
 # macro() definitions are used in the following because we need to append .inc
 # file paths into some global lists (*_CPP_DEPENDS). And those global lists are
@@ -33,9 +34,13 @@
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
       --spirv-core-grammar=${GRAMMAR_JSON_FILE}
       --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
+      --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
       --core-insts-output=${GRAMMAR_INSTS_INC_FILE}
       --operand-kinds-output=${GRAMMAR_KINDS_INC_FILE}
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
+    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT}
+            ${GRAMMAR_JSON_FILE}
+            ${DEBUGINFO_GRAMMAR_JSON_FILE}
+            ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
     COMMENT "Generate info tables for SPIR-V v${CONFIG_VERSION} core instructions and operands.")
   list(APPEND OPCODE_CPP_DEPENDS ${GRAMMAR_INSTS_INC_FILE})
   list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE})
@@ -50,9 +55,13 @@
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
       --spirv-core-grammar=${GRAMMAR_JSON_FILE}
       --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
+      --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
       --extension-enum-output=${GRAMMAR_EXTENSION_ENUM_INC_FILE}
       --enum-string-mapping-output=${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
+    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT}
+            ${GRAMMAR_JSON_FILE}
+            ${DEBUGINFO_GRAMMAR_JSON_FILE}
+            ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
     COMMENT "Generate enum-string mapping for SPIR-V v${CONFIG_VERSION}.")
   list(APPEND EXTENSION_H_DEPENDS ${GRAMMAR_EXTENSION_ENUM_INC_FILE})
   list(APPEND ENUM_STRING_MAPPING_CPP_DEPENDS ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE})
@@ -101,13 +110,14 @@
   list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE})
 endmacro(spvtools_opencl_tables)
 
-macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME)
+macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX)
   set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc")
   set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
   add_custom_command(OUTPUT ${INSTS_FILE}
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
       --extinst-vendor-grammar=${GRAMMAR_FILE}
       --vendor-insts-output=${INSTS_FILE}
+      --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX}
     DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
     COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.")
   add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE})
@@ -134,12 +144,14 @@
 spvtools_enum_string_mapping("unified1")
 spvtools_opencl_tables("unified1")
 spvtools_glsl_tables("unified1")
-spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp")
-spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm")
-spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs")
-spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb")
-spvtools_vendor_tables("debuginfo" "debuginfo")
+spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp" "")
+spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm" "")
+spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "")
+spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "")
+spvtools_vendor_tables("debuginfo" "debuginfo" "")
+spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_")
 spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
+spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE})
 
 spvtools_vimsyntax("unified1" "1.0")
 add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE})
diff --git a/source/binary.cpp b/source/binary.cpp
index 1d31283..0463061 100644
--- a/source/binary.cpp
+++ b/source/binary.cpp
@@ -477,9 +477,22 @@
       assert(SpvOpExtInst == opcode);
       assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE);
       spv_ext_inst_desc ext_inst;
-      if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst))
-        return diagnostic() << "Invalid extended instruction number: " << word;
-      spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
+      if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) ==
+          SPV_SUCCESS) {
+        // if we know about this ext inst, push the expected operands
+        spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
+      } else {
+        // if we don't know this extended instruction and the set isn't
+        // non-semantic, we cannot process further
+        if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) {
+          return diagnostic()
+                 << "Invalid extended instruction number: " << word;
+        } else {
+          // for non-semantic instruction sets, we know the form of all such
+          // extended instructions contains a series of IDs as parameters
+          expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
+        }
+      }
     } break;
 
     case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
@@ -623,7 +636,12 @@
     case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
     case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
-    case SPV_OPERAND_TYPE_DEBUG_OPERATION: {
+    case SPV_OPERAND_TYPE_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: {
       // A single word that is a plain enum value.
 
       // Map an optional operand type to its corresponding concrete type.
@@ -647,6 +665,7 @@
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: {
       // This operand is a mask.
 
diff --git a/source/disassemble.cpp b/source/disassemble.cpp
index c116f50..4b3972b 100644
--- a/source/disassemble.cpp
+++ b/source/disassemble.cpp
@@ -217,10 +217,18 @@
       break;
     case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
       spv_ext_inst_desc ext_inst;
-      if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst))
-        assert(false && "should have caught this earlier");
       SetRed();
-      stream_ << ext_inst->name;
+      if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) ==
+          SPV_SUCCESS) {
+        stream_ << ext_inst->name;
+      } else {
+        if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) {
+          assert(false && "should have caught this earlier");
+        } else {
+          // for non-semantic instruction sets we can just print the number
+          stream_ << word;
+        }
+      }
     } break;
     case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
       spv_opcode_desc opcode_desc;
@@ -272,7 +280,12 @@
     case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
     case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
-    case SPV_OPERAND_TYPE_DEBUG_OPERATION: {
+    case SPV_OPERAND_TYPE_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: {
       spv_operand_desc entry;
       if (grammar_.lookupOperand(operand.type, word, &entry))
         assert(false && "should have caught this earlier");
@@ -285,6 +298,7 @@
     case SPV_OPERAND_TYPE_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
       EmitMaskOperand(operand.type, word);
       break;
     default:
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp
index 0499e23..e332f0d 100644
--- a/source/ext_inst.cpp
+++ b/source/ext_inst.cpp
@@ -28,6 +28,7 @@
 
 #include "debuginfo.insts.inc"
 #include "glsl.std.450.insts.inc"
+#include "opencl.debuginfo.100.insts.inc"
 #include "opencl.std.insts.inc"
 
 #include "spirv-tools/libspirv.h"
@@ -51,6 +52,8 @@
      ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries},
     {SPV_EXT_INST_TYPE_DEBUGINFO, ARRAY_SIZE(debuginfo_entries),
      debuginfo_entries},
+    {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+     ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries},
 };
 
 static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0),
@@ -116,9 +119,24 @@
   if (!strcmp("DebugInfo", name)) {
     return SPV_EXT_INST_TYPE_DEBUGINFO;
   }
+  if (!strcmp("OpenCL.DebugInfo.100", name)) {
+    return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100;
+  }
+  // ensure to add any known non-semantic extended instruction sets
+  // above this point, and update spvExtInstIsNonSemantic()
+  if (!strncmp("NonSemantic.", name, 12)) {
+    return SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN;
+  }
   return SPV_EXT_INST_TYPE_NONE;
 }
 
+bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) {
+  if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN) {
+    return true;
+  }
+  return false;
+}
+
 spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
                                        const spv_ext_inst_type_t type,
                                        const char* name,
diff --git a/source/ext_inst.h b/source/ext_inst.h
index a821cc2..b42d82b 100644
--- a/source/ext_inst.h
+++ b/source/ext_inst.h
@@ -21,6 +21,9 @@
 // Gets the type of the extended instruction set with the specified name.
 spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name);
 
+// Returns true if the extended instruction set is non-semantic
+bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type);
+
 // Finds the named extented instruction of the given type in the given extended
 // instruction table. On success, returns SPV_SUCCESS and writes a handle of
 // the instruction entry into *entry.
diff --git a/source/extinst.opencl.debuginfo.100.grammar.json b/source/extinst.opencl.debuginfo.100.grammar.json
new file mode 100644
index 0000000..08062be
--- /dev/null
+++ b/source/extinst.opencl.debuginfo.100.grammar.json
@@ -0,0 +1,632 @@
+{
+  "copyright" : [
+    "Copyright (c) 2018 The Khronos Group Inc.",
+    "",
+    "Permission is hereby granted, free of charge, to any person obtaining a copy",
+    "of this software and/or associated documentation files (the \"Materials\"),",
+    "to deal in the Materials without restriction, including without limitation",
+    "the rights to use, copy, modify, merge, publish, distribute, sublicense,",
+    "and/or sell copies of the Materials, and to permit persons to whom the",
+    "Materials are furnished to do so, subject to the following conditions:",
+    "",
+    "The above copyright notice and this permission notice shall be included in",
+    "all copies or substantial portions of the Materials.",
+    "",
+    "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS",
+    "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND",
+    "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ",
+    "",
+    "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS",
+    "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
+    "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL",
+    "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
+    "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING",
+    "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS",
+    "IN THE MATERIALS."
+  ],
+  "version" : 200,
+  "revision" : 2,
+  "instructions" : [
+    {
+      "opname" : "DebugInfoNone",
+      "opcode" : 0
+    },
+    {
+      "opname" : "DebugCompilationUnit",
+      "opcode" : 1,
+      "operands" : [
+        { "kind" : "LiteralInteger", "name" : "'Version'" },
+        { "kind" : "LiteralInteger", "name" : "'DWARF Version'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "SourceLanguage", "name" : "'Language'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeBasic",
+      "opcode" : 2,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypePointer",
+      "opcode" : 3,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "StorageClass", "name" : "'Storage Class'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeQualifier",
+      "opcode" : 4,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeArray",
+      "opcode" : 5,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeVector",
+      "opcode" : 6,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "LiteralInteger", "name" : "'Component Count'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypedef",
+      "opcode" : 7,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeFunction",
+      "opcode" : 8,
+      "operands" : [
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Return Type'" },
+        { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeEnum",
+      "opcode" : 9,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Underlying Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeComposite",
+      "opcode" : 10,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "DebugCompositeType", "name" : "'Tag'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeMember",
+      "opcode" : 11,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Offset'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeInheritance",
+      "opcode" : 12,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Child'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Offset'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypePtrToMember",
+      "opcode" : 13,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Member Type'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplate",
+      "opcode" : 14,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Target'" },
+        { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplateParameter",
+      "opcode" : 15,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Actual Type'" },
+        { "kind" : "IdRef", "name" : "'Value'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplateTemplateParameter",
+      "opcode" : 16,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Template Name'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplateParameterPack",
+      "opcode" : 17,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugGlobalVariable",
+      "opcode" : 18,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "IdRef", "name" : "'Variable'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugFunctionDeclaration",
+      "opcode" : 19,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+      ]
+    },
+    {
+      "opname" : "DebugFunction",
+      "opcode" : 20,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "LiteralInteger", "name" : "'Scope Line'" },
+        { "kind" : "IdRef", "name" : "'Function'" },
+        { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugLexicalBlock",
+      "opcode" : 21,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugLexicalBlockDiscriminator",
+      "opcode" : 22,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Discriminator'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugScope",
+      "opcode" : 23,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Scope'" },
+        { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugNoScope",
+      "opcode" : 24
+    },
+    {
+      "opname" : "DebugInlinedAt",
+      "opcode" : 25,
+      "operands" : [
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "IdRef", "name" : "'Scope'" },
+        { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugLocalVariable",
+      "opcode" : 26,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugInlinedVariable",
+      "opcode" : 27,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Variable'" },
+        { "kind" : "IdRef", "name" : "'Inlined'" }
+      ]
+    },
+    {
+      "opname" : "DebugDeclare",
+      "opcode" : 28,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Local Variable'" },
+        { "kind" : "IdRef", "name" : "'Variable'" },
+        { "kind" : "IdRef", "name" : "'Expression'" }
+      ]
+    },
+    {
+      "opname" : "DebugValue",
+      "opcode" : 29,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Local Variable'" },
+        { "kind" : "IdRef", "name" : "'Value'" },
+        { "kind" : "IdRef", "name" : "'Expression'" },
+        { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugOperation",
+      "opcode" : 30,
+      "operands" : [
+        { "kind" : "DebugOperation", "name" : "'OpCode'" },
+        { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugExpression",
+      "opcode" : 31,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugMacroDef",
+      "opcode" : 32,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugMacroUndef",
+      "opcode" : 33,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "IdRef", "name" : "'Macro'" }
+      ]
+    },
+    {
+      "opname" : "DebugImportedEntity",
+      "opcode" : 34,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "DebugImportedEntity", "name" : "'Tag'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "IdRef", "name" : "'Entity'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugSource",
+      "opcode" : 35,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'File'" },
+        { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" }
+      ]
+    }
+  ],
+  "operand_kinds" : [
+    {
+      "category" : "BitEnum",
+      "kind" : "DebugInfoFlags",
+      "enumerants" : [
+        {
+          "enumerant" : "FlagIsProtected",
+          "value" : "0x01"
+        },
+        {
+          "enumerant" : "FlagIsPrivate",
+          "value" : "0x02"
+        },
+        {
+          "enumerant" : "FlagIsPublic",
+          "value" : "0x03"
+        },
+        {
+          "enumerant" : "FlagIsLocal",
+          "value" : "0x04"
+        },
+        {
+          "enumerant" : "FlagIsDefinition",
+          "value" : "0x08"
+        },
+        {
+          "enumerant" : "FlagFwdDecl",
+          "value" : "0x10"
+        },
+        {
+          "enumerant" : "FlagArtificial",
+          "value" : "0x20"
+        },
+        {
+          "enumerant" : "FlagExplicit",
+          "value" : "0x40"
+        },
+        {
+          "enumerant" : "FlagPrototyped",
+          "value" : "0x80"
+        },
+        {
+          "enumerant" : "FlagObjectPointer",
+          "value" : "0x100"
+        },
+        {
+          "enumerant" : "FlagStaticMember",
+          "value" : "0x200"
+        },
+        {
+          "enumerant" : "FlagIndirectVariable",
+          "value" : "0x400"
+        },
+        {
+          "enumerant" : "FlagLValueReference",
+          "value" : "0x800"
+        },
+        {
+          "enumerant" : "FlagRValueReference",
+          "value" : "0x1000"
+        },
+        {
+          "enumerant" : "FlagIsOptimized",
+          "value" : "0x2000"
+        },
+        {
+          "enumerant" : "FlagIsEnumClass",
+          "value" : "0x4000"
+        },
+        {
+          "enumerant" : "FlagTypePassByValue",
+          "value" : "0x8000"
+        },
+        {
+          "enumerant" : "FlagTypePassByReference",
+          "value" : "0x10000"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugBaseTypeAttributeEncoding",
+      "enumerants" : [
+        {
+          "enumerant" : "Unspecified",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "Address",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "Boolean",
+          "value" : "2"
+        },
+        {
+          "enumerant" : "Float",
+          "value" : "3"
+        },
+        {
+          "enumerant" : "Signed",
+          "value" : "4"
+        },
+        {
+          "enumerant" : "SignedChar",
+          "value" : "5"
+        },
+        {
+          "enumerant" : "Unsigned",
+          "value" : "6"
+        },
+        {
+          "enumerant" : "UnsignedChar",
+          "value" : "7"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugCompositeType",
+      "enumerants" : [
+        {
+          "enumerant" : "Class",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "Structure",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "Union",
+          "value" : "2"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugTypeQualifier",
+      "enumerants" : [
+        {
+          "enumerant" : "ConstType",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "VolatileType",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "RestrictType",
+          "value" : "2"
+        },
+        {
+          "enumerant" : "AtomicType",
+          "value" : "3"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugOperation",
+      "enumerants" : [
+        {
+          "enumerant" : "Deref",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "Plus",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "Minus",
+          "value" : "2"
+        },
+        {
+          "enumerant" : "PlusUconst",
+          "value" : "3",
+          "parameters" : [
+             { "kind" : "LiteralInteger" }
+          ]
+        },
+        {
+          "enumerant" : "BitPiece",
+          "value" : "4",
+          "parameters" : [
+             { "kind" : "LiteralInteger" },
+             { "kind" : "LiteralInteger" }
+          ]
+        },
+        {
+          "enumerant" : "Swap",
+          "value" : "5"
+        },
+        {
+          "enumerant" : "Xderef",
+          "value" : "6"
+        },
+        {
+          "enumerant" : "StackValue",
+          "value" : "7"
+        },
+        {
+          "enumerant" : "Constu",
+          "value" : "8",
+          "parameters" : [
+             { "kind" : "LiteralInteger" }
+          ]
+        },
+        {
+          "enumerant" : "Fragment",
+          "value" : "9",
+          "parameters" : [
+             { "kind" : "LiteralInteger" },
+             { "kind" : "LiteralInteger" }
+          ]
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugImportedEntity",
+      "enumerants" : [
+        {
+          "enumerant" : "ImportedModule",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "ImportedDeclaration",
+          "value" : "1"
+	}
+      ]
+    }
+  ]
+}
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 97f8976..bc7d453 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -47,12 +47,15 @@
         fuzzer_pass_apply_id_synonyms.h
         fuzzer_pass_construct_composites.h
         fuzzer_pass_copy_objects.h
+        fuzzer_pass_merge_blocks.h
         fuzzer_pass_obfuscate_constants.h
+        fuzzer_pass_outline_functions.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_split_blocks.h
         fuzzer_util.h
         id_use_descriptor.h
         instruction_descriptor.h
+        instruction_message.h
         protobufs/spirvfuzz_protobufs.h
         pseudo_random_generator.h
         random_generator.h
@@ -60,18 +63,29 @@
         shrinker.h
         transformation.h
         transformation_add_constant_boolean.h
+        transformation_add_constant_composite.h
         transformation_add_constant_scalar.h
         transformation_add_dead_break.h
         transformation_add_dead_continue.h
+        transformation_add_function.h
+        transformation_add_global_undef.h
+        transformation_add_global_variable.h
         transformation_add_no_contraction_decoration.h
+        transformation_add_type_array.h
         transformation_add_type_boolean.h
         transformation_add_type_float.h
+        transformation_add_type_function.h
         transformation_add_type_int.h
+        transformation_add_type_matrix.h
         transformation_add_type_pointer.h
+        transformation_add_type_struct.h
+        transformation_add_type_vector.h
         transformation_composite_construct.h
         transformation_composite_extract.h
         transformation_copy_object.h
+        transformation_merge_blocks.h
         transformation_move_block_down.h
+        transformation_outline_function.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
         transformation_replace_id_with_synonym.h
@@ -101,30 +115,44 @@
         fuzzer_pass_apply_id_synonyms.cpp
         fuzzer_pass_construct_composites.cpp
         fuzzer_pass_copy_objects.cpp
+        fuzzer_pass_merge_blocks.cpp
         fuzzer_pass_obfuscate_constants.cpp
+        fuzzer_pass_outline_functions.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
         instruction_descriptor.cpp
+        instruction_message.cpp
         pseudo_random_generator.cpp
         random_generator.cpp
         replayer.cpp
         shrinker.cpp
         transformation.cpp
         transformation_add_constant_boolean.cpp
+        transformation_add_constant_composite.cpp
         transformation_add_constant_scalar.cpp
         transformation_add_dead_break.cpp
         transformation_add_dead_continue.cpp
+        transformation_add_function.cpp
+        transformation_add_global_undef.cpp
+        transformation_add_global_variable.cpp
         transformation_add_no_contraction_decoration.cpp
+        transformation_add_type_array.cpp
         transformation_add_type_boolean.cpp
         transformation_add_type_float.cpp
+        transformation_add_type_function.cpp
         transformation_add_type_int.cpp
+        transformation_add_type_matrix.cpp
         transformation_add_type_pointer.cpp
+        transformation_add_type_struct.cpp
+        transformation_add_type_vector.cpp
         transformation_composite_construct.cpp
         transformation_composite_extract.cpp
         transformation_copy_object.cpp
+        transformation_merge_blocks.cpp
         transformation_move_block_down.cpp
+        transformation_outline_function.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
         transformation_replace_id_with_synonym.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 01b4258..95913d0 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -31,7 +31,9 @@
 #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
 #include "source/fuzz/fuzzer_pass_construct_composites.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
+#include "source/fuzz/fuzzer_pass_merge_blocks.h"
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
+#include "source/fuzz/fuzzer_pass_outline_functions.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@@ -65,13 +67,26 @@
 }  // namespace
 
 struct Fuzzer::Impl {
-  explicit Impl(spv_target_env env) : target_env(env) {}
+  explicit Impl(spv_target_env env, uint32_t random_seed,
+                bool validate_after_each_pass)
+      : target_env(env),
+        seed(random_seed),
+        validate_after_each_fuzzer_pass(validate_after_each_pass) {}
 
-  const spv_target_env target_env;  // Target environment.
-  MessageConsumer consumer;         // Message consumer.
+  bool ApplyPassAndCheckValidity(FuzzerPass* pass,
+                                 const opt::IRContext& ir_context,
+                                 const spvtools::SpirvTools& tools) const;
+
+  const spv_target_env target_env;       // Target environment.
+  const uint32_t seed;                   // Seed for random number generator.
+  bool validate_after_each_fuzzer_pass;  // Determines whether the validator
+  // should be invoked after every fuzzer pass.
+  MessageConsumer consumer;  // Message consumer.
 };
 
-Fuzzer::Fuzzer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
+Fuzzer::Fuzzer(spv_target_env env, uint32_t seed,
+               bool validate_after_each_fuzzer_pass)
+    : impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass)) {}
 
 Fuzzer::~Fuzzer() = default;
 
@@ -79,10 +94,27 @@
   impl_->consumer = std::move(c);
 }
 
+bool Fuzzer::Impl::ApplyPassAndCheckValidity(
+    FuzzerPass* pass, const opt::IRContext& ir_context,
+    const spvtools::SpirvTools& tools) const {
+  pass->Apply();
+  if (validate_after_each_fuzzer_pass) {
+    std::vector<uint32_t> binary_to_validate;
+    ir_context.module()->ToBinary(&binary_to_validate, false);
+    if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size())) {
+      consumer(SPV_MSG_INFO, nullptr, {},
+               "Binary became invalid during fuzzing (set a breakpoint to "
+               "inspect); stopping.");
+      return false;
+    }
+  }
+  return true;
+}
+
 Fuzzer::FuzzerResultStatus Fuzzer::Run(
     const std::vector<uint32_t>& binary_in,
     const protobufs::FactSequence& initial_facts,
-    spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out,
+    std::vector<uint32_t>* binary_out,
     protobufs::TransformationSequence* transformation_sequence_out) const {
   // Check compatibility between the library version being linked with and the
   // header files being used.
@@ -108,10 +140,8 @@
       impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size());
   assert(ir_context);
 
-  // Make a PRNG, either from a given seed or from a random device.
-  PseudoRandomGenerator random_generator(
-      options->has_random_seed ? options->random_seed
-                               : static_cast<uint32_t>(std::random_device()()));
+  // Make a PRNG from the seed passed to the fuzzer on creation.
+  PseudoRandomGenerator random_generator(impl_->seed);
 
   // The fuzzer will introduce new ids into the module.  The module's id bound
   // gives the smallest id that can be used for this purpose.  We add an offset
@@ -128,9 +158,13 @@
 
   // Add some essential ingredients to the module if they are not already
   // present, such as boolean constants.
-  FuzzerPassAddUsefulConstructs(ir_context.get(), &fact_manager,
-                                &fuzzer_context, transformation_sequence_out)
-      .Apply();
+  FuzzerPassAddUsefulConstructs add_useful_constructs(
+      ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
+  if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context,
+                                        tools)) {
+    return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
+  }
 
   // Apply some semantics-preserving passes.
   std::vector<std::unique_ptr<FuzzerPass>> passes;
@@ -150,9 +184,15 @@
     MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
                                         &fact_manager, &fuzzer_context,
                                         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassMergeBlocks>(&passes, ir_context.get(),
+                                        &fact_manager, &fuzzer_context,
+                                        transformation_sequence_out);
     MaybeAddPass<FuzzerPassObfuscateConstants>(&passes, ir_context.get(),
                                                &fact_manager, &fuzzer_context,
                                                transformation_sequence_out);
+    MaybeAddPass<FuzzerPassOutlineFunctions>(&passes, ir_context.get(),
+                                             &fact_manager, &fuzzer_context,
+                                             transformation_sequence_out);
     MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
                                           &fact_manager, &fuzzer_context,
                                           transformation_sequence_out);
@@ -168,7 +208,11 @@
          (is_first ||
           fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
     is_first = false;
-    passes[fuzzer_context.RandomIndex(passes)]->Apply();
+    if (!impl_->ApplyPassAndCheckValidity(
+            passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context,
+            tools)) {
+      return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
+    }
   }
 
   // Now apply some passes that it does not make sense to apply repeatedly,
@@ -186,15 +230,13 @@
   MaybeAddPass<FuzzerPassAdjustSelectionControls>(
       &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
       transformation_sequence_out);
+  MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
+      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
   for (auto& pass : final_passes) {
-    pass->Apply();
-  }
-
-  if (fuzzer_context.ChooseEven()) {
-    FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager,
-                                          &fuzzer_context,
-                                          transformation_sequence_out)
-        .Apply();
+    if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
+      return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
+    }
   }
 
   // Encode the module as a binary.
diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h
index a257c2a..c1d2dee 100644
--- a/source/fuzz/fuzzer.h
+++ b/source/fuzz/fuzzer.h
@@ -32,11 +32,16 @@
   enum class FuzzerResultStatus {
     kComplete,
     kFailedToCreateSpirvToolsInterface,
+    kFuzzerPassLedToInvalidModule,
     kInitialBinaryInvalid,
   };
 
-  // Constructs a fuzzer from the given target environment.
-  explicit Fuzzer(spv_target_env env);
+  // Constructs a fuzzer from the given target environment |env|.  |seed| is a
+  // seed for pseudo-random number generation.
+  // |validate_after_each_fuzzer_pass| controls whether the validator will be
+  // invoked after every fuzzer pass is applied.
+  explicit Fuzzer(spv_target_env env, uint32_t seed,
+                  bool validate_after_each_fuzzer_pass);
 
   // Disables copy/move constructor/assignment operations.
   Fuzzer(const Fuzzer&) = delete;
@@ -51,14 +56,13 @@
   void SetMessageConsumer(MessageConsumer consumer);
 
   // Transforms |binary_in| to |binary_out| by running a number of randomized
-  // fuzzer passes, controlled via |options|.  Initial facts about the input
-  // binary and the context in which it will execute are provided via
-  // |initial_facts|.  The transformation sequence that was applied is returned
-  // via |transformation_sequence_out|.
+  // fuzzer passes.  Initial facts about the input binary and the context in
+  // which it will execute are provided via |initial_facts|.  The transformation
+  // sequence that was applied is returned via |transformation_sequence_out|.
   FuzzerResultStatus Run(
       const std::vector<uint32_t>& binary_in,
       const protobufs::FactSequence& initial_facts,
-      spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out,
+      std::vector<uint32_t>* binary_out,
       protobufs::TransformationSequence* transformation_sequence_out) const;
 
  private:
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 356cb35..98585d9 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -34,10 +34,12 @@
                                                                             90};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
                                                                           90};
-const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
 const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
 
@@ -81,10 +83,13 @@
   chance_of_constructing_composite_ =
       ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
   chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
+  chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
   chance_of_moving_block_down_ =
       ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
   chance_of_obfuscating_constant_ =
       ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
+  chance_of_outlining_function_ =
+      ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
   chance_of_replacing_id_with_synonym_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index c8242e6..619c131 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -81,10 +81,14 @@
     return chance_of_constructing_composite_;
   }
   uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
+  uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; }
   uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
   uint32_t GetChanceOfObfuscatingConstant() {
     return chance_of_obfuscating_constant_;
   }
+  uint32_t GetChanceOfOutliningFunction() {
+    return chance_of_outlining_function_;
+  }
   uint32_t GetChanceOfReplacingIdWithSynonym() {
     return chance_of_replacing_id_with_synonym_;
   }
@@ -119,8 +123,10 @@
   uint32_t chance_of_adjusting_selection_control_;
   uint32_t chance_of_constructing_composite_;
   uint32_t chance_of_copying_object_;
+  uint32_t chance_of_merging_blocks_;
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_obfuscating_constant_;
+  uint32_t chance_of_outlining_function_;
   uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_splitting_block_;
 
diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp
index 9eb5631..ff0adab 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -148,7 +148,6 @@
         transformation.Apply(GetIRContext(), GetFactManager());
         *GetTransformations()->add_transformation() =
             transformation.ToMessage();
-        // Indicate that one instruction was added.
       });
 }
 
diff --git a/source/fuzz/fuzzer_pass_merge_blocks.cpp b/source/fuzz/fuzzer_pass_merge_blocks.cpp
new file mode 100644
index 0000000..ca1bfb3
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_merge_blocks.cpp
@@ -0,0 +1,65 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_merge_blocks.h"
+
+#include <vector>
+
+#include "source/fuzz/transformation_merge_blocks.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassMergeBlocks::FuzzerPassMergeBlocks(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default;
+
+void FuzzerPassMergeBlocks::Apply() {
+  // First we populate a sequence of transformations that we might consider
+  // applying.
+  std::vector<TransformationMergeBlocks> potential_transformations;
+  // We do this by considering every block of every function.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      // We probabilistically decide to ignore some blocks.
+      if (!GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfMergingBlocks())) {
+        continue;
+      }
+      // For other blocks, we add a transformation to merge the block into its
+      // predecessor if that transformation would be applicable.
+      TransformationMergeBlocks transformation(block.id());
+      if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+        potential_transformations.push_back(transformation);
+      }
+    }
+  }
+
+  while (!potential_transformations.empty()) {
+    uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
+    auto transformation = potential_transformations.at(index);
+    potential_transformations.erase(potential_transformations.begin() + index);
+    if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+      transformation.Apply(GetIRContext(), GetFactManager());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_merge_blocks.h b/source/fuzz/fuzzer_pass_merge_blocks.h
new file mode 100644
index 0000000..457e591
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_merge_blocks.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for merging blocks in the module.
+class FuzzerPassMergeBlocks : public FuzzerPass {
+ public:
+  FuzzerPassMergeBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+                        FuzzerContext* fuzzer_context,
+                        protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassMergeBlocks();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_
diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp
new file mode 100644
index 0000000..d59c195
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_outline_functions.cpp
@@ -0,0 +1,99 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_outline_functions.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_outline_function.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default;
+
+void FuzzerPassOutlineFunctions::Apply() {
+  std::vector<opt::Function*> original_functions;
+  for (auto& function : *GetIRContext()->module()) {
+    original_functions.push_back(&function);
+  }
+  for (auto& function : original_functions) {
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfOutliningFunction())) {
+      continue;
+    }
+    std::vector<opt::BasicBlock*> blocks;
+    for (auto& block : *function) {
+      blocks.push_back(&block);
+    }
+    auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
+    auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
+    auto postdominator_analysis =
+        GetIRContext()->GetPostDominatorAnalysis(function);
+    std::vector<opt::BasicBlock*> candidate_exit_blocks;
+    for (auto postdominates_entry_block = entry_block;
+         postdominates_entry_block != nullptr;
+         postdominates_entry_block = postdominator_analysis->ImmediateDominator(
+             postdominates_entry_block)) {
+      if (dominator_analysis->Dominates(entry_block,
+                                        postdominates_entry_block)) {
+        candidate_exit_blocks.push_back(postdominates_entry_block);
+      }
+    }
+    if (candidate_exit_blocks.empty()) {
+      continue;
+    }
+    auto exit_block = candidate_exit_blocks[GetFuzzerContext()->RandomIndex(
+        candidate_exit_blocks)];
+
+    auto region_blocks = TransformationOutlineFunction::GetRegionBlocks(
+        GetIRContext(), entry_block, exit_block);
+    std::map<uint32_t, uint32_t> input_id_to_fresh_id;
+    for (auto id : TransformationOutlineFunction::GetRegionInputIds(
+             GetIRContext(), region_blocks, exit_block)) {
+      input_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId();
+    }
+    std::map<uint32_t, uint32_t> output_id_to_fresh_id;
+    for (auto id : TransformationOutlineFunction::GetRegionOutputIds(
+             GetIRContext(), region_blocks, exit_block)) {
+      output_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId();
+    }
+    TransformationOutlineFunction transformation(
+        entry_block->id(), exit_block->id(),
+        /*new_function_struct_return_type_id*/
+        GetFuzzerContext()->GetFreshId(),
+        /*new_function_type_id*/ GetFuzzerContext()->GetFreshId(),
+        /*new_function_id*/ GetFuzzerContext()->GetFreshId(),
+        /*new_function_region_entry_block*/
+        GetFuzzerContext()->GetFreshId(),
+        /*new_caller_result_id*/ GetFuzzerContext()->GetFreshId(),
+        /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
+        /*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id),
+        /*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id));
+    if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+      transformation.Apply(GetIRContext(), GetFactManager());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_outline_functions.h b/source/fuzz/fuzzer_pass_outline_functions.h
new file mode 100644
index 0000000..5448e7d
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_outline_functions.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for outlining single-entry single-exit regions of a  control
+// flow graph into their own functions.
+class FuzzerPassOutlineFunctions : public FuzzerPass {
+ public:
+  FuzzerPassOutlineFunctions(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassOutlineFunctions();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 4654682..1c39da0 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -14,6 +14,8 @@
 
 #include "source/fuzz/fuzzer_util.h"
 
+#include "source/opt/build_module.h"
+
 namespace spvtools {
 namespace fuzz {
 
@@ -180,108 +182,6 @@
   return block->end();
 }
 
-bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
-                                    opt::BasicBlock* bb_from,
-                                    opt::BasicBlock* bb_to) {
-  assert(bb_from->terminator()->opcode() == SpvOpBranch);
-
-  // If there is *already* an edge from |bb_from| to |bb_to|, then adding
-  // another edge is fine from a dominance point of view.
-  if (bb_from->terminator()->GetSingleWordInOperand(0) == bb_to->id()) {
-    return true;
-  }
-
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): the
-  //  solution below to determining whether a new edge respects dominance
-  //  rules is incomplete.  Test
-  //  TransformationAddDeadContinueTest::DISABLED_Miscellaneous6 exposes the
-  //  problem.  In practice, this limitation does not bite too often, and the
-  //  worst it does is leads to SPIR-V that spirv-val rejects.
-
-  // Let us assume that the module being manipulated is valid according to the
-  // rules of the SPIR-V language.
-  //
-  // Suppose that some block Y is dominated by |bb_to| (which includes the case
-  // where Y = |bb_to|).
-  //
-  // Suppose that Y uses an id i that is defined in some other block X.
-  //
-  // Because the module is valid, X must dominate Y.  We are concerned about
-  // whether an edge from |bb_from| to |bb_to| could *stop* X from dominating
-  // Y.
-  //
-  // Because |bb_to| dominates Y, a new edge from |bb_from| to |bb_to| can
-  // only affect whether X dominates Y if X dominates |bb_to|.
-  //
-  // So let us assume that X does dominate |bb_to|, so that we have:
-  //
-  //   (X defines i) dominates |bb_to| dominates (Y uses i)
-  //
-  // The new edge from |bb_from| to |bb_to| will stop the definition of i in X
-  // from dominating the use of i in Y exactly when the new edge will stop X
-  // from dominating |bb_to|.
-  //
-  // Now, the block X that we are worried about cannot dominate |bb_from|,
-  // because in that case X would still dominate |bb_to| after we add an edge
-  // from |bb_from| to |bb_to|.
-  //
-  // Also, it cannot be that X = |bb_to|, because nothing can stop a block
-  // from dominating itself.
-  //
-  // So we are looking for a block X such that:
-  //
-  // - X strictly dominates |bb_to|
-  // - X does not dominate |bb_from|
-  // - X defines an id i
-  // - i is used in some block Y
-  // - |bb_to| dominates Y
-
-  // Walk the dominator tree backwards, starting from the immediate dominator
-  // of |bb_to|.  We can stop when we find a block that also dominates
-  // |bb_from|.
-  auto dominator_analysis = context->GetDominatorAnalysis(bb_from->GetParent());
-  for (auto dominator = dominator_analysis->ImmediateDominator(bb_to);
-       dominator != nullptr &&
-       !dominator_analysis->Dominates(dominator, bb_from);
-       dominator = dominator_analysis->ImmediateDominator(dominator)) {
-    // |dominator| is a candidate for block X in the above description.
-    // We now look through the instructions for a candidate instruction i.
-    for (auto& inst : *dominator) {
-      // Consider all the uses of this instruction.
-      if (!context->get_def_use_mgr()->WhileEachUse(
-              &inst,
-              [bb_to, context, dominator_analysis](
-                  opt::Instruction* user, uint32_t operand_index) -> bool {
-                // If this use is in an OpPhi, we need to check that dominance
-                // of the relevant *parent* block is not spoiled.  Otherwise we
-                // need to check that dominance of the block containing the use
-                // is not spoiled.
-                opt::BasicBlock* use_block_or_phi_parent =
-                    user->opcode() == SpvOpPhi
-                        ? context->cfg()->block(
-                              user->GetSingleWordOperand(operand_index + 1))
-                        : context->get_instr_block(user);
-
-                // There might not be any relevant block, e.g. if the use is in
-                // a decoration; in this case the new edge is unproblematic.
-                if (use_block_or_phi_parent == nullptr) {
-                  return true;
-                }
-
-                // With reference to the above discussion,
-                // |use_block_or_phi_parent| is a candidate for the block Y.
-                // If |bb_to| dominates this block, the new edge would be
-                // problematic.
-                return !dominator_analysis->Dominates(bb_to,
-                                                      use_block_or_phi_parent);
-              })) {
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
 bool BlockIsReachableInItsFunction(opt::IRContext* context,
                                    opt::BasicBlock* bb) {
   auto enclosing_function = bb->GetParent();
@@ -399,6 +299,24 @@
   return array_length_constant->GetU32();
 }
 
+bool IsValid(opt::IRContext* context) {
+  std::vector<uint32_t> binary;
+  context->module()->ToBinary(&binary, false);
+  return SpirvTools(context->grammar().target_env()).Validate(binary);
+}
+
+std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
+  std::vector<uint32_t> binary;
+  context->module()->ToBinary(&binary, false);
+  return BuildModule(context->grammar().target_env(), nullptr, binary.data(),
+                     binary.size());
+}
+
+bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id) {
+  auto type = ir_context->get_type_mgr()->GetType(id);
+  return type && !type->AsFunction();
+}
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index 1569df0..af3eb1b 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -69,14 +69,6 @@
 opt::BasicBlock::iterator GetIteratorForInstruction(
     opt::BasicBlock* block, const opt::Instruction* inst);
 
-// The function determines whether adding an edge from |bb_from| to |bb_to| -
-// is legitimate with respect to the SPIR-V rule that a definition must
-// dominate all of its uses.  This is because adding such an edge can change
-// dominance in the control flow graph, potentially making the module invalid.
-bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
-                                    opt::BasicBlock* bb_from,
-                                    opt::BasicBlock* bb_to);
-
 // Returns true if and only if there is a path to |bb| from the entry block of
 // the function that contains |bb|.
 bool BlockIsReachableInItsFunction(opt::IRContext* context,
@@ -117,6 +109,17 @@
 uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
                       opt::IRContext* context);
 
+// Returns true if and only if |context| is valid, according to the validator.
+bool IsValid(opt::IRContext* context);
+
+// Returns a clone of |context|, by writing |context| to a binary and then
+// parsing it again.
+std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context);
+
+// Returns true if and only if |id| is the id of a type that is not a function
+// type.
+bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id);
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz
diff --git a/source/fuzz/instruction_message.cpp b/source/fuzz/instruction_message.cpp
new file mode 100644
index 0000000..b217a21
--- /dev/null
+++ b/source/fuzz/instruction_message.cpp
@@ -0,0 +1,69 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/instruction_message.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+protobufs::Instruction MakeInstructionMessage(
+    SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
+    const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>&
+        input_operands) {
+  protobufs::Instruction result;
+  result.set_opcode(opcode);
+  result.set_result_type_id(result_type_id);
+  result.set_result_id(result_id);
+  for (auto& operand : input_operands) {
+    auto operand_message = result.add_input_operand();
+    operand_message->set_operand_type(operand.first);
+    for (auto operand_word : operand.second) {
+      operand_message->add_operand_data(operand_word);
+    }
+  }
+  return result;
+}
+
+std::unique_ptr<opt::Instruction> InstructionFromMessage(
+    opt::IRContext* ir_context,
+    const protobufs::Instruction& instruction_message) {
+  // First, update the module's id bound with respect to the new instruction,
+  // if it has a result id.
+  if (instruction_message.result_id()) {
+    fuzzerutil::UpdateModuleIdBound(ir_context,
+                                    instruction_message.result_id());
+  }
+  // Now create a sequence of input operands from the input operand data in the
+  // protobuf message.
+  opt::Instruction::OperandList in_operands;
+  for (auto& operand_message : instruction_message.input_operand()) {
+    opt::Operand::OperandData operand_data;
+    for (auto& word : operand_message.operand_data()) {
+      operand_data.push_back(word);
+    }
+    in_operands.push_back(
+        {static_cast<spv_operand_type_t>(operand_message.operand_type()),
+         operand_data});
+  }
+  // Create and return the instruction.
+  return MakeUnique<opt::Instruction>(
+      ir_context, static_cast<SpvOp>(instruction_message.opcode()),
+      instruction_message.result_type_id(), instruction_message.result_id(),
+      in_operands);
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/instruction_message.h b/source/fuzz/instruction_message.h
new file mode 100644
index 0000000..ed339aa
--- /dev/null
+++ b/source/fuzz/instruction_message.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_
+#define SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_
+
+#include <memory>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/instruction.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Creates an Instruction protobuf message from its component parts.
+protobufs::Instruction MakeInstructionMessage(
+    SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
+    const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>&
+        input_operands);
+
+// Creates and returns an opt::Instruction from protobuf message
+// |instruction_message|, relative to |ir_context|.  In the process, the module
+// id bound associated with |ir_context| is updated to be at least as large as
+// the result id (if any) associated with the new instruction.
+std::unique_ptr<opt::Instruction> InstructionFromMessage(
+    opt::IRContext* ir_context,
+    const protobufs::Instruction& instruction_message);
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index b33c2e5..dbd1fb8 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -21,6 +21,16 @@
 
 package spvtools.fuzz.protobufs;
 
+message UInt32Pair {
+
+  // A pair of uint32s; useful for defining mappings.
+
+  uint32 first = 1;
+
+  uint32 second = 2;
+
+}
+
 message InstructionDescriptor {
 
   // Describes an instruction in some block of a function with respect to a
@@ -116,6 +126,37 @@
 
 }
 
+message InstructionOperand {
+
+  // Represents an operand to a SPIR-V instruction.
+
+  // The type of the operand.
+  uint32 operand_type = 1;
+
+  // The data associated with the operand.  For most operands (e.g. ids,
+  // storage classes and literals) this will be a single word.
+  repeated uint32 operand_data = 2;
+
+}
+
+message Instruction {
+
+  // Represents a SPIR-V instruction.
+
+  // The instruction's opcode (e.g. OpLabel).
+  uint32 opcode = 1;
+
+  // The id of the instruction's result type; 0 if there is no result type.
+  uint32 result_type_id = 2;
+
+  // The id of the instruction's result; 0 if there is no result.
+  uint32 result_id = 3;
+
+  // Zero or more input operands.
+  repeated InstructionOperand input_operand = 4;
+
+}
+
 message FactSequence {
   repeated Fact fact = 1;
 }
@@ -190,6 +231,17 @@
     TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
     TransformationCompositeExtract composite_extract = 21;
     TransformationVectorShuffle vector_shuffle = 22;
+    TransformationOutlineFunction outline_function = 23;
+    TransformationMergeBlocks merge_blocks = 24;
+    TransformationAddTypeVector add_type_vector = 25;
+    TransformationAddTypeArray add_type_array = 26;
+    TransformationAddTypeMatrix add_type_matrix = 27;
+    TransformationAddTypeStruct add_type_struct = 28;
+    TransformationAddTypeFunction add_type_function = 29;
+    TransformationAddConstantComposite add_constant_composite = 30;
+    TransformationAddGlobalVariable add_global_variable = 31;
+    TransformationAddGlobalUndef add_global_undef = 32;
+    TransformationAddFunction add_function = 33;
     // Add additional option using the next available number.
   }
 }
@@ -206,6 +258,21 @@
 
 }
 
+message TransformationAddConstantComposite {
+
+  // Adds a constant of the given composite type to the module.
+
+  // Fresh id for the composite
+  uint32 fresh_id = 1;
+
+  // A composite type id
+  uint32 type_id = 2;
+
+  // Constituent ids for the composite
+  repeated uint32 constituent_id = 3;
+
+}
+
 message TransformationAddConstantScalar {
 
   // Adds a constant of the given scalar type
@@ -262,6 +329,43 @@
 
 }
 
+message TransformationAddFunction {
+
+  // Adds a SPIR-V function to the module.
+
+  // The series of instructions that comprise the function.
+  repeated Instruction instruction = 1;
+
+}
+
+message TransformationAddGlobalUndef {
+
+  // Adds an undefined value of a given type to the module at global scope.
+
+  // Fresh id for the undefined value
+  uint32 fresh_id = 1;
+
+  // The type of the undefined value
+  uint32 type_id = 2;
+
+}
+
+message TransformationAddGlobalVariable {
+
+  // Adds a global variable of the given type to the module, with Private
+  // storage class and optionally with an initializer.
+
+  // Fresh id for the global variable
+  uint32 fresh_id = 1;
+
+  // The type of the global variable
+  uint32 type_id = 2;
+
+  // Optional initializer; 0 if there is no initializer
+  uint32 initializer_id = 3;
+
+}
+
 message TransformationAddNoContractionDecoration {
 
   // Applies OpDecorate NoContraction to the given result id
@@ -271,6 +375,21 @@
 
 }
 
+message TransformationAddTypeArray {
+
+  // Adds an array type of the given element type and size to the module
+
+  // Fresh id for the array type
+  uint32 fresh_id = 1;
+
+  // The array's element type
+  uint32 element_type_id = 2;
+
+  // The array's size
+  uint32 size_id = 3;
+
+}
+
 message TransformationAddTypeBoolean {
 
   // Adds OpTypeBool to the module
@@ -292,6 +411,21 @@
 
 }
 
+message TransformationAddTypeFunction {
+
+  // Adds a function type to the module
+
+  // Fresh id for the function type
+  uint32 fresh_id = 1;
+
+  // The function's return type
+  uint32 return_type_id = 2;
+
+  // The function's argument types
+  repeated uint32 argument_type_id = 3;
+
+}
+
 message TransformationAddTypeInt {
 
   // Adds OpTypeInt to the module with the given width and signedness
@@ -307,6 +441,22 @@
 
 }
 
+message TransformationAddTypeMatrix {
+
+  // Adds a matrix type to the module
+
+  // Fresh id for the matrix type
+  uint32 fresh_id = 1;
+
+  // The matrix's column type, which must be a floating-point vector (as per
+  // the "data rules" in the SPIR-V specification).
+  uint32 column_type_id = 2;
+
+  // The matrix's column count
+  uint32 column_count = 3;
+
+}
+
 message TransformationAddTypePointer {
 
   // Adds OpTypePointer to the module, with the given storage class and base
@@ -323,6 +473,33 @@
 
 }
 
+message TransformationAddTypeStruct {
+
+  // Adds a struct type to the module
+
+  // Fresh id for the struct type
+  uint32 fresh_id = 1;
+
+  // The struct's member types
+  repeated uint32 member_type_id = 3;
+
+}
+
+message TransformationAddTypeVector {
+
+  // Adds a vector type to the module
+
+  // Fresh id for the vector type
+  uint32 fresh_id = 1;
+
+  // The vector's component type
+  uint32 component_type_id = 2;
+
+  // The vector's component count
+  uint32 component_count = 3;
+
+}
+
 message TransformationCompositeConstruct {
 
   // A transformation that introduces an OpCompositeConstruct instruction to
@@ -380,6 +557,16 @@
 
 }
 
+message TransformationMergeBlocks {
+
+  // A transformation that merges a block with its predecessor.
+
+  // The id of the block that is to be merged with its predecessor; the merged
+  // block will have the *predecessor's* id.
+  uint32 block_id = 1;
+
+}
+
 message TransformationMoveBlockDown {
 
   // A transformation that moves a basic block to be one position lower in
@@ -389,6 +576,53 @@
   uint32 block_id = 1;
 }
 
+message TransformationOutlineFunction {
+
+  // A transformation that outlines a single-entry single-exit region of a
+  // control flow graph into a separate function, and replaces the region with
+  // a call to that function.
+
+  // Id of the entry block of the single-entry single-exit region to be outlined
+  uint32 entry_block = 1;
+
+  // Id of the exit block of the single-entry single-exit region to be outlined
+  uint32 exit_block = 2;
+
+  // Id of a struct that will store the return values of the new function
+  uint32 new_function_struct_return_type_id = 3;
+
+  // A fresh id for the type of the outlined function
+  uint32 new_function_type_id = 4;
+
+  // A fresh id for the outlined function itself
+  uint32 new_function_id = 5;
+
+  // A fresh id to represent the block in the outlined function that represents
+  // the first block of the outlined region.
+  uint32 new_function_region_entry_block = 6;
+
+  // A fresh id for the result of the OpFunctionCall instruction that will call
+  // the outlined function
+  uint32 new_caller_result_id = 7;
+
+  // A fresh id to capture the return value of the outlined function - the
+  // argument to OpReturn
+  uint32 new_callee_result_id = 8;
+
+  // Ids defined outside the region and used inside the region will become
+  // parameters to the outlined function.  This is a mapping from used ids to
+  // fresh parameter ids.
+  repeated UInt32Pair input_id_to_fresh_id = 9;
+
+  // Ids defined inside the region and used outside the region will become
+  // fresh ids defined by the outlined function, which get copied into the
+  // function's struct return value and then copied into their destination ids
+  // by the caller.  This is a mapping from original ids to corresponding fresh
+  // ids.
+  repeated UInt32Pair output_id_to_fresh_id = 10;
+
+}
+
 message TransformationReplaceBooleanConstantWithConstantBinary {
 
   // A transformation to capture replacing a use of a boolean constant with
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index d8fc92f..1489f85 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -17,18 +17,29 @@
 #include <cassert>
 
 #include "source/fuzz/transformation_add_constant_boolean.h"
+#include "source/fuzz/transformation_add_constant_composite.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
 #include "source/fuzz/transformation_add_dead_break.h"
 #include "source/fuzz/transformation_add_dead_continue.h"
+#include "source/fuzz/transformation_add_function.h"
+#include "source/fuzz/transformation_add_global_undef.h"
+#include "source/fuzz/transformation_add_global_variable.h"
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "source/fuzz/transformation_add_type_array.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_function.h"
 #include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_matrix.h"
 #include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_add_type_struct.h"
+#include "source/fuzz/transformation_add_type_vector.h"
 #include "source/fuzz/transformation_composite_construct.h"
 #include "source/fuzz/transformation_composite_extract.h"
 #include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/transformation_merge_blocks.h"
 #include "source/fuzz/transformation_move_block_down.h"
+#include "source/fuzz/transformation_outline_function.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
@@ -51,6 +62,9 @@
     case protobufs::Transformation::TransformationCase::kAddConstantBoolean:
       return MakeUnique<TransformationAddConstantBoolean>(
           message.add_constant_boolean());
+    case protobufs::Transformation::TransformationCase::kAddConstantComposite:
+      return MakeUnique<TransformationAddConstantComposite>(
+          message.add_constant_composite());
     case protobufs::Transformation::TransformationCase::kAddConstantScalar:
       return MakeUnique<TransformationAddConstantScalar>(
           message.add_constant_scalar());
@@ -59,20 +73,39 @@
     case protobufs::Transformation::TransformationCase::kAddDeadContinue:
       return MakeUnique<TransformationAddDeadContinue>(
           message.add_dead_continue());
+    case protobufs::Transformation::TransformationCase::kAddFunction:
+      return MakeUnique<TransformationAddFunction>(message.add_function());
+    case protobufs::Transformation::TransformationCase::kAddGlobalUndef:
+      return MakeUnique<TransformationAddGlobalUndef>(
+          message.add_global_undef());
+    case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
+      return MakeUnique<TransformationAddGlobalVariable>(
+          message.add_global_variable());
     case protobufs::Transformation::TransformationCase::
         kAddNoContractionDecoration:
       return MakeUnique<TransformationAddNoContractionDecoration>(
           message.add_no_contraction_decoration());
+    case protobufs::Transformation::TransformationCase::kAddTypeArray:
+      return MakeUnique<TransformationAddTypeArray>(message.add_type_array());
     case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
       return MakeUnique<TransformationAddTypeBoolean>(
           message.add_type_boolean());
     case protobufs::Transformation::TransformationCase::kAddTypeFloat:
       return MakeUnique<TransformationAddTypeFloat>(message.add_type_float());
+    case protobufs::Transformation::TransformationCase::kAddTypeFunction:
+      return MakeUnique<TransformationAddTypeFunction>(
+          message.add_type_function());
     case protobufs::Transformation::TransformationCase::kAddTypeInt:
       return MakeUnique<TransformationAddTypeInt>(message.add_type_int());
+    case protobufs::Transformation::TransformationCase::kAddTypeMatrix:
+      return MakeUnique<TransformationAddTypeMatrix>(message.add_type_matrix());
     case protobufs::Transformation::TransformationCase::kAddTypePointer:
       return MakeUnique<TransformationAddTypePointer>(
           message.add_type_pointer());
+    case protobufs::Transformation::TransformationCase::kAddTypeStruct:
+      return MakeUnique<TransformationAddTypeStruct>(message.add_type_struct());
+    case protobufs::Transformation::TransformationCase::kAddTypeVector:
+      return MakeUnique<TransformationAddTypeVector>(message.add_type_vector());
     case protobufs::Transformation::TransformationCase::kCompositeConstruct:
       return MakeUnique<TransformationCompositeConstruct>(
           message.composite_construct());
@@ -81,8 +114,13 @@
           message.composite_extract());
     case protobufs::Transformation::TransformationCase::kCopyObject:
       return MakeUnique<TransformationCopyObject>(message.copy_object());
+    case protobufs::Transformation::TransformationCase::kMergeBlocks:
+      return MakeUnique<TransformationMergeBlocks>(message.merge_blocks());
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
       return MakeUnique<TransformationMoveBlockDown>(message.move_block_down());
+    case protobufs::Transformation::TransformationCase::kOutlineFunction:
+      return MakeUnique<TransformationOutlineFunction>(
+          message.outline_function());
     case protobufs::Transformation::TransformationCase::
         kReplaceBooleanConstantWithConstantBinary:
       return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp
new file mode 100644
index 0000000..7ba1ea4
--- /dev/null
+++ b/source/fuzz/transformation_add_constant_composite.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_constant_composite.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddConstantComposite::TransformationAddConstantComposite(
+    const spvtools::fuzz::protobufs::TransformationAddConstantComposite&
+        message)
+    : message_(message) {}
+
+TransformationAddConstantComposite::TransformationAddConstantComposite(
+    uint32_t fresh_id, uint32_t type_id,
+    const std::vector<uint32_t>& constituent_ids) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+  for (auto constituent_id : constituent_ids) {
+    message_.add_constituent_id(constituent_id);
+  }
+}
+
+bool TransformationAddConstantComposite::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // Check that the given id is fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // Check that the composite type id is an instruction id.
+  auto composite_type_instruction =
+      context->get_def_use_mgr()->GetDef(message_.type_id());
+  if (!composite_type_instruction) {
+    return false;
+  }
+  // Gather up the operands for the composite constant, in the process checking
+  // whether the given type really defines a composite.
+  std::vector<uint32_t> constituent_type_ids;
+  switch (composite_type_instruction->opcode()) {
+    case SpvOpTypeArray:
+      for (uint32_t index = 0;
+           index <
+           fuzzerutil::GetArraySize(*composite_type_instruction, context);
+           index++) {
+        constituent_type_ids.push_back(
+            composite_type_instruction->GetSingleWordInOperand(0));
+      }
+      break;
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector:
+      for (uint32_t index = 0;
+           index < composite_type_instruction->GetSingleWordInOperand(1);
+           index++) {
+        constituent_type_ids.push_back(
+            composite_type_instruction->GetSingleWordInOperand(0));
+      }
+      break;
+    case SpvOpTypeStruct:
+      composite_type_instruction->ForEachInOperand(
+          [&constituent_type_ids](const uint32_t* member_type_id) {
+            constituent_type_ids.push_back(*member_type_id);
+          });
+      break;
+    default:
+      // Not a composite type.
+      return false;
+  }
+
+  // Check that the number of provided operands matches the number of
+  // constituents required by the type.
+  if (constituent_type_ids.size() !=
+      static_cast<uint32_t>(message_.constituent_id().size())) {
+    return false;
+  }
+
+  // Check that every provided operand refers to an instruction of the
+  // corresponding constituent type.
+  for (uint32_t index = 0; index < constituent_type_ids.size(); index++) {
+    auto constituent_instruction =
+        context->get_def_use_mgr()->GetDef(message_.constituent_id(index));
+    if (!constituent_instruction) {
+      return false;
+    }
+    if (constituent_instruction->type_id() != constituent_type_ids.at(index)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void TransformationAddConstantComposite::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  for (auto constituent_id : message_.constituent_id()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
+  }
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context, SpvOpConstantComposite, message_.type_id(), message_.fresh_id(),
+      in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddConstantComposite::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_add_constant_composite() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_constant_composite.h b/source/fuzz/transformation_add_constant_composite.h
new file mode 100644
index 0000000..9a824a0
--- /dev/null
+++ b/source/fuzz/transformation_add_constant_composite.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddConstantComposite : public Transformation {
+ public:
+  explicit TransformationAddConstantComposite(
+      const protobufs::TransformationAddConstantComposite& message);
+
+  TransformationAddConstantComposite(
+      uint32_t fresh_id, uint32_t type_id,
+      const std::vector<uint32_t>& constituent_ids);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - |message_.type_id| must be the id of a composite type
+  // - |message_.constituent_id| must refer to ids that match the constituent
+  //   types of this composite type
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpConstantComposite instruction defining a constant of type
+  // |message_.type_id|, using |message_.constituent_id| as constituents, with
+  // result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddConstantComposite message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_
diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp
index b244cf4..a37100b 100644
--- a/source/fuzz/transformation_add_dead_break.cpp
+++ b/source/fuzz/transformation_add_dead_break.cpp
@@ -175,18 +175,23 @@
     return false;
   }
 
-  // Check that adding the break would not violate the property that a
-  // definition must dominate all of its uses.
-  return fuzzerutil::NewEdgeRespectsUseDefDominance(context, bb_from, bb_to);
+  // Adding the dead break is only valid if SPIR-V rules related to dominance
+  // hold.  Rather than checking these rules explicitly, we defer to the
+  // validator.  We make a clone of the module, apply the transformation to the
+  // clone, and check whether the transformed clone is valid.
+  //
+  // In principle some of the above checks could be removed, with more reliance
+  // being places on the validator.  This should be revisited if we are sure
+  // the validator is complete with respect to checking structured control flow
+  // rules.
+  auto cloned_context = fuzzerutil::CloneIRContext(context);
+  ApplyImpl(cloned_context.get());
+  return fuzzerutil::IsValid(cloned_context.get());
 }
 
 void TransformationAddDeadBreak::Apply(opt::IRContext* context,
                                        FactManager* /*unused*/) const {
-  fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
-      context, context->cfg()->block(message_.from_block()),
-      context->cfg()->block(message_.to_block()),
-      message_.break_condition_value(), message_.phi_id());
-
+  ApplyImpl(context);
   // Invalidate all analyses
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
 }
@@ -197,5 +202,13 @@
   return result;
 }
 
+void TransformationAddDeadBreak::ApplyImpl(
+    spvtools::opt::IRContext* context) const {
+  fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
+      context, context->cfg()->block(message_.from_block()),
+      context->cfg()->block(message_.to_block()),
+      message_.break_condition_value(), message_.phi_id());
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/transformation_add_dead_break.h b/source/fuzz/transformation_add_dead_break.h
index 10d2cec..81a2c99 100644
--- a/source/fuzz/transformation_add_dead_break.h
+++ b/source/fuzz/transformation_add_dead_break.h
@@ -67,6 +67,14 @@
   bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* context,
                                                 opt::BasicBlock* bb_from) const;
 
+  // Used by 'Apply' to actually apply the transformation to the module of
+  // interest, and by 'IsApplicable' to do a dry-run of the transformation on a
+  // cloned module, in order to check that the transformation leads to a valid
+  // module.  This is only invoked by 'IsApplicable' after certain basic
+  // applicability checks have been made, ensuring that the invocation of this
+  // method is legal.
+  void ApplyImpl(opt::IRContext* context) const;
+
   protobufs::TransformationAddDeadBreak message_;
 };
 
diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp
index e644b88..0aacc5b 100644
--- a/source/fuzz/transformation_add_dead_continue.cpp
+++ b/source/fuzz/transformation_add_dead_continue.cpp
@@ -106,34 +106,30 @@
     return false;
   }
 
-  // Check that adding the continue would not violate the property that a
-  // definition must dominate all of its uses.
-  if (!fuzzerutil::NewEdgeRespectsUseDefDominance(
-          context, bb_from, context->cfg()->block(continue_block))) {
+  // Check whether the data passed to extend OpPhi instructions is appropriate.
+  if (!fuzzerutil::PhiIdsOkForNewEdge(context, bb_from,
+                                      context->cfg()->block(continue_block),
+                                      message_.phi_id())) {
     return false;
   }
 
-  // The transformation is good if and only if the given phi ids are sufficient
-  // to extend relevant OpPhi instructions in the continue block.
-  return fuzzerutil::PhiIdsOkForNewEdge(context, bb_from,
-                                        context->cfg()->block(continue_block),
-                                        message_.phi_id());
+  // Adding the dead break is only valid if SPIR-V rules related to dominance
+  // hold.  Rather than checking these rules explicitly, we defer to the
+  // validator.  We make a clone of the module, apply the transformation to the
+  // clone, and check whether the transformed clone is valid.
+  //
+  // In principle some of the above checks could be removed, with more reliance
+  // being places on the validator.  This should be revisited if we are sure
+  // the validator is complete with respect to checking structured control flow
+  // rules.
+  auto cloned_context = fuzzerutil::CloneIRContext(context);
+  ApplyImpl(cloned_context.get());
+  return fuzzerutil::IsValid(cloned_context.get());
 }
 
 void TransformationAddDeadContinue::Apply(opt::IRContext* context,
                                           FactManager* /*unused*/) const {
-  auto bb_from = context->cfg()->block(message_.from_block());
-  auto continue_block =
-      bb_from->IsLoopHeader()
-          ? bb_from->ContinueBlockId()
-          : context->GetStructuredCFGAnalysis()->LoopContinueBlock(
-                message_.from_block());
-  assert(continue_block &&
-         "Precondition for this transformation requires that "
-         "message_.from_block must be in a loop.");
-  fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
-      context, bb_from, context->cfg()->block(continue_block),
-      message_.continue_condition_value(), message_.phi_id());
+  ApplyImpl(context);
   // Invalidate all analyses
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
 }
@@ -144,5 +140,19 @@
   return result;
 }
 
+void TransformationAddDeadContinue::ApplyImpl(
+    spvtools::opt::IRContext* context) const {
+  auto bb_from = context->cfg()->block(message_.from_block());
+  auto continue_block =
+      bb_from->IsLoopHeader()
+          ? bb_from->ContinueBlockId()
+          : context->GetStructuredCFGAnalysis()->LoopContinueBlock(
+                message_.from_block());
+  assert(continue_block && "message_.from_block must be in a loop.");
+  fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
+      context, bb_from, context->cfg()->block(continue_block),
+      message_.continue_condition_value(), message_.phi_id());
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/transformation_add_dead_continue.h b/source/fuzz/transformation_add_dead_continue.h
index df6bb4c..86b4c93 100644
--- a/source/fuzz/transformation_add_dead_continue.h
+++ b/source/fuzz/transformation_add_dead_continue.h
@@ -64,6 +64,14 @@
   protobufs::Transformation ToMessage() const override;
 
  private:
+  // Used by 'Apply' to actually apply the transformation to the module of
+  // interest, and by 'IsApplicable' to do a dry-run of the transformation on a
+  // cloned module, in order to check that the transformation leads to a valid
+  // module.  This is only invoked by 'IsApplicable' after certain basic
+  // applicability checks have been made, ensuring that the invocation of this
+  // method is legal.
+  void ApplyImpl(opt::IRContext* context) const;
+
   protobufs::TransformationAddDeadContinue message_;
 };
 
diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp
new file mode 100644
index 0000000..5e53961
--- /dev/null
+++ b/source/fuzz/transformation_add_function.cpp
@@ -0,0 +1,156 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_function.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_message.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddFunction::TransformationAddFunction(
+    const spvtools::fuzz::protobufs::TransformationAddFunction& message)
+    : message_(message) {}
+
+TransformationAddFunction::TransformationAddFunction(
+    const std::vector<protobufs::Instruction>& instructions) {
+  for (auto& instruction : instructions) {
+    *message_.add_instruction() = instruction;
+  }
+}
+
+bool TransformationAddFunction::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // Because checking all the conditions for a function to be valid is a big
+  // job that the SPIR-V validator can already do, a "try it and see" approach
+  // is taken here.
+
+  // We first clone the current module, so that we can try adding the new
+  // function without risking wrecking |context|.
+  auto cloned_module = fuzzerutil::CloneIRContext(context);
+
+  // We try to add a function to the cloned module, which may fail if
+  // |message_.instruction| is not sufficiently well-formed.
+  if (!TryToAddFunction(cloned_module.get())) {
+    return false;
+  }
+  // Having managed to add the new function to the cloned module, we ascertain
+  // whether the cloned module is still valid.  If it is, the transformation is
+  // applicable.
+  return fuzzerutil::IsValid(cloned_module.get());
+}
+
+void TransformationAddFunction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  auto success = TryToAddFunction(context);
+  assert(success && "The function should be successfully added.");
+  (void)(success);  // Keep release builds happy (otherwise they may complain
+                    // that |success| is not used).
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddFunction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_function() = message_;
+  return result;
+}
+
+bool TransformationAddFunction::TryToAddFunction(
+    opt::IRContext* context) const {
+  // This function returns false if |message_.instruction| was not well-formed
+  // enough to actually create a function and add it to |context|.
+
+  // A function must have at least some instructions.
+  if (message_.instruction().empty()) {
+    return false;
+  }
+
+  // A function must start with OpFunction.
+  auto function_begin = message_.instruction(0);
+  if (function_begin.opcode() != SpvOpFunction) {
+    return false;
+  }
+
+  // Make a function, headed by the OpFunction instruction.
+  std::unique_ptr<opt::Function> new_function = MakeUnique<opt::Function>(
+      InstructionFromMessage(context, function_begin));
+
+  // Keeps track of which instruction protobuf message we are currently
+  // considering.
+  uint32_t instruction_index = 1;
+  const auto num_instructions =
+      static_cast<uint32_t>(message_.instruction().size());
+
+  // Iterate through all function parameter instructions, adding parameters to
+  // the new function.
+  while (instruction_index < num_instructions &&
+         message_.instruction(instruction_index).opcode() ==
+             SpvOpFunctionParameter) {
+    new_function->AddParameter(InstructionFromMessage(
+        context, message_.instruction(instruction_index)));
+    instruction_index++;
+  }
+
+  // After the parameters, there needs to be a label.
+  if (instruction_index == num_instructions ||
+      message_.instruction(instruction_index).opcode() != SpvOpLabel) {
+    return false;
+  }
+
+  // Iterate through the instructions block by block until the end of the
+  // function is reached.
+  while (instruction_index < num_instructions &&
+         message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) {
+    // Invariant: we should always be at a label instruction at this point.
+    assert(message_.instruction(instruction_index).opcode() == SpvOpLabel);
+
+    // Make a basic block using the label instruction, with the new function
+    // as its parent.
+    std::unique_ptr<opt::BasicBlock> block =
+        MakeUnique<opt::BasicBlock>(InstructionFromMessage(
+            context, message_.instruction(instruction_index)));
+    block->SetParent(new_function.get());
+
+    // Consider successive instructions until we hit another label or the end
+    // of the function, adding each such instruction to the block.
+    instruction_index++;
+    while (instruction_index < num_instructions &&
+           message_.instruction(instruction_index).opcode() !=
+               SpvOpFunctionEnd &&
+           message_.instruction(instruction_index).opcode() != SpvOpLabel) {
+      block->AddInstruction(InstructionFromMessage(
+          context, message_.instruction(instruction_index)));
+      instruction_index++;
+    }
+    // Add the block to the new function.
+    new_function->AddBasicBlock(std::move(block));
+  }
+  // Having considered all the blocks, we should be at the last instruction and
+  // it needs to be OpFunctionEnd.
+  if (instruction_index != num_instructions - 1 ||
+      message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) {
+    return false;
+  }
+  // Set the function's final instruction, add the function to the module and
+  // report success.
+  new_function->SetFunctionEnd(
+      InstructionFromMessage(context, message_.instruction(instruction_index)));
+  context->AddFunction(std::move(new_function));
+  return true;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_function.h b/source/fuzz/transformation_add_function.h
new file mode 100644
index 0000000..fee2732
--- /dev/null
+++ b/source/fuzz/transformation_add_function.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddFunction : public Transformation {
+ public:
+  explicit TransformationAddFunction(
+      const protobufs::TransformationAddFunction& message);
+
+  explicit TransformationAddFunction(
+      const std::vector<protobufs::Instruction>& instructions);
+
+  // - |message_.instruction| must correspond to a sufficiently well-formed
+  //   sequence of instructions that a function can be created from them
+  // - Adding the created function to the module must lead to a valid module.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds the function defined by |message_.instruction| to the module
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  // Attempts to create a function from the series of instructions in
+  // |message_.instruction| and add it to |context|.  Returns false if this is
+  // not possible due to the messages not respecting the basic structure of a
+  // function, e.g. if there is no OpFunction instruction or no blocks; in this
+  // case |context| is left in an indeterminate state.
+  //
+  // Otherwise returns true.  Whether |context| is valid after addition of the
+  // function depends on the contents of |message_.instruction|.
+  //
+  // Intended usage:
+  // - Perform a dry run of this method on a clone of a module, and use
+  //   the validator to check whether the resulting module is valid.  Working
+  //   on a clone means it does not matter if the function fails to be cleanly
+  //   added, or leads to an invalid module.
+  // - If the dry run succeeds, run the method on the real module of interest,
+  //   to add the function.
+  bool TryToAddFunction(opt::IRContext* context) const;
+
+  protobufs::TransformationAddFunction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
diff --git a/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp
new file mode 100644
index 0000000..f9585b3
--- /dev/null
+++ b/source/fuzz/transformation_add_global_undef.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_global_undef.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddGlobalUndef::TransformationAddGlobalUndef(
+    const spvtools::fuzz::protobufs::TransformationAddGlobalUndef& message)
+    : message_(message) {}
+
+TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id,
+                                                           uint32_t type_id) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+}
+
+bool TransformationAddGlobalUndef::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // A fresh id is required.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  auto type = context->get_type_mgr()->GetType(message_.type_id());
+  // The type must exist, and must not be a function type.
+  return type && !type->AsFunction();
+}
+
+void TransformationAddGlobalUndef::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context, SpvOpUndef, message_.type_id(), message_.fresh_id(),
+      opt::Instruction::OperandList()));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_global_undef() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_global_undef.h b/source/fuzz/transformation_add_global_undef.h
new file mode 100644
index 0000000..550d9f6
--- /dev/null
+++ b/source/fuzz/transformation_add_global_undef.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddGlobalUndef : public Transformation {
+ public:
+  explicit TransformationAddGlobalUndef(
+      const protobufs::TransformationAddGlobalUndef& message);
+
+  TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.type_id| must be the id of a non-function type
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpUndef instruction to the module, with |message_.type_id| as its
+  // type.  The instruction has result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddGlobalUndef message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp
new file mode 100644
index 0000000..cea268c
--- /dev/null
+++ b/source/fuzz/transformation_add_global_variable.cpp
@@ -0,0 +1,131 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_global_variable.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddGlobalVariable::TransformationAddGlobalVariable(
+    const spvtools::fuzz::protobufs::TransformationAddGlobalVariable& message)
+    : message_(message) {}
+
+TransformationAddGlobalVariable::TransformationAddGlobalVariable(
+    uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+  message_.set_initializer_id(initializer_id);
+}
+
+bool TransformationAddGlobalVariable::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The type id must correspond to a type.
+  auto type = context->get_type_mgr()->GetType(message_.type_id());
+  if (!type) {
+    return false;
+  }
+  // That type must be a pointer type ...
+  auto pointer_type = type->AsPointer();
+  if (!pointer_type) {
+    return false;
+  }
+  // ... with Private storage class.
+  if (pointer_type->storage_class() != SpvStorageClassPrivate) {
+    return false;
+  }
+  if (message_.initializer_id()) {
+    // The initializer id must be the id of a constant.  Check this with the
+    // constant manager.
+    auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
+        {message_.initializer_id()});
+    if (constant_id.empty()) {
+      return false;
+    }
+    assert(constant_id.size() == 1 &&
+           "We asked for the constant associated with a single id; we should "
+           "get a single constant.");
+    // The type of the constant must match the pointee type of the pointer.
+    if (pointer_type->pointee_type() != constant_id[0]->type()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void TransformationAddGlobalVariable::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList input_operands;
+  input_operands.push_back(
+      {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}});
+  if (message_.initializer_id()) {
+    input_operands.push_back(
+        {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
+  }
+  context->module()->AddGlobalValue(
+      MakeUnique<opt::Instruction>(context, SpvOpVariable, message_.type_id(),
+                                   message_.fresh_id(), input_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+
+  if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(context)) {
+    // Conservatively add this global to the interface of every entry point in
+    // the module.  This means that the global is available for other
+    // transformations to use.
+    //
+    // A downside of this is that the global will be in the interface even if it
+    // ends up never being used.
+    //
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
+    //  this if a more thorough approach to entry point interfaces is taken.
+    for (auto& entry_point : context->module()->entry_points()) {
+      entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
+    }
+  }
+
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_global_variable() = message_;
+  return result;
+}
+
+bool TransformationAddGlobalVariable::
+    PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
+        opt::IRContext* context) {
+  // TODO(afd): We capture the universal environments for which this requirement
+  //  holds.  The check should be refined on demand for other target
+  //  environments.
+  switch (context->grammar().target_env()) {
+    case SPV_ENV_UNIVERSAL_1_0:
+    case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_UNIVERSAL_1_3:
+      return false;
+    default:
+      return true;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h
new file mode 100644
index 0000000..ca63e68
--- /dev/null
+++ b/source/fuzz/transformation_add_global_variable.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddGlobalVariable : public Transformation {
+ public:
+  explicit TransformationAddGlobalVariable(
+      const protobufs::TransformationAddGlobalVariable& message);
+
+  TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
+                                  uint32_t initializer_id);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.type_id| must be the id of a pointer type with Private storage
+  //   class
+  // - |message_.initializer_id| must either be 0 or the id of a constant whose
+  //   type is the pointee type of |message_.type_id|
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds a global variable with Private storage class to the module, with type
+  // |message_.type_id| and either no initializer or |message_.initializer_id|
+  // as an initializer, depending on whether |message_.initializer_id| is 0.
+  // The global variable has result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  static bool PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
+      opt::IRContext* context);
+
+  protobufs::TransformationAddGlobalVariable message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
diff --git a/source/fuzz/transformation_add_type_array.cpp b/source/fuzz/transformation_add_type_array.cpp
new file mode 100644
index 0000000..2074e98
--- /dev/null
+++ b/source/fuzz/transformation_add_type_array.cpp
@@ -0,0 +1,88 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_array.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeArray::TransformationAddTypeArray(
+    const spvtools::fuzz::protobufs::TransformationAddTypeArray& message)
+    : message_(message) {}
+
+TransformationAddTypeArray::TransformationAddTypeArray(uint32_t fresh_id,
+                                                       uint32_t element_type_id,
+                                                       uint32_t size_id) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_element_type_id(element_type_id);
+  message_.set_size_id(size_id);
+}
+
+bool TransformationAddTypeArray::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // A fresh id is required.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  auto element_type =
+      context->get_type_mgr()->GetType(message_.element_type_id());
+  if (!element_type || element_type->AsFunction()) {
+    // The element type id either does not refer to a type, or refers to a
+    // function type; both are illegal.
+    return false;
+  }
+  auto constant =
+      context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()});
+  if (constant.empty()) {
+    // The size id does not refer to a constant.
+    return false;
+  }
+  assert(constant.size() == 1 &&
+         "Only one constant id was provided, so only one constant should have "
+         "been returned");
+
+  auto int_constant = constant[0]->AsIntConstant();
+  if (!int_constant) {
+    // The size constant is not an integer.
+    return false;
+  }
+  // We require that the size constant be a 32-bit value that is positive when
+  // interpreted as being signed.
+  return int_constant->words().size() == 1 && int_constant->GetS32() >= 1;
+}
+
+void TransformationAddTypeArray::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}});
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}});
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeArray::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_array() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_array.h b/source/fuzz/transformation_add_type_array.h
new file mode 100644
index 0000000..b6e0718
--- /dev/null
+++ b/source/fuzz/transformation_add_type_array.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeArray : public Transformation {
+ public:
+  explicit TransformationAddTypeArray(
+      const protobufs::TransformationAddTypeArray& message);
+
+  TransformationAddTypeArray(uint32_t fresh_id, uint32_t element_type_id,
+                             uint32_t size_id);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.element_type_id| must be the id of a non-function type
+  // - |message_.size_id| must be the id of a 32-bit integer constant that is
+  //   positive when interpreted as signed.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeArray instruction to the module, with element type given by
+  // |message_.element_type_id| and size given by |message_.size_id|.  The
+  // result id of the instruction is |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeArray message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
diff --git a/source/fuzz/transformation_add_type_function.cpp b/source/fuzz/transformation_add_type_function.cpp
new file mode 100644
index 0000000..4b6717b
--- /dev/null
+++ b/source/fuzz/transformation_add_type_function.cpp
@@ -0,0 +1,113 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_function.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeFunction::TransformationAddTypeFunction(
+    const spvtools::fuzz::protobufs::TransformationAddTypeFunction& message)
+    : message_(message) {}
+
+TransformationAddTypeFunction::TransformationAddTypeFunction(
+    uint32_t fresh_id, uint32_t return_type_id,
+    const std::vector<uint32_t>& argument_type_ids) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_return_type_id(return_type_id);
+  for (auto id : argument_type_ids) {
+    message_.add_argument_type_id(id);
+  }
+}
+
+bool TransformationAddTypeFunction::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The return and argument types must be type ids but not not be function
+  // type ids.
+  if (!fuzzerutil::IsNonFunctionTypeId(context, message_.return_type_id())) {
+    return false;
+  }
+  for (auto argument_type_id : message_.argument_type_id()) {
+    if (!fuzzerutil::IsNonFunctionTypeId(context, argument_type_id)) {
+      return false;
+    }
+  }
+  // Check whether there is already an OpTypeFunction definition that uses
+  // exactly the same return and argument type ids.  (Note that the type manager
+  // does not allow us to check this, as it does not distinguish between
+  // function types with different but isomorphic pointer argument types.)
+  for (auto& inst : context->module()->types_values()) {
+    if (inst.opcode() != SpvOpTypeFunction) {
+      // Consider only OpTypeFunction instructions.
+      continue;
+    }
+    if (inst.GetSingleWordInOperand(0) != message_.return_type_id()) {
+      // Different return types - cannot be the same.
+      continue;
+    }
+    if (inst.NumInOperands() !=
+        1 + static_cast<uint32_t>(message_.argument_type_id().size())) {
+      // Different numbers of arguments - cannot be the same.
+      continue;
+    }
+    bool found_argument_mismatch = false;
+    for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
+      if (message_.argument_type_id(index - 1) !=
+          inst.GetSingleWordInOperand(index)) {
+        // Argument mismatch - cannot be the same.
+        found_argument_mismatch = true;
+        break;
+      }
+    }
+    if (found_argument_mismatch) {
+      continue;
+    }
+    // Everything matches - the type is already declared.
+    return false;
+  }
+  return true;
+}
+
+void TransformationAddTypeFunction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.return_type_id()}});
+  for (auto argument_type_id : message_.argument_type_id()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {argument_type_id}});
+  }
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeFunction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_function() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_function.h b/source/fuzz/transformation_add_type_function.h
new file mode 100644
index 0000000..2b59661
--- /dev/null
+++ b/source/fuzz/transformation_add_type_function.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeFunction : public Transformation {
+ public:
+  explicit TransformationAddTypeFunction(
+      const protobufs::TransformationAddTypeFunction& message);
+
+  TransformationAddTypeFunction(uint32_t fresh_id, uint32_t return_type_id,
+                                const std::vector<uint32_t>& argument_type_ids);
+
+  // - |message_.fresh_id| must not be used by the module
+  // - |message_.return_type_id| and each element of |message_.argument_type_id|
+  //   must be the ids of non-function types
+  // - The module must not contain an OpTypeFunction instruction defining a
+  //   function type with the signature provided by teh given return and
+  //   argument types
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeFunction instruction to the module, with signature given by
+  // |message_.return_type_id| and |message_.argument_type_id|.  The result id
+  // for the instruction is |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeFunction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_
diff --git a/source/fuzz/transformation_add_type_matrix.cpp b/source/fuzz/transformation_add_type_matrix.cpp
new file mode 100644
index 0000000..07ab705
--- /dev/null
+++ b/source/fuzz/transformation_add_type_matrix.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_matrix.h"
+
+#include "fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeMatrix::TransformationAddTypeMatrix(
+    const spvtools::fuzz::protobufs::TransformationAddTypeMatrix& message)
+    : message_(message) {}
+
+TransformationAddTypeMatrix::TransformationAddTypeMatrix(
+    uint32_t fresh_id, uint32_t column_type_id, uint32_t column_count) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_column_type_id(column_type_id);
+  message_.set_column_count(column_count);
+}
+
+bool TransformationAddTypeMatrix::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The column type must be a floating-point vector.
+  auto column_type =
+      context->get_type_mgr()->GetType(message_.column_type_id());
+  if (!column_type) {
+    return false;
+  }
+  return column_type->AsVector() &&
+         column_type->AsVector()->element_type()->AsFloat();
+}
+
+void TransformationAddTypeMatrix::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}});
+  in_operands.push_back(
+      {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}});
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_matrix() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_matrix.h b/source/fuzz/transformation_add_type_matrix.h
new file mode 100644
index 0000000..ee3caf7
--- /dev/null
+++ b/source/fuzz/transformation_add_type_matrix.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeMatrix : public Transformation {
+ public:
+  explicit TransformationAddTypeMatrix(
+      const protobufs::TransformationAddTypeMatrix& message);
+
+  TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t base_type_id,
+                              uint32_t size);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - |message_.column_type_id| must be the id of a floating-point vector type
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeMatrix instruction to the module, with column type
+  // |message_.column_type_id| and |message_.column_count| columns, with result
+  // id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeMatrix message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
diff --git a/source/fuzz/transformation_add_type_struct.cpp b/source/fuzz/transformation_add_type_struct.cpp
new file mode 100644
index 0000000..1ae8372
--- /dev/null
+++ b/source/fuzz/transformation_add_type_struct.cpp
@@ -0,0 +1,73 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_struct.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeStruct::TransformationAddTypeStruct(
+    const spvtools::fuzz::protobufs::TransformationAddTypeStruct& message)
+    : message_(message) {}
+
+TransformationAddTypeStruct::TransformationAddTypeStruct(
+    uint32_t fresh_id, const std::vector<uint32_t>& member_type_ids) {
+  message_.set_fresh_id(fresh_id);
+  for (auto member_type_id : member_type_ids) {
+    message_.add_member_type_id(member_type_id);
+  }
+}
+
+bool TransformationAddTypeStruct::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // A fresh id is required.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  for (auto member_type : message_.member_type_id()) {
+    auto type = context->get_type_mgr()->GetType(member_type);
+    if (!type || type->AsFunction()) {
+      // The member type id either does not refer to a type, or refers to a
+      // function type; both are illegal.
+      return false;
+    }
+  }
+  return true;
+}
+
+void TransformationAddTypeStruct::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  for (auto member_type : message_.member_type_id()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {member_type}});
+  }
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeStruct::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_struct() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_struct.h b/source/fuzz/transformation_add_type_struct.h
new file mode 100644
index 0000000..edf3ec6
--- /dev/null
+++ b/source/fuzz/transformation_add_type_struct.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeStruct : public Transformation {
+ public:
+  explicit TransformationAddTypeStruct(
+      const protobufs::TransformationAddTypeStruct& message);
+
+  TransformationAddTypeStruct(uint32_t fresh_id,
+                              const std::vector<uint32_t>& component_type_ids);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - |message_.member_type_id| must be a sequence of non-function type ids
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeStruct instruction whose field types are given by
+  // |message_.member_type_id|, with result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeStruct message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_
diff --git a/source/fuzz/transformation_add_type_vector.cpp b/source/fuzz/transformation_add_type_vector.cpp
new file mode 100644
index 0000000..3fdf50b
--- /dev/null
+++ b/source/fuzz/transformation_add_type_vector.cpp
@@ -0,0 +1,69 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_vector.h"
+
+#include "fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeVector::TransformationAddTypeVector(
+    const spvtools::fuzz::protobufs::TransformationAddTypeVector& message)
+    : message_(message) {}
+
+TransformationAddTypeVector::TransformationAddTypeVector(
+    uint32_t fresh_id, uint32_t component_type_id, uint32_t component_count) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_component_type_id(component_type_id);
+  message_.set_component_count(component_count);
+}
+
+bool TransformationAddTypeVector::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  auto component_type =
+      context->get_type_mgr()->GetType(message_.component_type_id());
+  if (!component_type) {
+    return false;
+  }
+  return component_type->AsBool() || component_type->AsFloat() ||
+         component_type->AsInteger();
+}
+
+void TransformationAddTypeVector::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.component_type_id()}});
+  in_operands.push_back(
+      {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}});
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeVector::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_vector() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_add_type_vector.h b/source/fuzz/transformation_add_type_vector.h
new file mode 100644
index 0000000..7b50f6a
--- /dev/null
+++ b/source/fuzz/transformation_add_type_vector.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeVector : public Transformation {
+ public:
+  explicit TransformationAddTypeVector(
+      const protobufs::TransformationAddTypeVector& message);
+
+  TransformationAddTypeVector(uint32_t fresh_id, uint32_t base_type_id,
+                              uint32_t size);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - |message_.component_type_id| must be the id of a scalar type
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeVector instruction to the module, with component type
+  // |message_.component_type_id| and |message_.component_count| components,
+  // with result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeVector message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
diff --git a/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp
new file mode 100644
index 0000000..316e80d
--- /dev/null
+++ b/source/fuzz/transformation_merge_blocks.cpp
@@ -0,0 +1,81 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_merge_blocks.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/opt/block_merge_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationMergeBlocks::TransformationMergeBlocks(
+    const spvtools::fuzz::protobufs::TransformationMergeBlocks& message)
+    : message_(message) {}
+
+TransformationMergeBlocks::TransformationMergeBlocks(uint32_t block_id) {
+  message_.set_block_id(block_id);
+}
+
+bool TransformationMergeBlocks::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id());
+  // The given block must exist.
+  if (!second_block) {
+    return false;
+  }
+  // The block must have just one predecessor.
+  auto predecessors = context->cfg()->preds(second_block->id());
+  if (predecessors.size() != 1) {
+    return false;
+  }
+  auto first_block = context->cfg()->block(predecessors.at(0));
+
+  return opt::blockmergeutil::CanMergeWithSuccessor(context, first_block);
+}
+
+void TransformationMergeBlocks::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id());
+  auto first_block =
+      context->cfg()->block(context->cfg()->preds(second_block->id()).at(0));
+
+  auto function = first_block->GetParent();
+  // We need an iterator pointing to the predecessor, hence the loop.
+  for (auto bi = function->begin(); bi != function->end(); ++bi) {
+    if (bi->id() == first_block->id()) {
+      assert(opt::blockmergeutil::CanMergeWithSuccessor(context, &*bi) &&
+             "Because 'Apply' should only be invoked if 'IsApplicable' holds, "
+             "it must be possible to merge |bi| with its successor.");
+      opt::blockmergeutil::MergeWithSuccessor(context, function, bi);
+      // Invalidate all analyses, since we have changed the module
+      // significantly.
+      context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+      return;
+    }
+  }
+  assert(false &&
+         "Control should not reach here - we should always find the desired "
+         "block");
+}
+
+protobufs::Transformation TransformationMergeBlocks::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_merge_blocks() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h
new file mode 100644
index 0000000..86216db
--- /dev/null
+++ b/source/fuzz/transformation_merge_blocks.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationMergeBlocks : public Transformation {
+ public:
+  explicit TransformationMergeBlocks(
+      const protobufs::TransformationMergeBlocks& message);
+
+  TransformationMergeBlocks(uint32_t block_id);
+
+  // - |message_.block_id| must be the id of a block, b
+  // - b must have a single predecessor, a
+  // - b must be the sole successor of a
+  // - Replacing a with the merge of a and b (and removing b) must lead to a
+  //   valid module
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // The contents of b are merged into a, and a's terminator is replaced with
+  // the terminator of b.  Block b is removed from the module.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationMergeBlocks message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp
new file mode 100644
index 0000000..1d1d48e
--- /dev/null
+++ b/source/fuzz/transformation_outline_function.cpp
@@ -0,0 +1,938 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_outline_function.h"
+
+#include <set>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+
+std::map<uint32_t, uint32_t> PairSequenceToMap(
+    const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>&
+        pair_sequence) {
+  std::map<uint32_t, uint32_t> result;
+  for (auto& pair : pair_sequence) {
+    result[pair.first()] = pair.second();
+  }
+  return result;
+}
+
+}  // namespace
+
+TransformationOutlineFunction::TransformationOutlineFunction(
+    const spvtools::fuzz::protobufs::TransformationOutlineFunction& message)
+    : message_(message) {}
+
+TransformationOutlineFunction::TransformationOutlineFunction(
+    uint32_t entry_block, uint32_t exit_block,
+    uint32_t new_function_struct_return_type_id, uint32_t new_function_type_id,
+    uint32_t new_function_id, uint32_t new_function_region_entry_block,
+    uint32_t new_caller_result_id, uint32_t new_callee_result_id,
+    std::map<uint32_t, uint32_t>&& input_id_to_fresh_id,
+    std::map<uint32_t, uint32_t>&& output_id_to_fresh_id) {
+  message_.set_entry_block(entry_block);
+  message_.set_exit_block(exit_block);
+  message_.set_new_function_struct_return_type_id(
+      new_function_struct_return_type_id);
+  message_.set_new_function_type_id(new_function_type_id);
+  message_.set_new_function_id(new_function_id);
+  message_.set_new_function_region_entry_block(new_function_region_entry_block);
+  message_.set_new_caller_result_id(new_caller_result_id);
+  message_.set_new_callee_result_id(new_callee_result_id);
+  for (auto& entry : input_id_to_fresh_id) {
+    protobufs::UInt32Pair pair;
+    pair.set_first(entry.first);
+    pair.set_second(entry.second);
+    *message_.add_input_id_to_fresh_id() = pair;
+  }
+  for (auto& entry : output_id_to_fresh_id) {
+    protobufs::UInt32Pair pair;
+    pair.set_first(entry.first);
+    pair.set_second(entry.second);
+    *message_.add_output_id_to_fresh_id() = pair;
+  }
+}
+
+bool TransformationOutlineFunction::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  std::set<uint32_t> ids_used_by_this_transformation;
+
+  // The various new ids used by the transformation must be fresh and distinct.
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_function_struct_return_type_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_function_type_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_function_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_function_region_entry_block(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_caller_result_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_callee_result_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  for (auto& pair : message_.input_id_to_fresh_id()) {
+    if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+            pair.second(), context, &ids_used_by_this_transformation)) {
+      return false;
+    }
+  }
+
+  for (auto& pair : message_.output_id_to_fresh_id()) {
+    if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+            pair.second(), context, &ids_used_by_this_transformation)) {
+      return false;
+    }
+  }
+
+  // The entry and exit block ids must indeed refer to blocks.
+  for (auto block_id : {message_.entry_block(), message_.exit_block()}) {
+    auto block_label = context->get_def_use_mgr()->GetDef(block_id);
+    if (!block_label || block_label->opcode() != SpvOpLabel) {
+      return false;
+    }
+  }
+
+  auto entry_block = context->cfg()->block(message_.entry_block());
+  auto exit_block = context->cfg()->block(message_.exit_block());
+
+  // The entry block cannot start with OpVariable - this would mean that
+  // outlining would remove a variable from the function containing the region
+  // being outlined.
+  if (entry_block->begin()->opcode() == SpvOpVariable) {
+    return false;
+  }
+
+  // For simplicity, we do not allow the entry block to be a loop header.
+  if (entry_block->GetLoopMergeInst()) {
+    return false;
+  }
+
+  // For simplicity, we do not allow the exit block to be a merge block or
+  // continue target.
+  bool exit_block_is_merge_or_continue = false;
+  context->get_def_use_mgr()->WhileEachUse(
+      exit_block->id(),
+      [&exit_block_is_merge_or_continue](
+          const opt::Instruction* use_instruction,
+          uint32_t /*unused*/) -> bool {
+        switch (use_instruction->opcode()) {
+          case SpvOpLoopMerge:
+          case SpvOpSelectionMerge:
+            exit_block_is_merge_or_continue = true;
+            return false;
+          default:
+            return true;
+        }
+      });
+  if (exit_block_is_merge_or_continue) {
+    return false;
+  }
+
+  // The entry block cannot start with OpPhi.  This is to keep the
+  // transformation logic simple.  (Another transformation to split the OpPhis
+  // from a block could be applied to avoid this scenario.)
+  if (entry_block->begin()->opcode() == SpvOpPhi) {
+    return false;
+  }
+
+  // The block must be in the same function.
+  if (entry_block->GetParent() != exit_block->GetParent()) {
+    return false;
+  }
+
+  // The entry block must dominate the exit block.
+  auto dominator_analysis =
+      context->GetDominatorAnalysis(entry_block->GetParent());
+  if (!dominator_analysis->Dominates(entry_block, exit_block)) {
+    return false;
+  }
+
+  // The exit block must post-dominate the entry block.
+  auto postdominator_analysis =
+      context->GetPostDominatorAnalysis(entry_block->GetParent());
+  if (!postdominator_analysis->Dominates(exit_block, entry_block)) {
+    return false;
+  }
+
+  // Find all the blocks dominated by |message_.entry_block| and post-dominated
+  // by |message_.exit_block|.
+  auto region_set = GetRegionBlocks(
+      context, entry_block = context->cfg()->block(message_.entry_block()),
+      exit_block = context->cfg()->block(message_.exit_block()));
+
+  // Check whether |region_set| really is a single-entry single-exit region, and
+  // also check whether structured control flow constructs and their merge
+  // and continue constructs are either wholly in or wholly out of the region -
+  // e.g. avoid the situation where the region contains the head of a loop but
+  // not the loop's continue construct.
+  //
+  // This is achieved by going through every block in the function that contains
+  // the region.
+  for (auto& block : *entry_block->GetParent()) {
+    if (&block == exit_block) {
+      // It is OK (and typically expected) for the exit block of the region to
+      // have successors outside the region.  It is also OK for the exit block
+      // to head a structured control flow construct - the block containing the
+      // call to the outlined function will end up heading this construct if
+      // outlining takes place.
+      continue;
+    }
+
+    if (region_set.count(&block) != 0) {
+      // The block is in the region and is not the region's exit block.  Let's
+      // see whether all of the block's successors are in the region.  If they
+      // are not, the region is not single-entry single-exit.
+      bool all_successors_in_region = true;
+      block.WhileEachSuccessorLabel([&all_successors_in_region, context,
+                                     &region_set](uint32_t successor) -> bool {
+        if (region_set.count(context->cfg()->block(successor)) == 0) {
+          all_successors_in_region = false;
+          return false;
+        }
+        return true;
+      });
+      if (!all_successors_in_region) {
+        return false;
+      }
+    }
+
+    if (auto merge = block.GetMergeInst()) {
+      // The block is a loop or selection header -- the header and its
+      // associated merge block had better both be in the region or both be
+      // outside the region.
+      auto merge_block = context->cfg()->block(merge->GetSingleWordOperand(0));
+      if (region_set.count(&block) != region_set.count(merge_block)) {
+        return false;
+      }
+    }
+
+    if (auto loop_merge = block.GetLoopMergeInst()) {
+      // Similar to the above, but for the continue target of a loop.
+      auto continue_target =
+          context->cfg()->block(loop_merge->GetSingleWordOperand(1));
+      if (continue_target != exit_block &&
+          region_set.count(&block) != region_set.count(continue_target)) {
+        return false;
+      }
+    }
+  }
+
+  // For each region input id, i.e. every id defined outside the region but
+  // used inside the region, ...
+  std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
+      PairSequenceToMap(message_.input_id_to_fresh_id());
+  for (auto id : GetRegionInputIds(context, region_set, exit_block)) {
+    // There needs to be a corresponding fresh id to be used as a function
+    // parameter.
+    if (input_id_to_fresh_id_map.count(id) == 0) {
+      return false;
+    }
+    // Furthermore, no region input id is allowed to be the result of an access
+    // chain.  This is because region input ids will become function parameters,
+    // and it is not legal to pass an access chain as a function parameter.
+    if (context->get_def_use_mgr()->GetDef(id)->opcode() == SpvOpAccessChain) {
+      return false;
+    }
+  }
+
+  // For each region output id -- i.e. every id defined inside the region but
+  // used outside the region -- there needs to be a corresponding fresh id that
+  // can hold the value for this id computed in the outlined function.
+  std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
+      PairSequenceToMap(message_.output_id_to_fresh_id());
+  for (auto id : GetRegionOutputIds(context, region_set, exit_block)) {
+    if (output_id_to_fresh_id_map.count(id) == 0) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationOutlineFunction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  // The entry block for the region before outlining.
+  auto original_region_entry_block =
+      context->cfg()->block(message_.entry_block());
+
+  // The exit block for the region before outlining.
+  auto original_region_exit_block =
+      context->cfg()->block(message_.exit_block());
+
+  // The single-entry single-exit region defined by |message_.entry_block| and
+  // |message_.exit_block|.
+  std::set<opt::BasicBlock*> region_blocks = GetRegionBlocks(
+      context, original_region_entry_block, original_region_exit_block);
+
+  // Input and output ids for the region being outlined.
+  std::vector<uint32_t> region_input_ids =
+      GetRegionInputIds(context, region_blocks, original_region_exit_block);
+  std::vector<uint32_t> region_output_ids =
+      GetRegionOutputIds(context, region_blocks, original_region_exit_block);
+
+  // Maps from input and output ids to fresh ids.
+  std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
+      PairSequenceToMap(message_.input_id_to_fresh_id());
+  std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
+      PairSequenceToMap(message_.output_id_to_fresh_id());
+
+  UpdateModuleIdBoundForFreshIds(context, input_id_to_fresh_id_map,
+                                 output_id_to_fresh_id_map);
+
+  // Construct a map that associates each output id with its type id.
+  std::map<uint32_t, uint32_t> output_id_to_type_id;
+  for (uint32_t output_id : region_output_ids) {
+    output_id_to_type_id[output_id] =
+        context->get_def_use_mgr()->GetDef(output_id)->type_id();
+  }
+
+  // The region will be collapsed to a single block that calls a function
+  // containing the outlined region.  This block needs to end with whatever
+  // the exit block of the region ended with before outlining.  We thus clone
+  // the terminator of the region's exit block, and the merge instruction for
+  // the block if there is one, so that we can append them to the end of the
+  // collapsed block later.
+  std::unique_ptr<opt::Instruction> cloned_exit_block_terminator =
+      std::unique_ptr<opt::Instruction>(
+          original_region_exit_block->terminator()->Clone(context));
+  std::unique_ptr<opt::Instruction> cloned_exit_block_merge =
+      original_region_exit_block->GetMergeInst()
+          ? std::unique_ptr<opt::Instruction>(
+                original_region_exit_block->GetMergeInst()->Clone(context))
+          : nullptr;
+
+  // Make a function prototype for the outlined function, which involves
+  // figuring out its required type.
+  std::unique_ptr<opt::Function> outlined_function = PrepareFunctionPrototype(
+      context, region_input_ids, region_output_ids, input_id_to_fresh_id_map);
+
+  // Adapt the region to be outlined so that its input ids are replaced with the
+  // ids of the outlined function's input parameters, and so that output ids
+  // are similarly remapped.
+  RemapInputAndOutputIdsInRegion(
+      context, *original_region_exit_block, region_blocks, region_input_ids,
+      region_output_ids, input_id_to_fresh_id_map, output_id_to_fresh_id_map);
+
+  // Fill out the body of the outlined function according to the region that is
+  // being outlined.
+  PopulateOutlinedFunction(context, *original_region_entry_block,
+                           *original_region_exit_block, region_blocks,
+                           region_output_ids, output_id_to_fresh_id_map,
+                           outlined_function.get());
+
+  // Collapse the region that has been outlined into a function down to a single
+  // block that calls said function.
+  ShrinkOriginalRegion(
+      context, region_blocks, region_input_ids, region_output_ids,
+      output_id_to_type_id, outlined_function->type_id(),
+      std::move(cloned_exit_block_merge),
+      std::move(cloned_exit_block_terminator), original_region_entry_block);
+
+  // Add the outlined function to the module.
+  context->module()->AddFunction(std::move(outlined_function));
+
+  // Major surgery has been conducted on the module, so invalidate all analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationOutlineFunction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_outline_function() = message_;
+  return result;
+}
+
+bool TransformationOutlineFunction::
+    CheckIdIsFreshAndNotUsedByThisTransformation(
+        uint32_t id, opt::IRContext* context,
+        std::set<uint32_t>* ids_used_by_this_transformation) const {
+  if (!fuzzerutil::IsFreshId(context, id)) {
+    return false;
+  }
+  if (ids_used_by_this_transformation->count(id) != 0) {
+    return false;
+  }
+  ids_used_by_this_transformation->insert(id);
+  return true;
+}
+
+std::vector<uint32_t> TransformationOutlineFunction::GetRegionInputIds(
+    opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+    opt::BasicBlock* region_exit_block) {
+  std::vector<uint32_t> result;
+
+  auto enclosing_function = region_exit_block->GetParent();
+
+  // Consider each parameter of the function containing the region.
+  enclosing_function->ForEachParam([context, &region_set, &result](
+                                       opt::Instruction* function_parameter) {
+    // Consider every use of the parameter.
+    context->get_def_use_mgr()->WhileEachUse(
+        function_parameter, [context, function_parameter, &region_set, &result](
+                                opt::Instruction* use, uint32_t /*unused*/) {
+          // Get the block, if any, in which the parameter is used.
+          auto use_block = context->get_instr_block(use);
+          // If the use is in a block that lies within the region, the
+          // parameter is an input id for the region.
+          if (use_block && region_set.count(use_block) != 0) {
+            result.push_back(function_parameter->result_id());
+            return false;
+          }
+          return true;
+        });
+  });
+
+  // Consider all definitions in the function that might turn out to be input
+  // ids.
+  for (auto& block : *enclosing_function) {
+    std::vector<opt::Instruction*> candidate_input_ids_for_block;
+    if (region_set.count(&block) == 0) {
+      // All instructions in blocks outside the region are candidate's for
+      // generating input ids.
+      for (auto& inst : block) {
+        candidate_input_ids_for_block.push_back(&inst);
+      }
+    } else {
+      // Blocks in the region cannot generate input ids.
+      continue;
+    }
+
+    // Consider each candidate input id to check whether it is used in the
+    // region.
+    for (auto& inst : candidate_input_ids_for_block) {
+      context->get_def_use_mgr()->WhileEachUse(
+          inst,
+          [context, &inst, region_exit_block, &region_set, &result](
+              opt::Instruction* use, uint32_t /*unused*/) -> bool {
+
+            // Find the block in which this id use occurs, recording the id as
+            // an input id if the block is outside the region, with some
+            // exceptions detailed below.
+            auto use_block = context->get_instr_block(use);
+
+            if (!use_block) {
+              // There might be no containing block, e.g. if the use is in a
+              // decoration.
+              return true;
+            }
+
+            if (region_set.count(use_block) == 0) {
+              // The use is not in the region: this does not make it an input
+              // id.
+              return true;
+            }
+
+            if (use_block == region_exit_block && use->IsBlockTerminator()) {
+              // We do not regard uses in the exit block terminator as input
+              // ids, as this terminator does not get outlined.
+              return true;
+            }
+
+            result.push_back(inst->result_id());
+            return false;
+          });
+    }
+  }
+  return result;
+}
+
+std::vector<uint32_t> TransformationOutlineFunction::GetRegionOutputIds(
+    opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+    opt::BasicBlock* region_exit_block) {
+  std::vector<uint32_t> result;
+
+  // Consider each block in the function containing the region.
+  for (auto& block : *region_exit_block->GetParent()) {
+    if (region_set.count(&block) == 0) {
+      // Skip blocks that are not in the region.
+      continue;
+    }
+    // Consider each use of each instruction defined in the block.
+    for (auto& inst : block) {
+      context->get_def_use_mgr()->WhileEachUse(
+          &inst,
+          [&region_set, context, &inst, region_exit_block, &result](
+              opt::Instruction* use, uint32_t /*unused*/) -> bool {
+
+            // Find the block in which this id use occurs, recording the id as
+            // an output id if the block is outside the region, with some
+            // exceptions detailed below.
+            auto use_block = context->get_instr_block(use);
+
+            if (!use_block) {
+              // There might be no containing block, e.g. if the use is in a
+              // decoration.
+              return true;
+            }
+
+            if (region_set.count(use_block) != 0) {
+              // The use is in the region.
+              if (use_block != region_exit_block || !use->IsBlockTerminator()) {
+                // Furthermore, the use is not in the terminator of the region's
+                // exit block.
+                return true;
+              }
+            }
+
+            result.push_back(inst.result_id());
+            return false;
+          });
+    }
+  }
+  return result;
+}
+
+std::set<opt::BasicBlock*> TransformationOutlineFunction::GetRegionBlocks(
+    opt::IRContext* context, opt::BasicBlock* entry_block,
+    opt::BasicBlock* exit_block) {
+  auto enclosing_function = entry_block->GetParent();
+  auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
+  auto postdominator_analysis =
+      context->GetPostDominatorAnalysis(enclosing_function);
+
+  std::set<opt::BasicBlock*> result;
+  for (auto& block : *enclosing_function) {
+    if (dominator_analysis->Dominates(entry_block, &block) &&
+        postdominator_analysis->Dominates(exit_block, &block)) {
+      result.insert(&block);
+    }
+  }
+  return result;
+}
+
+std::unique_ptr<opt::Function>
+TransformationOutlineFunction::PrepareFunctionPrototype(
+    opt::IRContext* context, const std::vector<uint32_t>& region_input_ids,
+    const std::vector<uint32_t>& region_output_ids,
+    const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const {
+  uint32_t return_type_id = 0;
+  uint32_t function_type_id = 0;
+
+  // First, try to find an existing function type that is suitable.  This is
+  // only possible if the region generates no output ids; if it generates output
+  // ids we are going to make a new struct for those, and since that struct does
+  // not exist there cannot already be a function type with this struct as its
+  // return type.
+  if (region_output_ids.empty()) {
+    opt::analysis::Void void_type;
+    return_type_id = context->get_type_mgr()->GetId(&void_type);
+    std::vector<const opt::analysis::Type*> argument_types;
+    for (auto id : region_input_ids) {
+      argument_types.push_back(context->get_type_mgr()->GetType(
+          context->get_def_use_mgr()->GetDef(id)->type_id()));
+    }
+    opt::analysis::Function function_type(&void_type, argument_types);
+    function_type_id = context->get_type_mgr()->GetId(&function_type);
+  }
+
+  // If no existing function type was found, we need to create one.
+  if (function_type_id == 0) {
+    assert(
+        ((return_type_id == 0) == !region_output_ids.empty()) &&
+        "We should only have set the return type if there are no output ids.");
+    // If the region generates output ids, we need to make a struct with one
+    // field per output id.
+    if (!region_output_ids.empty()) {
+      opt::Instruction::OperandList struct_member_types;
+      for (uint32_t output_id : region_output_ids) {
+        auto output_id_type =
+            context->get_def_use_mgr()->GetDef(output_id)->type_id();
+        struct_member_types.push_back({SPV_OPERAND_TYPE_ID, {output_id_type}});
+      }
+      // Add a new struct type to the module.
+      context->module()->AddType(MakeUnique<opt::Instruction>(
+          context, SpvOpTypeStruct, 0,
+          message_.new_function_struct_return_type_id(),
+          std::move(struct_member_types)));
+      // The return type for the function is the newly-created struct.
+      return_type_id = message_.new_function_struct_return_type_id();
+    }
+    assert(
+        return_type_id != 0 &&
+        "We should either have a void return type, or have created a struct.");
+
+    // The region's input ids dictate the parameter types to the function.
+    opt::Instruction::OperandList function_type_operands;
+    function_type_operands.push_back({SPV_OPERAND_TYPE_ID, {return_type_id}});
+    for (auto id : region_input_ids) {
+      function_type_operands.push_back(
+          {SPV_OPERAND_TYPE_ID,
+           {context->get_def_use_mgr()->GetDef(id)->type_id()}});
+    }
+    // Add a new function type to the module, and record that this is the type
+    // id for the new function.
+    context->module()->AddType(MakeUnique<opt::Instruction>(
+        context, SpvOpTypeFunction, 0, message_.new_function_type_id(),
+        function_type_operands));
+    function_type_id = message_.new_function_type_id();
+  }
+
+  // Create a new function with |message_.new_function_id| as the function id,
+  // and the return type and function type prepared above.
+  std::unique_ptr<opt::Function> outlined_function =
+      MakeUnique<opt::Function>(MakeUnique<opt::Instruction>(
+          context, SpvOpFunction, return_type_id, message_.new_function_id(),
+          opt::Instruction::OperandList(
+              {{spv_operand_type_t ::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+                {SpvFunctionControlMaskNone}},
+               {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+                {function_type_id}}})));
+
+  // Add one parameter to the function for each input id, using the fresh ids
+  // provided in |input_id_to_fresh_id_map|.
+  for (auto id : region_input_ids) {
+    outlined_function->AddParameter(MakeUnique<opt::Instruction>(
+        context, SpvOpFunctionParameter,
+        context->get_def_use_mgr()->GetDef(id)->type_id(),
+        input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList()));
+  }
+
+  return outlined_function;
+}
+
+void TransformationOutlineFunction::UpdateModuleIdBoundForFreshIds(
+    opt::IRContext* context,
+    const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+    const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const {
+  // Enlarge the module's id bound as needed to accommodate the various fresh
+  // ids associated with the transformation.
+  fuzzerutil::UpdateModuleIdBound(
+      context, message_.new_function_struct_return_type_id());
+  fuzzerutil::UpdateModuleIdBound(context, message_.new_function_type_id());
+  fuzzerutil::UpdateModuleIdBound(context, message_.new_function_id());
+  fuzzerutil::UpdateModuleIdBound(context,
+                                  message_.new_function_region_entry_block());
+  fuzzerutil::UpdateModuleIdBound(context, message_.new_caller_result_id());
+  fuzzerutil::UpdateModuleIdBound(context, message_.new_callee_result_id());
+
+  for (auto& entry : input_id_to_fresh_id_map) {
+    fuzzerutil::UpdateModuleIdBound(context, entry.second);
+  }
+
+  for (auto& entry : output_id_to_fresh_id_map) {
+    fuzzerutil::UpdateModuleIdBound(context, entry.second);
+  }
+}
+
+void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion(
+    opt::IRContext* context, const opt::BasicBlock& original_region_exit_block,
+    const std::set<opt::BasicBlock*>& region_blocks,
+    const std::vector<uint32_t>& region_input_ids,
+    const std::vector<uint32_t>& region_output_ids,
+    const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+    const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const {
+  // Change all uses of input ids inside the region to the corresponding fresh
+  // ids that will ultimately be parameters of the outlined function.
+  // This is done by considering each region input id in turn.
+  for (uint32_t id : region_input_ids) {
+    // We then consider each use of the input id.
+    context->get_def_use_mgr()->ForEachUse(
+        id, [context, id, &input_id_to_fresh_id_map, region_blocks](
+                opt::Instruction* use, uint32_t operand_index) {
+          // Find the block in which this use of the input id occurs.
+          opt::BasicBlock* use_block = context->get_instr_block(use);
+          // We want to rewrite the use id if its block occurs in the outlined
+          // region.
+          if (region_blocks.count(use_block) != 0) {
+            // Rewrite this use of the input id.
+            use->SetOperand(operand_index, {input_id_to_fresh_id_map.at(id)});
+          }
+        });
+  }
+
+  // Change each definition of a region output id to define the corresponding
+  // fresh ids that will store intermediate value for the output ids.  Also
+  // change all uses of the output id located in the outlined region.
+  // This is done by considering each region output id in turn.
+  for (uint32_t id : region_output_ids) {
+    // First consider each use of the output id and update the relevant uses.
+    context->get_def_use_mgr()->ForEachUse(
+        id,
+        [context, &original_region_exit_block, id, &output_id_to_fresh_id_map,
+         region_blocks](opt::Instruction* use, uint32_t operand_index) {
+          // Find the block in which this use of the output id occurs.
+          auto use_block = context->get_instr_block(use);
+          // We want to rewrite the use id if its block occurs in the outlined
+          // region, with one exception: the terminator of the exit block of
+          // the region is going to remain in the original function, so if the
+          // use appears in such a terminator instruction we leave it alone.
+          if (
+              // The block is in the region ...
+              region_blocks.count(use_block) != 0 &&
+              // ... and the use is not in the terminator instruction of the
+              // region's exit block.
+              !(use_block == &original_region_exit_block &&
+                use->IsBlockTerminator())) {
+            // Rewrite this use of the output id.
+            use->SetOperand(operand_index, {output_id_to_fresh_id_map.at(id)});
+          }
+        });
+
+    // Now change the instruction that defines the output id so that it instead
+    // defines the corresponding fresh id.  We do this after changing all the
+    // uses so that the definition of the original id is still registered when
+    // we analyse its uses.
+    context->get_def_use_mgr()->GetDef(id)->SetResultId(
+        output_id_to_fresh_id_map.at(id));
+  }
+}
+
+void TransformationOutlineFunction::PopulateOutlinedFunction(
+    opt::IRContext* context, const opt::BasicBlock& original_region_entry_block,
+    const opt::BasicBlock& original_region_exit_block,
+    const std::set<opt::BasicBlock*>& region_blocks,
+    const std::vector<uint32_t>& region_output_ids,
+    const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
+    opt::Function* outlined_function) const {
+  // When we create the exit block for the outlined region, we use this pointer
+  // to track of it so that we can manipulate it later.
+  opt::BasicBlock* outlined_region_exit_block = nullptr;
+
+  // The region entry block in the new function is identical to the entry block
+  // of the region being outlined, except that it has
+  // |message_.new_function_region_entry_block| as its id.
+  std::unique_ptr<opt::BasicBlock> outlined_region_entry_block =
+      MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
+          context, SpvOpLabel, 0, message_.new_function_region_entry_block(),
+          opt::Instruction::OperandList()));
+  outlined_region_entry_block->SetParent(outlined_function);
+  if (&original_region_entry_block == &original_region_exit_block) {
+    outlined_region_exit_block = outlined_region_entry_block.get();
+  }
+
+  for (auto& inst : original_region_entry_block) {
+    outlined_region_entry_block->AddInstruction(
+        std::unique_ptr<opt::Instruction>(inst.Clone(context)));
+  }
+  outlined_function->AddBasicBlock(std::move(outlined_region_entry_block));
+
+  // We now go through the single-entry single-exit region defined by the entry
+  // and exit blocks, adding clones of all blocks to the new function.
+
+  // Consider every block in the enclosing function.
+  auto enclosing_function = original_region_entry_block.GetParent();
+  for (auto block_it = enclosing_function->begin();
+       block_it != enclosing_function->end();) {
+    // Skip the region's entry block - we already dealt with it above.
+    if (region_blocks.count(&*block_it) == 0 ||
+        &*block_it == &original_region_entry_block) {
+      ++block_it;
+      continue;
+    }
+    // Clone the block so that it can be added to the new function.
+    auto cloned_block =
+        std::unique_ptr<opt::BasicBlock>(block_it->Clone(context));
+
+    // If this is the region's exit block, then the cloned block is the outlined
+    // region's exit block.
+    if (&*block_it == &original_region_exit_block) {
+      assert(outlined_region_exit_block == nullptr &&
+             "We should not yet have encountered the exit block.");
+      outlined_region_exit_block = cloned_block.get();
+    }
+
+    cloned_block->SetParent(outlined_function);
+
+    // Redirect any OpPhi operands whose predecessors are the original region
+    // entry block to become the new function entry block.
+    cloned_block->ForEachPhiInst([this](opt::Instruction* phi_inst) {
+      for (uint32_t predecessor_index = 1;
+           predecessor_index < phi_inst->NumInOperands();
+           predecessor_index += 2) {
+        if (phi_inst->GetSingleWordInOperand(predecessor_index) ==
+            message_.entry_block()) {
+          phi_inst->SetInOperand(predecessor_index,
+                                 {message_.new_function_region_entry_block()});
+        }
+      }
+    });
+
+    outlined_function->AddBasicBlock(std::move(cloned_block));
+    block_it = block_it.Erase();
+  }
+  assert(outlined_region_exit_block != nullptr &&
+         "We should have encountered the region's exit block when iterating "
+         "through the function");
+
+  // We now need to adapt the exit block for the region - in the new function -
+  // so that it ends with a return.
+
+  // We first eliminate the merge instruction (if any) and the terminator for
+  // the cloned exit block.
+  for (auto inst_it = outlined_region_exit_block->begin();
+       inst_it != outlined_region_exit_block->end();) {
+    if (inst_it->opcode() == SpvOpLoopMerge ||
+        inst_it->opcode() == SpvOpSelectionMerge) {
+      inst_it = inst_it.Erase();
+    } else if (inst_it->IsBlockTerminator()) {
+      inst_it = inst_it.Erase();
+    } else {
+      ++inst_it;
+    }
+  }
+
+  // We now add either OpReturn or OpReturnValue as the cloned exit block's
+  // terminator.
+  if (region_output_ids.empty()) {
+    // The case where there are no region output ids is simple: we just add
+    // OpReturn.
+    outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+        context, SpvOpReturn, 0, 0, opt::Instruction::OperandList()));
+  } else {
+    // In the case where there are output ids, we add an OpCompositeConstruct
+    // instruction to pack all the output values into a struct, and then an
+    // OpReturnValue instruction to return this struct.
+    opt::Instruction::OperandList struct_member_operands;
+    for (uint32_t id : region_output_ids) {
+      struct_member_operands.push_back(
+          {SPV_OPERAND_TYPE_ID, {output_id_to_fresh_id_map.at(id)}});
+    }
+    outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+        context, SpvOpCompositeConstruct,
+        message_.new_function_struct_return_type_id(),
+        message_.new_callee_result_id(), struct_member_operands));
+    outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+        context, SpvOpReturnValue, 0, 0,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {message_.new_callee_result_id()}}})));
+  }
+
+  outlined_function->SetFunctionEnd(MakeUnique<opt::Instruction>(
+      context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList()));
+}
+
+void TransformationOutlineFunction::ShrinkOriginalRegion(
+    opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks,
+    const std::vector<uint32_t>& region_input_ids,
+    const std::vector<uint32_t>& region_output_ids,
+    const std::map<uint32_t, uint32_t>& output_id_to_type_id,
+    uint32_t return_type_id,
+    std::unique_ptr<opt::Instruction> cloned_exit_block_merge,
+    std::unique_ptr<opt::Instruction> cloned_exit_block_terminator,
+    opt::BasicBlock* original_region_entry_block) const {
+  // Erase all blocks from the original function that are in the outlined
+  // region, except for the region's entry block.
+  //
+  // In the process, identify all references to the exit block of the region,
+  // as merge blocks, continue targets, or OpPhi predecessors, and rewrite them
+  // to refer to the region entry block (the single block to which we are
+  // shrinking the region).
+  auto enclosing_function = original_region_entry_block->GetParent();
+  for (auto block_it = enclosing_function->begin();
+       block_it != enclosing_function->end();) {
+    if (&*block_it == original_region_entry_block) {
+      ++block_it;
+    } else if (region_blocks.count(&*block_it) == 0) {
+      // The block is not in the region.  Check whether it has the last block
+      // of the region as an OpPhi predecessor, and if so change the
+      // predecessor to be the first block of the region (i.e. the block
+      // containing the call to what was outlined).
+      assert(block_it->MergeBlockIdIfAny() != message_.exit_block() &&
+             "Outlined region must not end with a merge block");
+      assert(block_it->ContinueBlockIdIfAny() != message_.exit_block() &&
+             "Outlined region must not end with a continue target");
+      block_it->ForEachPhiInst([this](opt::Instruction* phi_inst) {
+        for (uint32_t predecessor_index = 1;
+             predecessor_index < phi_inst->NumInOperands();
+             predecessor_index += 2) {
+          if (phi_inst->GetSingleWordInOperand(predecessor_index) ==
+              message_.exit_block()) {
+            phi_inst->SetInOperand(predecessor_index, {message_.entry_block()});
+          }
+        }
+      });
+      ++block_it;
+    } else {
+      // The block is in the region and is not the region's entry block: kill
+      // it.
+      block_it = block_it.Erase();
+    }
+  }
+
+  // Now erase all instructions from the region's entry block, as they have
+  // been outlined.
+  for (auto inst_it = original_region_entry_block->begin();
+       inst_it != original_region_entry_block->end();) {
+    inst_it = inst_it.Erase();
+  }
+
+  // Now we add a call to the outlined function to the region's entry block.
+  opt::Instruction::OperandList function_call_operands;
+  function_call_operands.push_back(
+      {SPV_OPERAND_TYPE_ID, {message_.new_function_id()}});
+  // The function parameters are the region input ids.
+  for (auto input_id : region_input_ids) {
+    function_call_operands.push_back({SPV_OPERAND_TYPE_ID, {input_id}});
+  }
+
+  original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
+      context, SpvOpFunctionCall, return_type_id,
+      message_.new_caller_result_id(), function_call_operands));
+
+  // If there are output ids, the function call will return a struct.  For each
+  // output id, we add an extract operation to pull the appropriate struct
+  // member out into an output id.
+  for (uint32_t index = 0; index < region_output_ids.size(); ++index) {
+    uint32_t output_id = region_output_ids[index];
+    original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
+        context, SpvOpCompositeExtract, output_id_to_type_id.at(output_id),
+        output_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {message_.new_caller_result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}})));
+  }
+
+  // Finally, we terminate the block with the merge instruction (if any) that
+  // used to belong to the region's exit block, and the terminator that used
+  // to belong to the region's exit block.
+  if (cloned_exit_block_merge != nullptr) {
+    original_region_entry_block->AddInstruction(
+        std::move(cloned_exit_block_merge));
+  }
+  original_region_entry_block->AddInstruction(
+      std::move(cloned_exit_block_terminator));
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_outline_function.h b/source/fuzz/transformation_outline_function.h
new file mode 100644
index 0000000..784499d
--- /dev/null
+++ b/source/fuzz/transformation_outline_function.h
@@ -0,0 +1,222 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationOutlineFunction : public Transformation {
+ public:
+  explicit TransformationOutlineFunction(
+      const protobufs::TransformationOutlineFunction& message);
+
+  TransformationOutlineFunction(
+      uint32_t entry_block, uint32_t exit_block,
+      uint32_t new_function_struct_return_type_id,
+      uint32_t new_function_type_id, uint32_t new_function_id,
+      uint32_t new_function_region_entry_block, uint32_t new_caller_result_id,
+      uint32_t new_callee_result_id,
+      std::map<uint32_t, uint32_t>&& input_id_to_fresh_id,
+      std::map<uint32_t, uint32_t>&& output_id_to_fresh_id);
+
+  // - All the fresh ids occurring in the transformation must be distinct and
+  //   fresh
+  // - |message_.entry_block| and |message_.exit_block| must form a single-entry
+  //   single-exit control flow graph region
+  // - |message_.entry_block| must not start with OpVariable
+  // - |message_.entry_block| must not be a loop header
+  // - |message_.exit_block| must not be a merge block or the continue target
+  //   of a loop
+  // - A structured control flow construct must lie either completely within the
+  //   region or completely outside it
+  // - |message.entry_block| must not start with OpPhi; this is to keep the
+  //   transformation simple - another transformation should be used to split
+  //   a desired entry block that starts with OpPhi if needed
+  // - |message_.input_id_to_fresh_id| must contain an entry for every id
+  //   defined outside the region but used in the region
+  // - |message_.output_id_to_fresh_id| must contain an entry for every id
+  //   defined in the region but used outside the region
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // - A new function with id |message_.new_function_id| is added to the module.
+  // - If the region generates output ids, the return type of this function is
+  //   a new struct type with one field per output id, and with type id
+  //   |message_.new_function_struct_return_type|, otherwise the function return
+  //   types is void and |message_.new_function_struct_return_type| is not used.
+  // - If the region generates input ids, the new function has one parameter per
+  //   input id.  Fresh ids for these parameters are provided by
+  //   |message_.input_id_to_fresh_id|.
+  // - Unless the type required for the new function is already known,
+  //   |message_.new_function_type_id| is used as the type id for a new function
+  //   type, and the new function uses this type.
+  // - The new function starts with a dummy block with id
+  //   |message_.new_function_first_block|, which jumps straight to a successor
+  //   block, to avoid violating rules on what the first block in a function may
+  //   look like.
+  // - The outlined region is replaced with a single block, with the same id
+  //   as |message_.entry_block|, and which calls the new function, passing the
+  //   region's input ids as parameters.  The result is  stored in
+  //   |message_.new_caller_result_id|, which has type
+  //   |message_.new_function_struct_return_type| (unless there are
+  //   no output ids, in which case the return type is void).  The components
+  //   of this returned struct are then copied out into the region's output ids.
+  //   The block ends with the merge instruction (if any) and terminator of
+  //   |message_.exit_block|.
+  // - The body of the new function is identical to the outlined region, except
+  //   that (a) the region's entry block has id
+  //   |message_.new_function_region_entry_block|, (b) input id uses are
+  //   replaced with parameter accesses, (c) and definitions of output ids are
+  //   replaced with definitions of corresponding fresh ids provided by
+  //   |message_.output_id_to_fresh_id|, and (d) the block of the function
+  //   ends by returning a composite of type
+  //   |message_.new_function_struct_return_type| comprised of all the fresh
+  //   output ids (unless the return type is void, in which case no value is
+  //   returned.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns the set of blocks dominated by |entry_block| and post-dominated
+  // by |exit_block|.
+  static std::set<opt::BasicBlock*> GetRegionBlocks(
+      opt::IRContext* context, opt::BasicBlock* entry_block,
+      opt::BasicBlock* exit_block);
+
+  // Yields ids that are used in |region_set| and that are either parameters
+  // to the function containing |region_set|, or are defined by blocks of this
+  // function that are outside |region_set|.
+  //
+  // Special cases: OpPhi instructions in |region_entry_block| and the
+  // terminator of |region_exit_block| do not get outlined, therefore
+  // - id uses in OpPhi instructions in |region_entry_block| are ignored
+  // - id uses in the terminator instruction of |region_exit_block| are ignored
+  static std::vector<uint32_t> GetRegionInputIds(
+      opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+      opt::BasicBlock* region_exit_block);
+
+  // Yields all ids that are defined in |region_set| and used outside
+  // |region_set|.
+  //
+  // Special cases: for similar reasons as for |GetRegionInputIds|,
+  // - ids defined in the region and used in the terminator of
+  //   |region_exit_block| count as output ids
+  static std::vector<uint32_t> GetRegionOutputIds(
+      opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+      opt::BasicBlock* region_exit_block);
+
+ private:
+  // A helper method for the applicability check.  Returns true if and only if
+  // |id| is (a) a fresh id for the module, and (b) an id that has not
+  // previously been subject to this check.  We use this to check whether the
+  // ids given for the transformation are not only fresh but also different from
+  // one another.
+  bool CheckIdIsFreshAndNotUsedByThisTransformation(
+      uint32_t id, opt::IRContext* context,
+      std::set<uint32_t>* ids_used_by_this_transformation) const;
+
+  // Ensures that the module's id bound is at least the maximum of any fresh id
+  // associated with the transformation.
+  void UpdateModuleIdBoundForFreshIds(
+      opt::IRContext* context,
+      const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+      const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
+
+  // Uses |input_id_to_fresh_id_map| and |output_id_to_fresh_id_map| to convert,
+  // in the region to be outlined, all the input ids in |region_input_ids| and
+  // the output ids in |region_output_ids| to their fresh counterparts.
+  // Parameters |region_blocks| provides access to the blocks that must be
+  // modified, and |original_region_exit_block| allows for some special cases
+  // where ids should not be remapped.
+  void RemapInputAndOutputIdsInRegion(
+      opt::IRContext* context,
+      const opt::BasicBlock& original_region_exit_block,
+      const std::set<opt::BasicBlock*>& region_blocks,
+      const std::vector<uint32_t>& region_input_ids,
+      const std::vector<uint32_t>& region_output_ids,
+      const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+      const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
+
+  // Produce a Function object that has the right function type and parameter
+  // declarations.  The function argument types and parameter ids are dictated
+  // by |region_input_ids| and |input_id_to_fresh_id_map|.  The function return
+  // type is dictated by |region_output_ids|.
+  //
+  // A new struct type to represent the function return type, and a new function
+  // type for the function, will be added to the module (unless suitable types
+  // are already present).
+  std::unique_ptr<opt::Function> PrepareFunctionPrototype(
+      opt::IRContext* context, const std::vector<uint32_t>& region_input_ids,
+      const std::vector<uint32_t>& region_output_ids,
+      const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const;
+
+  // Creates the body of the outlined function by cloning blocks from the
+  // original region, given by |region_blocks|, adapting the cloned version
+  // of |original_region_exit_block| so that it returns something appropriate,
+  // and patching up branches to |original_region_entry_block| to refer to its
+  // clone.  Parameters |region_output_ids| and |output_id_to_fresh_id_map| are
+  // used to determine what the function should return.
+  void PopulateOutlinedFunction(
+      opt::IRContext* context,
+      const opt::BasicBlock& original_region_entry_block,
+      const opt::BasicBlock& original_region_exit_block,
+      const std::set<opt::BasicBlock*>& region_blocks,
+      const std::vector<uint32_t>& region_output_ids,
+      const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
+      opt::Function* outlined_function) const;
+
+  // Shrinks the outlined region, given by |region_blocks|, down to the single
+  // block |original_region_entry_block|.  This block is itself shrunk to just
+  // contain:
+  // - any OpPhi instructions that were originally present
+  // - a call to the outlined function, with parameters provided by
+  //   |region_input_ids|
+  // - instructions to route components of the call's return value into
+  //   |region_output_ids|
+  // - The merge instruction (if any) and terminator of the original region's
+  //   exit block, given by |cloned_exit_block_merge| and
+  //   |cloned_exit_block_terminator|
+  // Parameters |output_id_to_type_id| and |return_type_id| provide the
+  // provide types for the region's output ids, and the return type of the
+  // outlined function: as the module is in an inconsistent state when this
+  // function is called, this information cannot be gotten from the def-use
+  // manager.
+  void ShrinkOriginalRegion(
+      opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks,
+      const std::vector<uint32_t>& region_input_ids,
+      const std::vector<uint32_t>& region_output_ids,
+      const std::map<uint32_t, uint32_t>& output_id_to_type_id,
+      uint32_t return_type_id,
+      std::unique_ptr<opt::Instruction> cloned_exit_block_merge,
+      std::unique_ptr<opt::Instruction> cloned_exit_block_terminator,
+      opt::BasicBlock* original_region_entry_block) const;
+
+  protobufs::TransformationOutlineFunction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
diff --git a/source/operand.cpp b/source/operand.cpp
index cd26a3d..39d17a6 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -29,6 +29,7 @@
 // per-item basis. https://github.com/KhronosGroup/SPIRV-Tools/issues/1195
 
 #include "operand.kinds-unified1.inc"
+#include "spirv-tools/libspirv.h"
 
 static const spv_operand_table_t kOperandTable = {
     ARRAY_SIZE(pygen_variable_OperandInfoTable),
@@ -228,6 +229,18 @@
       return "debug type qualifier";
     case SPV_OPERAND_TYPE_DEBUG_OPERATION:
       return "debug operation";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
+      return "OpenCL.DebugInfo.100 debug info flags";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+      return "OpenCL.DebugInfo.100 debug base type encoding";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
+      return "OpenCL.DebugInfo.100 debug composite type";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
+      return "OpenCL.DebugInfo.100 debug type qualifier";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
+      return "OpenCL.DebugInfo.100 debug operation";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
+      return "OpenCL.DebugInfo.100 debug imported entity";
 
     // The next values are for values returned from an instruction, not actually
     // an operand.  So the specific strings don't matter.  But let's add them
@@ -312,6 +325,11 @@
     case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
     case SPV_OPERAND_TYPE_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
       return true;
     default:
       break;
@@ -328,6 +346,7 @@
     case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
     case SPV_OPERAND_TYPE_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
       return true;
     default:
       break;
diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp
index 107723d..263a069 100644
--- a/source/opt/block_merge_util.cpp
+++ b/source/opt/block_merge_util.cpp
@@ -49,6 +49,18 @@
   return IsMerge(context, block->id());
 }
 
+// Returns true if |id| is the continue target of a merge instruction.
+bool IsContinue(IRContext* context, uint32_t id) {
+  return !context->get_def_use_mgr()->WhileEachUse(
+      id, [](Instruction* user, uint32_t index) {
+        SpvOp op = user->opcode();
+        if (op == SpvOpLoopMerge && index == 1u) {
+          return false;
+        }
+        return true;
+      });
+}
+
 // Removes any OpPhi instructions in |block|, which should have exactly one
 // predecessor, replacing uses of OpPhi ids with the ids associated with the
 // predecessor.
@@ -86,6 +98,11 @@
     return false;
   }
 
+  if (pred_is_merge && IsContinue(context, lab_id)) {
+    // Cannot merge a continue target with a merge block.
+    return false;
+  }
+
   // Don't bother trying to merge unreachable blocks.
   if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) {
     if (!dominators->IsReachable(block)) return false;
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp
index 4c02c73..5022e1b 100644
--- a/source/opt/convert_to_half_pass.cpp
+++ b/source/opt/convert_to_half_pass.cpp
@@ -42,7 +42,7 @@
   return Pass::IsFloat(ty_id, width);
 }
 
-bool ConvertToHalfPass::IsRelaxed(Instruction* inst) {
+bool ConvertToHalfPass::IsDecoratedRelaxed(Instruction* inst) {
   uint32_t r_id = inst->result_id();
   for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false))
     if (r_inst->opcode() == SpvOpDecorate &&
@@ -51,6 +51,12 @@
   return false;
 }
 
+bool ConvertToHalfPass::IsRelaxed(uint32_t id) {
+  return relaxed_ids_set_.count(id) > 0;
+}
+
+void ConvertToHalfPass::AddRelaxed(uint32_t id) { relaxed_ids_set_.insert(id); }
+
 analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) {
   analysis::Float float_ty(width);
   return context()->get_type_mgr()->GetRegisteredType(&float_ty);
@@ -87,16 +93,19 @@
 }
 
 void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width,
-                                   InstructionBuilder* builder) {
+                                   Instruction* inst) {
   Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp);
   uint32_t ty_id = val_inst->type_id();
   uint32_t nty_id = EquivFloatTypeId(ty_id, width);
   if (nty_id == ty_id) return;
   Instruction* cvt_inst;
+  InstructionBuilder builder(
+      context(), inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   if (val_inst->opcode() == SpvOpUndef)
-    cvt_inst = builder->AddNullaryOp(nty_id, SpvOpUndef);
+    cvt_inst = builder.AddNullaryOp(nty_id, SpvOpUndef);
   else
-    cvt_inst = builder->AddUnaryOp(nty_id, SpvOpFConvert, *val_idp);
+    cvt_inst = builder.AddUnaryOp(nty_id, SpvOpFConvert, *val_idp);
   *val_idp = cvt_inst->result_id();
 }
 
@@ -153,17 +162,15 @@
   bool modified = false;
   // Convert all float32 based operands to float16 equivalent and change
   // instruction type to float16 equivalent.
-  InstructionBuilder builder(
-      context(), inst,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  inst->ForEachInId([&builder, &modified, this](uint32_t* idp) {
+  inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
     Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
     if (!IsFloat(op_inst, 32)) return;
-    GenConvert(idp, 16, &builder);
+    GenConvert(idp, 16, inst);
     modified = true;
   });
   if (IsFloat(inst, 32)) {
     inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+    converted_ids_.insert(inst->result_id());
     modified = true;
   }
   if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
@@ -171,23 +178,10 @@
 }
 
 bool ConvertToHalfPass::ProcessPhi(Instruction* inst) {
-  // Skip if not float32
-  if (!IsFloat(inst, 32)) return false;
-  // Skip if no relaxed operands.
-  bool relaxed_found = false;
-  uint32_t ocnt = 0;
-  inst->ForEachInId([&ocnt, &relaxed_found, this](uint32_t* idp) {
-    if (ocnt % 2 == 0) {
-      Instruction* val_inst = get_def_use_mgr()->GetDef(*idp);
-      if (IsRelaxed(val_inst)) relaxed_found = true;
-    }
-    ++ocnt;
-  });
-  if (!relaxed_found) return false;
   // Add float16 converts of any float32 operands and change type
   // of phi to float16 equivalent. Operand converts need to be added to
   // preceeding blocks.
-  ocnt = 0;
+  uint32_t ocnt = 0;
   uint32_t* prev_idp;
   inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) {
     if (ocnt % 2 == 0) {
@@ -203,65 +197,32 @@
               insert_before->opcode() != SpvOpLoopMerge)
             ++insert_before;
         }
-        InstructionBuilder builder(context(), &*insert_before,
-                                   IRContext::kAnalysisDefUse |
-                                       IRContext::kAnalysisInstrToBlockMapping);
-        GenConvert(prev_idp, 16, &builder);
+        GenConvert(prev_idp, 16, &*insert_before);
       }
     }
     ++ocnt;
   });
   inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
   get_def_use_mgr()->AnalyzeInstUse(inst);
+  converted_ids_.insert(inst->result_id());
   return true;
 }
 
-bool ConvertToHalfPass::ProcessExtract(Instruction* inst) {
-  bool modified = false;
-  uint32_t comp_id = inst->GetSingleWordInOperand(0);
-  Instruction* comp_inst = get_def_use_mgr()->GetDef(comp_id);
-  // If extract is relaxed float32 based type and the composite is a relaxed
-  // float32 based type, convert it to float16 equivalent. This is slightly
-  // aggressive and pushes any likely conversion to apply to the whole
-  // composite rather than apply to each extracted component later. This
-  // can be a win if the platform can convert the entire composite in the same
-  // time as one component. It risks converting components that may not be
-  // used, although empirical data on a large set of real-world shaders seems
-  // to suggest this is not common and the composite convert is the best choice.
-  if (IsFloat(inst, 32) && IsRelaxed(inst) && IsFloat(comp_inst, 32) &&
-      IsRelaxed(comp_inst)) {
-    InstructionBuilder builder(
-        context(), inst,
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-    GenConvert(&comp_id, 16, &builder);
-    inst->SetInOperand(0, {comp_id});
-    comp_inst = get_def_use_mgr()->GetDef(comp_id);
-    modified = true;
-  }
-  // If the composite is a float16 based type, make sure the type of the
-  // extract agrees.
-  if (IsFloat(comp_inst, 16) && !IsFloat(inst, 16)) {
-    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
-    modified = true;
-  }
-  if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
-  return modified;
-}
-
 bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
   // If float32 and relaxed, change to float16 convert
-  if (IsFloat(inst, 32) && IsRelaxed(inst)) {
+  if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) {
     inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
     get_def_use_mgr()->AnalyzeInstUse(inst);
+    converted_ids_.insert(inst->result_id());
   }
-  // If operand and result types are the same, replace result with operand
-  // and change convert to copy to keep validator happy; DCE will clean it up
+  // If operand and result types are the same, change FConvert to CopyObject to
+  // keep validator happy; simplification and DCE will clean it up
+  // One way this can happen is if an FConvert generated during this pass
+  // (likely by ProcessPhi) is later encountered here and its operand has been
+  // changed to half.
   uint32_t val_id = inst->GetSingleWordInOperand(0);
   Instruction* val_inst = get_def_use_mgr()->GetDef(val_id);
-  if (inst->type_id() == val_inst->type_id()) {
-    context()->ReplaceAllUsesWith(inst->result_id(), val_id);
-    inst->SetOpcode(SpvOpCopyObject);
-  }
+  if (inst->type_id() == val_inst->type_id()) inst->SetOpcode(SpvOpCopyObject);
   return true;  // modified
 }
 
@@ -270,12 +231,8 @@
   // If image reference, only need to convert dref args back to float32
   if (dref_image_ops_.count(inst->opcode()) != 0) {
     uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx);
-    Instruction* dref_inst = get_def_use_mgr()->GetDef(dref_id);
-    if (IsFloat(dref_inst, 16) && IsRelaxed(dref_inst)) {
-      InstructionBuilder builder(
-          context(), inst,
-          IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-      GenConvert(&dref_id, 32, &builder);
+    if (converted_ids_.count(dref_id) > 0) {
+      GenConvert(&dref_id, 32, inst);
       inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id});
       get_def_use_mgr()->AnalyzeInstUse(inst);
       modified = true;
@@ -288,32 +245,24 @@
   bool modified = false;
   // If non-relaxed instruction has changed operands, need to convert
   // them back to float32
-  InstructionBuilder builder(
-      context(), inst,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  inst->ForEachInId([&builder, &modified, this](uint32_t* idp) {
-    Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
-    if (!IsFloat(op_inst, 16)) return;
-    if (!IsRelaxed(op_inst)) return;
+  inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
+    if (converted_ids_.count(*idp) == 0) return;
     uint32_t old_id = *idp;
-    GenConvert(idp, 32, &builder);
+    GenConvert(idp, 32, inst);
     if (*idp != old_id) modified = true;
   });
   if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
   return modified;
 }
 
-bool ConvertToHalfPass::GenHalfCode(Instruction* inst) {
+bool ConvertToHalfPass::GenHalfInst(Instruction* inst) {
   bool modified = false;
   // Remember id for later deletion of RelaxedPrecision decoration
-  bool inst_relaxed = IsRelaxed(inst);
-  if (inst_relaxed) relaxed_ids_.push_back(inst->result_id());
+  bool inst_relaxed = IsRelaxed(inst->result_id());
   if (IsArithmetic(inst) && inst_relaxed)
     modified = GenHalfArith(inst);
-  else if (inst->opcode() == SpvOpPhi)
+  else if (inst->opcode() == SpvOpPhi && inst_relaxed)
     modified = ProcessPhi(inst);
-  else if (inst->opcode() == SpvOpCompositeExtract)
-    modified = ProcessExtract(inst);
   else if (inst->opcode() == SpvOpFConvert)
     modified = ProcessConvert(inst);
   else if (image_ops_.count(inst->opcode()) != 0)
@@ -323,13 +272,62 @@
   return modified;
 }
 
+bool ConvertToHalfPass::CloseRelaxInst(Instruction* inst) {
+  if (inst->result_id() == 0) return false;
+  if (IsRelaxed(inst->result_id())) return false;
+  if (!IsFloat(inst, 32)) return false;
+  if (IsDecoratedRelaxed(inst)) {
+    AddRelaxed(inst->result_id());
+    return true;
+  }
+  if (closure_ops_.count(inst->opcode()) == 0) return false;
+  // Can relax if all float operands are relaxed
+  bool relax = true;
+  inst->ForEachInId([&relax, this](uint32_t* idp) {
+    Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
+    if (!IsFloat(op_inst, 32)) return;
+    if (!IsRelaxed(*idp)) relax = false;
+  });
+  if (relax) {
+    AddRelaxed(inst->result_id());
+    return true;
+  }
+  // Can relax if all uses are relaxed
+  relax = true;
+  get_def_use_mgr()->ForEachUser(inst, [&relax, this](Instruction* uinst) {
+    if (uinst->result_id() == 0 || !IsFloat(uinst, 32) ||
+        (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id()))) {
+      relax = false;
+      return;
+    }
+  });
+  if (relax) {
+    AddRelaxed(inst->result_id());
+    return true;
+  }
+  return false;
+}
+
 bool ConvertToHalfPass::ProcessFunction(Function* func) {
+  // Do a closure of Relaxed on composite and phi instructions
+  bool changed = true;
+  while (changed) {
+    changed = false;
+    cfg()->ForEachBlockInReversePostOrder(
+        func->entry().get(), [&changed, this](BasicBlock* bb) {
+          for (auto ii = bb->begin(); ii != bb->end(); ++ii)
+            changed |= CloseRelaxInst(&*ii);
+        });
+  }
+  // Do convert of relaxed instructions to half precision
   bool modified = false;
   cfg()->ForEachBlockInReversePostOrder(
       func->entry().get(), [&modified, this](BasicBlock* bb) {
         for (auto ii = bb->begin(); ii != bb->end(); ++ii)
-          modified |= GenHalfCode(&*ii);
+          modified |= GenHalfInst(&*ii);
       });
+  // Replace invalid converts of matrix into equivalent vector extracts,
+  // converts and finally a composite construct
   cfg()->ForEachBlockInReversePostOrder(
       func->entry().get(), [&modified, this](BasicBlock* bb) {
         for (auto ii = bb->begin(); ii != bb->end(); ++ii)
@@ -346,7 +344,7 @@
   // If modified, make sure module has Float16 capability
   if (modified) context()->AddCapability(SpvCapabilityFloat16);
   // Remove all RelaxedPrecision decorations from instructions and globals
-  for (auto c_id : relaxed_ids_) RemoveRelaxedDecoration(c_id);
+  for (auto c_id : relaxed_ids_set_) RemoveRelaxedDecoration(c_id);
   for (auto& val : get_module()->types_values()) {
     uint32_t v_id = val.result_id();
     if (v_id != 0) RemoveRelaxedDecoration(v_id);
@@ -366,6 +364,7 @@
       SpvOpVectorShuffle,
       SpvOpCompositeConstruct,
       SpvOpCompositeInsert,
+      SpvOpCompositeExtract,
       SpvOpCopyObject,
       SpvOpTranspose,
       SpvOpConvertSToF,
@@ -453,7 +452,19 @@
       SpvOpImageSparseSampleProjDrefExplicitLod,
       SpvOpImageSparseDrefGather,
   };
-  relaxed_ids_.clear();
+  closure_ops_ = {
+      SpvOpVectorExtractDynamic,
+      SpvOpVectorInsertDynamic,
+      SpvOpVectorShuffle,
+      SpvOpCompositeConstruct,
+      SpvOpCompositeInsert,
+      SpvOpCompositeExtract,
+      SpvOpCopyObject,
+      SpvOpTranspose,
+      SpvOpPhi,
+  };
+  relaxed_ids_set_.clear();
+  converted_ids_.clear();
 }
 
 }  // namespace opt
diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h
index 5225848..143aebf 100644
--- a/source/opt/convert_to_half_pass.h
+++ b/source/opt/convert_to_half_pass.h
@@ -38,7 +38,8 @@
   const char* name() const override { return "convert-to-half-pass"; }
 
  private:
-  // Return true if |inst| is an arithmetic op that can be of type float16
+  // Return true if |inst| is an arithmetic, composite or phi op that can be
+  // of type float16
   bool IsArithmetic(Instruction* inst);
 
   // Return true if |inst| returns scalar, vector or matrix type with base
@@ -46,7 +47,13 @@
   bool IsFloat(Instruction* inst, uint32_t width);
 
   // Return true if |inst| is decorated with RelaxedPrecision
-  bool IsRelaxed(Instruction* inst);
+  bool IsDecoratedRelaxed(Instruction* inst);
+
+  // Return true if |id| has been added to the relaxed id set
+  bool IsRelaxed(uint32_t id);
+
+  // Add |id| to the relaxed id set
+  void AddRelaxed(uint32_t id);
 
   // Return type id for float with |width|
   analysis::Type* FloatScalarType(uint32_t width);
@@ -64,19 +71,23 @@
 
   // Append instructions to builder to convert value |*val_idp| to type
   // |ty_id| but with |width|. Set |*val_idp| to the new id.
-  void GenConvert(uint32_t* val_idp, uint32_t width,
-                  InstructionBuilder* builder);
+  void GenConvert(uint32_t* val_idp, uint32_t width, Instruction* inst);
 
   // Remove RelaxedPrecision decoration of |id|.
   void RemoveRelaxedDecoration(uint32_t id);
 
+  // Add |inst| to relaxed instruction set if warranted. Specifically, if
+  // it is float32 and either decorated relaxed or a composite or phi
+  // instruction where all operands are relaxed or all uses are relaxed.
+  bool CloseRelaxInst(Instruction* inst);
+
   // If |inst| is an arithmetic, phi, extract or convert instruction of float32
   // base type and decorated with RelaxedPrecision, change it to the equivalent
   // float16 based type instruction. Specifically, insert instructions to
   // convert all operands to float16 (if needed) and change its type to the
   // equivalent float16 type. Otherwise, insert instructions to convert its
   // operands back to their original types, if needed.
-  bool GenHalfCode(Instruction* inst);
+  bool GenHalfInst(Instruction* inst);
 
   // Gen code for relaxed arithmetic |inst|
   bool GenHalfArith(Instruction* inst);
@@ -84,9 +95,6 @@
   // Gen code for relaxed phi |inst|
   bool ProcessPhi(Instruction* inst);
 
-  // Gen code for relaxed extract |inst|
-  bool ProcessExtract(Instruction* inst);
-
   // Gen code for relaxed convert |inst|
   bool ProcessConvert(Instruction* inst);
 
@@ -98,11 +106,11 @@
 
   // If |inst| is an FConvert of a matrix type, decompose it to a series
   // of vector extracts, converts and inserts into an Undef. These are
-  // generated by GenHalfCode because they are easier to manipulate, but are
+  // generated by GenHalfInst because they are easier to manipulate, but are
   // invalid so we need to clean them up.
   bool MatConvertCleanup(Instruction* inst);
 
-  // Call GenHalfCode on every instruction in |func|.
+  // Call GenHalfInst on every instruction in |func|.
   // If code is generated for an instruction, replace the instruction
   // with the new instructions that are generated.
   bool ProcessFunction(Function* func);
@@ -124,8 +132,14 @@
   // Set of dref sample operations
   std::unordered_set<uint32_t> dref_image_ops_;
 
+  // Set of dref sample operations
+  std::unordered_set<uint32_t> closure_ops_;
+
+  // Set of ids of all relaxed instructions
+  std::unordered_set<uint32_t> relaxed_ids_set_;
+
   // Ids of all converted instructions
-  std::vector<uint32_t> relaxed_ids_;
+  std::unordered_set<uint32_t> converted_ids_;
 };
 
 }  // namespace opt
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index de740ca..1c8cdc8 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -553,7 +553,6 @@
                           const analysis::Constant* input1,
                           const analysis::Constant* input2) {
   assert(input1 && input2);
-  assert(input1->type() == input2->type());
   const analysis::Type* type = input1->type();
   std::vector<uint32_t> words;
   if (const analysis::Vector* vector_type = type->AsVector()) {
@@ -1502,60 +1501,64 @@
   };
 }
 
-FoldingRule CompositeExtractFeedingConstruct() {
-  // If the OpCompositeConstruct is simply putting back together elements that
-  // where extracted from the same souce, we can simlpy reuse the source.
-  //
-  // This is a common code pattern because of the way that scalar replacement
-  // works.
-  return [](IRContext* context, Instruction* inst,
-            const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpCompositeConstruct &&
-           "Wrong opcode.  Should be OpCompositeConstruct.");
-    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
-    uint32_t original_id = 0;
+// If the OpCompositeConstruct is simply putting back together elements that
+// where extracted from the same source, we can simply reuse the source.
+//
+// This is a common code pattern because of the way that scalar replacement
+// works.
+bool CompositeExtractFeedingConstruct(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>&) {
+  assert(inst->opcode() == SpvOpCompositeConstruct &&
+         "Wrong opcode.  Should be OpCompositeConstruct.");
+  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  uint32_t original_id = 0;
 
-    // Check each element to make sure they are:
-    // - extractions
-    // - extracting the same position they are inserting
-    // - all extract from the same id.
-    for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
-      uint32_t element_id = inst->GetSingleWordInOperand(i);
-      Instruction* element_inst = def_use_mgr->GetDef(element_id);
+  if (inst->NumInOperands() == 0) {
+    // The struct being constructed has no members.
+    return false;
+  }
 
-      if (element_inst->opcode() != SpvOpCompositeExtract) {
-        return false;
-      }
+  // Check each element to make sure they are:
+  // - extractions
+  // - extracting the same position they are inserting
+  // - all extract from the same id.
+  for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
+    const uint32_t element_id = inst->GetSingleWordInOperand(i);
+    Instruction* element_inst = def_use_mgr->GetDef(element_id);
 
-      if (element_inst->NumInOperands() != 2) {
-        return false;
-      }
-
-      if (element_inst->GetSingleWordInOperand(1) != i) {
-        return false;
-      }
-
-      if (i == 0) {
-        original_id =
-            element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
-      } else if (original_id != element_inst->GetSingleWordInOperand(
-                                    kExtractCompositeIdInIdx)) {
-        return false;
-      }
-    }
-
-    // The last check it to see that the object being extracted from is the
-    // correct type.
-    Instruction* original_inst = def_use_mgr->GetDef(original_id);
-    if (original_inst->type_id() != inst->type_id()) {
+    if (element_inst->opcode() != SpvOpCompositeExtract) {
       return false;
     }
 
-    // Simplify by using the original object.
-    inst->SetOpcode(SpvOpCopyObject);
-    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
-    return true;
-  };
+    if (element_inst->NumInOperands() != 2) {
+      return false;
+    }
+
+    if (element_inst->GetSingleWordInOperand(1) != i) {
+      return false;
+    }
+
+    if (i == 0) {
+      original_id =
+          element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
+    } else if (original_id !=
+               element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx)) {
+      return false;
+    }
+  }
+
+  // The last check it to see that the object being extracted from is the
+  // correct type.
+  Instruction* original_inst = def_use_mgr->GetDef(original_id);
+  if (original_inst->type_id() != inst->type_id()) {
+    return false;
+  }
+
+  // Simplify by using the original object.
+  inst->SetOpcode(SpvOpCopyObject);
+  inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
+  return true;
 }
 
 FoldingRule InsertFeedingExtract() {
@@ -2420,7 +2423,7 @@
   // Note that the order in which rules are added to the list matters. If a rule
   // applies to the instruction, the rest of the rules will not be attempted.
   // Take that into consideration.
-  rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct());
+  rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct);
 
   rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract());
   rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract());
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp
index e309d3a..22c979c 100644
--- a/source/opt/graphics_robust_access_pass.cpp
+++ b/source/opt/graphics_robust_access_pass.cpp
@@ -121,6 +121,10 @@
 //      the result of an OpArrayLength instruction acting on the pointer of
 //      the containing structure as noted above.
 //
+//      - Access chain indices are always treated as signed, so:
+//        - Clamp the upper bound at the signed integer maximum.
+//        - Use SClamp for all clamping.
+//
 //    - TODO(dneto): OpImageTexelPointer:
 //      - Clamp coordinate to the image size returned by OpImageQuerySize
 //      - If multi-sampled, clamp the sample index to the count returned by
@@ -141,6 +145,7 @@
 #include <cstring>
 #include <functional>
 #include <initializer_list>
+#include <limits>
 #include <utility>
 
 #include "constants.h"
@@ -246,6 +251,7 @@
   }
   for (auto* inst : access_chains) {
     ClampIndicesForAccessChain(inst);
+    if (module_status_.failed) return module_status_.modified;
   }
 
   for (auto* inst : image_texel_pointers) {
@@ -261,6 +267,8 @@
   auto* constant_mgr = context()->get_constant_mgr();
   auto* def_use_mgr = context()->get_def_use_mgr();
   auto* type_mgr = context()->get_type_mgr();
+  const bool have_int64_cap =
+      context()->get_feature_mgr()->HasCapability(SpvCapabilityInt64);
 
   // Replaces one of the OpAccessChain index operands with a new value.
   // Updates def-use analysis.
@@ -268,37 +276,66 @@
                                             Instruction* new_value) {
     inst.SetOperand(operand_index, {new_value->result_id()});
     def_use_mgr->AnalyzeInstUse(&inst);
+    return SPV_SUCCESS;
   };
 
   // Replaces one of the OpAccesssChain index operands with a clamped value.
   // Replace the operand at |operand_index| with the value computed from
-  // unsigned_clamp(%old_value, %min_value, %max_value).  It also analyzes
+  // signed_clamp(%old_value, %min_value, %max_value).  It also analyzes
   // the new instruction and records that them module is modified.
-  auto clamp_index = [&inst, this, &replace_index](
+  // Assumes %min_value is signed-less-or-equal than %max_value. (All callees
+  // use 0 for %min_value).
+  auto clamp_index = [&inst, type_mgr, this, &replace_index](
                          uint32_t operand_index, Instruction* old_value,
                          Instruction* min_value, Instruction* max_value) {
-    auto* clamp_inst = MakeClampInst(old_value, min_value, max_value, &inst);
-    replace_index(operand_index, clamp_inst);
+    auto* clamp_inst =
+        MakeSClampInst(*type_mgr, old_value, min_value, max_value, &inst);
+    return replace_index(operand_index, clamp_inst);
   };
 
   // Ensures the specified index of access chain |inst| has a value that is
   // at most |count| - 1.  If the index is already a constant value less than
   // |count| then no change is made.
-  auto clamp_to_literal_count = [&inst, this, &constant_mgr, &type_mgr,
-                                 &replace_index, &clamp_index](
-                                    uint32_t operand_index, uint64_t count) {
+  auto clamp_to_literal_count =
+      [&inst, this, &constant_mgr, &type_mgr, have_int64_cap, &replace_index,
+       &clamp_index](uint32_t operand_index, uint64_t count) -> spv_result_t {
     Instruction* index_inst =
         this->GetDef(inst.GetSingleWordOperand(operand_index));
     const auto* index_type =
         type_mgr->GetType(index_inst->type_id())->AsInteger();
     assert(index_type);
+    const auto index_width = index_type->width();
+
     if (count <= 1) {
       // Replace the index with 0.
-      replace_index(operand_index, GetValueForType(0, index_type));
-      return;
+      return replace_index(operand_index, GetValueForType(0, index_type));
     }
 
-    const auto index_width = index_type->width();
+    uint64_t maxval = count - 1;
+
+    // Compute the bit width of a viable type to hold |maxval|.
+    // Look for a bit width, up to 64 bits wide, to fit maxval.
+    uint32_t maxval_width = index_width;
+    while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) {
+      maxval_width *= 2;
+    }
+    // Determine the type for |maxval|.
+    analysis::Integer signed_type_for_query(maxval_width, true);
+    auto* maxval_type =
+        type_mgr->GetRegisteredType(&signed_type_for_query)->AsInteger();
+    // Access chain indices are treated as signed, so limit the maximum value
+    // of the index so it will always be positive for a signed clamp operation.
+    maxval = std::min(maxval, ((uint64_t(1) << (maxval_width - 1)) - 1));
+
+    if (index_width > 64) {
+      return this->Fail() << "Can't handle indices wider than 64 bits, found "
+                             "constant index with "
+                          << index_width << " bits as index number "
+                          << operand_index << " of access chain "
+                          << inst.PrettyPrint();
+    }
+
+    // Split into two cases: the current index is a constant, or not.
 
     // If the index is a constant then |index_constant| will not be a null
     // pointer.  (If index is an |OpConstantNull| then it |index_constant| will
@@ -313,55 +350,62 @@
         value = int64_t(int_index_constant->GetS32BitValue());
       } else if (index_width <= 64) {
         value = int_index_constant->GetS64BitValue();
-      } else {
-        this->Fail() << "Can't handle indices wider than 64 bits, found "
-                        "constant index with "
-                     << index_type->width() << "bits";
-        return;
       }
       if (value < 0) {
-        replace_index(operand_index, GetValueForType(0, index_type));
-      } else if (uint64_t(value) < count) {
+        return replace_index(operand_index, GetValueForType(0, index_type));
+      } else if (uint64_t(value) <= maxval) {
         // Nothing to do.
-        return;
+        return SPV_SUCCESS;
       } else {
-        // Replace with count - 1.
+        // Replace with maxval.
         assert(count > 0);  // Already took care of this case above.
-        replace_index(operand_index, GetValueForType(count - 1, index_type));
+        return replace_index(operand_index,
+                             GetValueForType(maxval, maxval_type));
       }
     } else {
       // Generate a clamp instruction.
-
-      // Compute the bit width of a viable type to hold (count-1).
-      const auto maxval = count - 1;
-      const auto* maxval_type = index_type;
-      // Look for a bit width, up to 64 bits wide, to fit maxval.
-      uint32_t maxval_width = index_width;
-      while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) {
-        maxval_width *= 2;
+      assert(maxval >= 1);
+      assert(index_width <= 64);  // Otherwise, already returned above.
+      if (index_width >= 64 && !have_int64_cap) {
+        // An inconsistent module.
+        return Fail() << "Access chain index is wider than 64 bits, but Int64 "
+                         "is not declared: "
+                      << index_inst->PrettyPrint();
       }
       // Widen the index value if necessary
       if (maxval_width > index_width) {
-        // Find the wider type.  We only need this case if a constant (array)
-        // bound is too big.  This never requires us to *add* a capability
-        // declaration for Int64 because the existence of the array bound would
-        // already have required that declaration.
+        // Find the wider type.  We only need this case if a constant array
+        // bound is too big.
+
+        // From how we calculated maxval_width, widening won't require adding
+        // the Int64 capability.
+        assert(have_int64_cap || maxval_width <= 32);
+        if (!have_int64_cap && maxval_width >= 64) {
+          // Be defensive, but this shouldn't happen.
+          return this->Fail()
+                 << "Clamping index would require adding Int64 capability. "
+                 << "Can't clamp 32-bit index " << operand_index
+                 << " of access chain " << inst.PrettyPrint();
+        }
         index_inst = WidenInteger(index_type->IsSigned(), maxval_width,
                                   index_inst, &inst);
-        maxval_type = type_mgr->GetType(index_inst->type_id())->AsInteger();
       }
+
       // Finally, clamp the index.
-      clamp_index(operand_index, index_inst, GetValueForType(0, maxval_type),
-                  GetValueForType(maxval, maxval_type));
+      return clamp_index(operand_index, index_inst,
+                         GetValueForType(0, maxval_type),
+                         GetValueForType(maxval, maxval_type));
     }
+    return SPV_SUCCESS;
   };
 
   // Ensures the specified index of access chain |inst| has a value that is at
   // most the value of |count_inst| minus 1, where |count_inst| is treated as an
-  // unsigned integer.
+  // unsigned integer. This can log a failure.
   auto clamp_to_count = [&inst, this, &constant_mgr, &clamp_to_literal_count,
-                         &clamp_index, &type_mgr](uint32_t operand_index,
-                                                  Instruction* count_inst) {
+                         &clamp_index,
+                         &type_mgr](uint32_t operand_index,
+                                    Instruction* count_inst) -> spv_result_t {
     Instruction* index_inst =
         this->GetDef(inst.GetSingleWordOperand(operand_index));
     const auto* index_type =
@@ -378,12 +422,11 @@
       } else if (width <= 64) {
         value = count_constant->AsIntConstant()->GetU64BitValue();
       } else {
-        this->Fail() << "Can't handle indices wider than 64 bits, found "
-                        "constant index with "
-                     << index_type->width() << "bits";
-        return;
+        return this->Fail() << "Can't handle indices wider than 64 bits, found "
+                               "constant index with "
+                            << index_type->width() << "bits";
       }
-      clamp_to_literal_count(operand_index, value);
+      return clamp_to_literal_count(operand_index, value);
     } else {
       // Widen them to the same width.
       const auto index_width = index_type->width();
@@ -406,9 +449,21 @@
           &inst, SpvOpISub, type_mgr->GetId(wider_type), TakeNextId(),
           {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}},
            {SPV_OPERAND_TYPE_ID, {one->result_id()}}});
-      clamp_index(operand_index, index_inst, GetValueForType(0, wider_type),
-                  count_minus_1);
+      auto* zero = GetValueForType(0, wider_type);
+      // Make sure we clamp to an upper bound that is at most the signed max
+      // for the target type.
+      const uint64_t max_signed_value =
+          ((uint64_t(1) << (target_width - 1)) - 1);
+      // Use unsigned-min to ensure that the result is always non-negative.
+      // That ensures we satisfy the invariant for SClamp, where the "min"
+      // argument we give it (zero), is no larger than the third argument.
+      auto* upper_bound =
+          MakeUMinInst(*type_mgr, count_minus_1,
+                       GetValueForType(max_signed_value, wider_type), &inst);
+      // Now clamp the index to this upper bound.
+      return clamp_index(operand_index, index_inst, zero, upper_bound);
     }
+    return SPV_SUCCESS;
   };
 
   const Instruction* base_inst = GetDef(inst.GetSingleWordInOperand(0));
@@ -483,6 +538,7 @@
           return;
         }
         clamp_to_count(idx, array_len);
+        if (module_status_.failed) return;
         pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
       } break;
 
@@ -557,22 +613,51 @@
   return conversion;
 }
 
-Instruction* GraphicsRobustAccessPass::MakeClampInst(Instruction* x,
-                                                     Instruction* min,
-                                                     Instruction* max,
-                                                     Instruction* where) {
+Instruction* GraphicsRobustAccessPass::MakeUMinInst(
+    const analysis::TypeManager& tm, Instruction* x, Instruction* y,
+    Instruction* where) {
+  // Get IDs of instructions we'll be referencing. Evaluate them before calling
+  // the function so we force a deterministic ordering in case both of them need
+  // to take a new ID.
+  const uint32_t glsl_insts_id = GetGlslInsts();
+  uint32_t smin_id = TakeNextId();
+  const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
+  const auto ywidth = tm.GetType(y->type_id())->AsInteger()->width();
+  assert(xwidth == ywidth);
+  (void)xwidth;
+  (void)ywidth;
+  auto* smin_inst = InsertInst(
+      where, SpvOpExtInst, x->type_id(), smin_id,
+      {
+          {SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
+          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UMin}},
+          {SPV_OPERAND_TYPE_ID, {x->result_id()}},
+          {SPV_OPERAND_TYPE_ID, {y->result_id()}},
+      });
+  return smin_inst;
+}
+
+Instruction* GraphicsRobustAccessPass::MakeSClampInst(
+    const analysis::TypeManager& tm, Instruction* x, Instruction* min,
+    Instruction* max, Instruction* where) {
   // Get IDs of instructions we'll be referencing. Evaluate them before calling
   // the function so we force a deterministic ordering in case both of them need
   // to take a new ID.
   const uint32_t glsl_insts_id = GetGlslInsts();
   uint32_t clamp_id = TakeNextId();
-  assert(x->type_id() == min->type_id());
-  assert(x->type_id() == max->type_id());
+  const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
+  const auto minwidth = tm.GetType(min->type_id())->AsInteger()->width();
+  const auto maxwidth = tm.GetType(max->type_id())->AsInteger()->width();
+  assert(xwidth == minwidth);
+  assert(xwidth == maxwidth);
+  (void)xwidth;
+  (void)minwidth;
+  (void)maxwidth;
   auto* clamp_inst = InsertInst(
       where, SpvOpExtInst, x->type_id(), clamp_id,
       {
           {SPV_OPERAND_TYPE_ID, {glsl_insts_id}},
-          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UClamp}},
+          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450SClamp}},
           {SPV_OPERAND_TYPE_ID, {x->result_id()}},
           {SPV_OPERAND_TYPE_ID, {min->result_id()}},
           {SPV_OPERAND_TYPE_ID, {max->result_id()}},
@@ -716,6 +801,7 @@
 spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer(
     opt::Instruction* image_texel_pointer) {
   // TODO(dneto): Write tests for this code.
+  // TODO(dneto): Use signed-clamp
   return SPV_SUCCESS;
 
   // Example:
@@ -916,9 +1002,9 @@
         {constant_mgr->GetDefiningInstruction(coordinate_1)->result_id()}}});
 
   // Clamp the coordinate
-  auto* clamp_coord =
-      MakeClampInst(coord, constant_mgr->GetDefiningInstruction(coordinate_0),
-                    query_max_including_faces, image_texel_pointer);
+  auto* clamp_coord = MakeSClampInst(
+      *type_mgr, coord, constant_mgr->GetDefiningInstruction(coordinate_0),
+      query_max_including_faces, image_texel_pointer);
   image_texel_pointer->SetInOperand(1, {clamp_coord->result_id()});
 
   // Clamp the sample index
@@ -936,8 +1022,8 @@
                                    {{SPV_OPERAND_TYPE_ID, {query_samples_id}},
                                     {SPV_OPERAND_TYPE_ID, {component_1_id}}});
 
-    auto* clamp_samples = MakeClampInst(
-        samples, constant_mgr->GetDefiningInstruction(coordinate_0),
+    auto* clamp_samples = MakeSClampInst(
+        *type_mgr, samples, constant_mgr->GetDefiningInstruction(coordinate_0),
         max_samples, image_texel_pointer);
     image_texel_pointer->SetInOperand(2, {clamp_samples->result_id()});
 
diff --git a/source/opt/graphics_robust_access_pass.h b/source/opt/graphics_robust_access_pass.h
index b21154e..6fc692c 100644
--- a/source/opt/graphics_robust_access_pass.h
+++ b/source/opt/graphics_robust_access_pass.h
@@ -18,13 +18,13 @@
 #include <map>
 #include <unordered_map>
 
-#include "source/diagnostic.h"
-
 #include "constants.h"
 #include "def_use_manager.h"
 #include "instruction.h"
 #include "module.h"
 #include "pass.h"
+#include "source/diagnostic.h"
+#include "type_manager.h"
 
 namespace spvtools {
 namespace opt {
@@ -49,7 +49,8 @@
   // consumer.
   spvtools::DiagnosticStream Fail();
 
-  // Returns SPV_SUCCESS if this pass can correctly process the module.
+  // Returns SPV_SUCCESS if this pass can correctly process the module,
+  // as far as we can tell from capabilities and the memory model.
   // Otherwise logs a message and returns a failure code.
   spv_result_t IsCompatibleModule();
 
@@ -59,12 +60,12 @@
   spv_result_t ProcessCurrentModule();
 
   // Process the given function.  Updates the state value |_|.  Returns true
-  // if the module was modified.
+  // if the module was modified.  This can log a failure.
   bool ProcessAFunction(opt::Function*);
 
   // Clamps indices in the OpAccessChain or OpInBoundsAccessChain instruction
   // |access_chain|. Inserts instructions before the given instruction.  Updates
-  // analyses and records that the module is modified.
+  // analyses and records that the module is modified.  This can log a failure.
   void ClampIndicesForAccessChain(Instruction* access_chain);
 
   // Returns the id of the instruction importing the "GLSL.std.450" extended
@@ -85,17 +86,29 @@
   Instruction* WidenInteger(bool sign_extend, uint32_t bit_width,
                             Instruction* value, Instruction* before_inst);
 
-  // Returns a new instruction that invokes the UClamp GLSL.std.450 extended
+  // Returns a new instruction that invokes the UMin GLSL.std.450 extended
+  // instruction with the two given operands.  That is, the result of the
+  // instruction is:
+  //  - |x| if |x| is unsigned-less than |y|
+  //  - |y| otherwise
+  // We assume that |x| and |y| are scalar integer types with the same
+  // width.  The instruction is inserted before |where|.
+  opt::Instruction* MakeUMinInst(const analysis::TypeManager& tm,
+                                 Instruction* x, Instruction* y,
+                                 Instruction* where);
+
+  // Returns a new instruction that invokes the SClamp GLSL.std.450 extended
   // instruction with the three given operands.  That is, the result of the
   // instruction is:
-  //  - |min| if |x| is unsigned-less than |min|
-  //  - |max| if |x| is unsigned-more than |max|
+  //  - |min| if |x| is signed-less than |min|
+  //  - |max| if |x| is signed-more than |max|
   //  - |x| otherwise.
-  // We assume that |min| is unsigned-less-or-equal to |max|, and that the
+  // We assume that |min| is signed-less-or-equal to |max|, and that the
   // operands all have the same scalar integer type.  The instruction is
   // inserted before |where|.
-  opt::Instruction* MakeClampInst(Instruction* x, Instruction* min,
-                                  Instruction* max, Instruction* where);
+  opt::Instruction* MakeSClampInst(const analysis::TypeManager& tm,
+                                   Instruction* x, Instruction* min,
+                                   Instruction* max, Instruction* where);
 
   // Returns a new instruction which evaluates to the length the runtime array
   // referenced by the access chain at the specfied index.  The instruction is
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index 36991ec..3c874a7 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -27,7 +27,6 @@
 static const int kSpvFunctionCallFunctionId = 2;
 static const int kSpvFunctionCallArgumentId = 3;
 static const int kSpvReturnValueId = 0;
-static const int kSpvLoopMergeContinueTargetIdInIdx = 1;
 
 namespace spvtools {
 namespace opt {
@@ -285,19 +284,14 @@
     if (rid != 0) callee_result_ids.insert(rid);
   });
 
-  // If the caller is in a single-block loop, and the callee has multiple
-  // blocks, then the normal inlining logic will place the OpLoopMerge in
-  // the last of several blocks in the loop.  Instead, it should be placed
-  // at the end of the first block.  First determine if the caller is in a
-  // single block loop.  We'll wait to move the OpLoopMerge until the end
-  // of the regular inlining logic, and only if necessary.
-  bool caller_is_single_block_loop = false;
+  // If the caller is a loop header and the callee has multiple blocks, then the
+  // normal inlining logic will place the OpLoopMerge in the last of several
+  // blocks in the loop.  Instead, it should be placed at the end of the first
+  // block.  We'll wait to move the OpLoopMerge until the end of the regular
+  // inlining logic, and only if necessary.
   bool caller_is_loop_header = false;
-  if (auto* loop_merge = call_block_itr->GetLoopMergeInst()) {
+  if (call_block_itr->GetLoopMergeInst()) {
     caller_is_loop_header = true;
-    caller_is_single_block_loop =
-        call_block_itr->id() ==
-        loop_merge->GetSingleWordInOperand(kSpvLoopMergeContinueTargetIdInIdx);
   }
 
   bool callee_begins_with_structured_header =
@@ -611,10 +605,6 @@
     --loop_merge_itr;
     assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
     std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
-    if (caller_is_single_block_loop) {
-      // Also, update its continue target to point to the last block.
-      cp_inst->SetInOperand(kSpvLoopMergeContinueTargetIdInIdx, {last->id()});
-    }
     first->tail().InsertBefore(std::move(cp_inst));
 
     // Remove the loop merge from the last block.
diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h
index 51d7712..447871b 100644
--- a/source/opt/inst_bindless_check_pass.h
+++ b/source/opt/inst_bindless_check_pass.h
@@ -28,18 +28,19 @@
 // external design may change as the layer evolves.
 class InstBindlessCheckPass : public InstrumentPass {
  public:
-  // For test harness only
-  InstBindlessCheckPass()
-      : InstrumentPass(7, 23, kInstValidationIdBindless, 1),
-        input_length_enabled_(true),
-        input_init_enabled_(true) {}
-  // For all other interfaces
+  // Deprecated interface
   InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
                         bool input_length_enable, bool input_init_enable,
                         uint32_t version)
       : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, version),
         input_length_enabled_(input_length_enable),
         input_init_enabled_(input_init_enable) {}
+  // Preferred Interface
+  InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
+                        bool input_length_enable, bool input_init_enable)
+      : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless),
+        input_length_enabled_(input_length_enable),
+        input_init_enabled_(input_init_enable) {}
 
   ~InstBindlessCheckPass() override = default;
 
diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h
index 9ad3528..67ffcc3 100644
--- a/source/opt/inst_buff_addr_check_pass.h
+++ b/source/opt/inst_buff_addr_check_pass.h
@@ -28,13 +28,13 @@
 // external design of this class may change as the layer evolves.
 class InstBuffAddrCheckPass : public InstrumentPass {
  public:
-  // For test harness only
-  InstBuffAddrCheckPass()
-      : InstrumentPass(7, 23, kInstValidationIdBuffAddr, 1) {}
-  // For all other interfaces
+  // Deprecated interface
   InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version)
       : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr,
                        version) {}
+  // Preferred interface
+  InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id)
+      : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {}
 
   ~InstBuffAddrCheckPass() override = default;
 
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp
index 2a3e7f0..dfcd164 100644
--- a/source/opt/instrument_pass.cpp
+++ b/source/opt/instrument_pass.cpp
@@ -185,32 +185,12 @@
           GetUintId(), SpvOpCompositeExtract, load_id, 1);
       Instruction* z_inst = builder->AddIdLiteralOp(
           GetUintId(), SpvOpCompositeExtract, load_id, 2);
-      if (version_ == 1) {
-        // For version 1 format, as a stopgap, pack uvec3 into first word:
-        // x << 21 | y << 10 | z. Second word is unused. (DEPRECATED)
-        Instruction* x_shft_inst = builder->AddBinaryOp(
-            GetUintId(), SpvOpShiftLeftLogical, x_inst->result_id(),
-            builder->GetUintConstantId(21));
-        Instruction* y_shft_inst = builder->AddBinaryOp(
-            GetUintId(), SpvOpShiftLeftLogical, y_inst->result_id(),
-            builder->GetUintConstantId(10));
-        Instruction* x_or_y_inst = builder->AddBinaryOp(
-            GetUintId(), SpvOpBitwiseOr, x_shft_inst->result_id(),
-            y_shft_inst->result_id());
-        Instruction* x_or_y_or_z_inst =
-            builder->AddBinaryOp(GetUintId(), SpvOpBitwiseOr,
-                                 x_or_y_inst->result_id(), z_inst->result_id());
-        GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationId,
-                                x_or_y_or_z_inst->result_id(), builder);
-      } else {
-        // For version 2 format, write all three words
-        GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX,
-                                x_inst->result_id(), builder);
-        GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY,
-                                y_inst->result_id(), builder);
-        GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ,
-                                z_inst->result_id(), builder);
-      }
+      GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX,
+                              x_inst->result_id(), builder);
+      GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY,
+                              y_inst->result_id(), builder);
+      GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ,
+                              z_inst->result_id(), builder);
     } break;
     case SpvExecutionModelGeometry: {
       // Load and store PrimitiveId and InvocationId.
@@ -231,30 +211,23 @@
           kInstTessCtlOutPrimitiveId, base_offset_id, builder);
     } break;
     case SpvExecutionModelTessellationEvaluation: {
-      if (version_ == 1) {
-        // For format version 1, load and store InvocationId.
-        GenBuiltinOutputCode(
-            context()->GetBuiltinInputVarId(SpvBuiltInInvocationId),
-            kInstTessOutInvocationId, base_offset_id, builder);
-      } else {
-        // For format version 2, load and store PrimitiveId and TessCoord.uv
-        GenBuiltinOutputCode(
-            context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId),
-            kInstTessEvalOutPrimitiveId, base_offset_id, builder);
-        uint32_t load_id = GenVarLoad(
-            context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder);
-        Instruction* uvec3_cast_inst =
-            builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id);
-        uint32_t uvec3_cast_id = uvec3_cast_inst->result_id();
-        Instruction* u_inst = builder->AddIdLiteralOp(
-            GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0);
-        Instruction* v_inst = builder->AddIdLiteralOp(
-            GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1);
-        GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU,
-                                u_inst->result_id(), builder);
-        GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV,
-                                v_inst->result_id(), builder);
-      }
+      // Load and store PrimitiveId and TessCoord.uv
+      GenBuiltinOutputCode(
+          context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId),
+          kInstTessEvalOutPrimitiveId, base_offset_id, builder);
+      uint32_t load_id = GenVarLoad(
+          context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder);
+      Instruction* uvec3_cast_inst =
+          builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id);
+      uint32_t uvec3_cast_id = uvec3_cast_inst->result_id();
+      Instruction* u_inst = builder->AddIdLiteralOp(
+          GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0);
+      Instruction* v_inst = builder->AddIdLiteralOp(
+          GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1);
+      GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU,
+                              u_inst->result_id(), builder);
+      GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV,
+                              v_inst->result_id(), builder);
     } break;
     case SpvExecutionModelFragment: {
       // Load FragCoord and convert to Uint
@@ -671,8 +644,7 @@
         context(), &*new_blk_ptr,
         IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
     // Gen test if debug output buffer size will not be exceeded.
-    uint32_t val_spec_offset =
-        (version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt;
+    uint32_t val_spec_offset = kInstStageOutCnt;
     uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt;
     uint32_t buf_id = GetOutputBufferId();
     uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
@@ -892,6 +864,14 @@
 }
 
 bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
+  // Check that format version 2 requested
+  if (version_ != 2u) {
+    if (consumer()) {
+      std::string message = "Unsupported instrumentation format requested";
+      consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+    }
+    return false;
+  }
   // Make sure all entry points have the same execution model. Do not
   // instrument if they do not.
   // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module
@@ -905,12 +885,17 @@
   for (auto& e : get_module()->entry_points()) {
     if (ecnt == 0)
       stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx);
-    else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != stage)
+    else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) !=
+             stage) {
+      if (consumer()) {
+        std::string message = "Mixed stage shader module not supported";
+        consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+      }
       return false;
+    }
     ++ecnt;
   }
-  // Only supporting vertex, fragment and compute shaders at the moment.
-  // TODO(greg-lunarg): Handle all stages.
+  // Check for supported stages
   if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment &&
       stage != SpvExecutionModelGeometry &&
       stage != SpvExecutionModelGLCompute &&
@@ -920,8 +905,14 @@
       stage != SpvExecutionModelIntersectionNV &&
       stage != SpvExecutionModelAnyHitNV &&
       stage != SpvExecutionModelClosestHitNV &&
-      stage != SpvExecutionModelMissNV && stage != SpvExecutionModelCallableNV)
+      stage != SpvExecutionModelMissNV &&
+      stage != SpvExecutionModelCallableNV) {
+    if (consumer()) {
+      std::string message = "Stage not supported by instrumentation";
+      consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+    }
     return false;
+  }
   // Add together the roots of all entry points
   std::queue<uint32_t> roots;
   for (auto& e : get_module()->entry_points()) {
diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h
index ead3b73..02568fb 100644
--- a/source/opt/instrument_pass.h
+++ b/source/opt/instrument_pass.h
@@ -81,7 +81,16 @@
  protected:
   // Create instrumentation pass for |validation_id| which utilizes descriptor
   // set |desc_set| for debug input and output buffers and writes |shader_id|
-  // into debug output records with format |version|.
+  // into debug output records.
+  InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id)
+      : Pass(),
+        desc_set_(desc_set),
+        shader_id_(shader_id),
+        validation_id_(validation_id),
+        version_(2u) {}
+  // Create instrumentation pass for |validation_id| which utilizes descriptor
+  // set |desc_set| for debug input and output buffers and writes |shader_id|
+  // into debug output records with format |version|. Deprecated.
   InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id,
                  uint32_t version)
       : Pass(),
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index d940180..7bca29b 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -273,6 +273,14 @@
     }
   }
 
+  if (AreAnalysesValid(kAnalysisIdToFuncMapping)) {
+    for (auto& fn : *module_) {
+      if (id_to_func_[fn.result_id()] != &fn) {
+        return false;
+      }
+    }
+  }
+
   if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
     for (auto& func : *module()) {
       for (auto& block : func) {
@@ -818,6 +826,7 @@
     roots->pop();
     if (done.insert(fi).second) {
       Function* fn = GetFunction(fi);
+      assert(fn && "Trying to process a function that does not exist.");
       modified = pfn(fn) || modified;
       AddCalls(fn, roots);
     }
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index c68b3e2..e21e680 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -16,6 +16,7 @@
 
 #include <utility>
 
+#include "source/ext_inst.h"
 #include "source/opt/log.h"
 #include "source/opt/reflect.h"
 #include "source/util/make_unique.h"
@@ -113,11 +114,14 @@
       } else if (IsTypeInst(opcode)) {
         module_->AddType(std::move(spv_inst));
       } else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
-                 opcode == SpvOpUndef) {
+                 opcode == SpvOpUndef ||
+                 (opcode == SpvOpExtInst &&
+                  spvExtInstIsNonSemantic(inst->ext_inst_type))) {
         module_->AddGlobalValue(std::move(spv_inst));
       } else {
         Errorf(consumer_, src, loc,
-               "Unhandled inst type (opcode: %d) found outside function definition.",
+               "Unhandled inst type (opcode: %d) found outside function "
+               "definition.",
                opcode);
         return false;
       }
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index ece1abe..241aa75 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -198,33 +198,39 @@
       .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateMergeReturnPass())
       .RegisterPass(CreateInlineExhaustivePass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateEliminateDeadFunctionsPass())
       .RegisterPass(CreatePrivateToLocalPass())
-      .RegisterPass(CreateScalarReplacementPass())
-      .RegisterPass(CreateLocalAccessChainConvertPass())
-      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
-      .RegisterPass(CreateLocalSingleStoreElimPass())
-      .RegisterPass(CreateAggressiveDCEPass())
-      .RegisterPass(CreateSimplificationPass())
-      .RegisterPass(CreateDeadInsertElimPass())
+      .RegisterPass(CreateScalarReplacementPass(0))
       .RegisterPass(CreateLocalMultiStoreElimPass())
-      .RegisterPass(CreateAggressiveDCEPass())
       .RegisterPass(CreateCCPPass())
+      .RegisterPass(CreateLoopUnrollPass(true))
+      .RegisterPass(CreateDeadBranchElimPass())
+      .RegisterPass(CreateSimplificationPass())
+      .RegisterPass(CreateScalarReplacementPass(0))
+      .RegisterPass(CreateLocalSingleStoreElimPass())
+      .RegisterPass(CreateIfConversionPass())
+      .RegisterPass(CreateSimplificationPass())
       .RegisterPass(CreateAggressiveDCEPass())
       .RegisterPass(CreateDeadBranchElimPass())
-      .RegisterPass(CreateIfConversionPass())
-      .RegisterPass(CreateAggressiveDCEPass())
       .RegisterPass(CreateBlockMergePass())
-      .RegisterPass(CreateSimplificationPass())
+      .RegisterPass(CreateLocalAccessChainConvertPass())
+      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
+      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateCopyPropagateArraysPass())
+      .RegisterPass(CreateVectorDCEPass())
       .RegisterPass(CreateDeadInsertElimPass())
+      .RegisterPass(CreateEliminateDeadMembersPass())
+      .RegisterPass(CreateLocalSingleStoreElimPass())
+      .RegisterPass(CreateBlockMergePass())
+      .RegisterPass(CreateLocalMultiStoreElimPass())
       .RegisterPass(CreateRedundancyEliminationPass())
-      .RegisterPass(CreateCFGCleanupPass())
-      .RegisterPass(CreateAggressiveDCEPass());
+      .RegisterPass(CreateSimplificationPass())
+      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateCFGCleanupPass());
 }
 
 Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
-  return RegisterPass(CreateStripDebugInfoPass())
-      .RegisterPass(CreateStripAtomicCounterMemoryPass())
+  return RegisterPass(CreateStripAtomicCounterMemoryPass())
       .RegisterPass(CreateGenerateWebGPUInitializersPass())
       .RegisterPass(CreateLegalizeVectorShufflePass())
       .RegisterPass(CreateSplitInvalidUnreachablePass())
diff --git a/source/opt/register_pressure.cpp b/source/opt/register_pressure.cpp
index 34dac1d..cb24674 100644
--- a/source/opt/register_pressure.cpp
+++ b/source/opt/register_pressure.cpp
@@ -78,9 +78,16 @@
   //   - Second, walk loop forest to propagate registers crossing back-edges
   //   (add iterative values into the liveness set).
   void Compute() {
-    cfg_.ForEachBlockInPostOrder(&*function_->begin(), [this](BasicBlock* bb) {
-      ComputePartialLiveness(bb);
-    });
+    for (BasicBlock& start_bb : *function_) {
+      if (reg_pressure_->Get(start_bb.id()) != nullptr) {
+        continue;
+      }
+      cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) {
+        if (reg_pressure_->Get(bb->id()) == nullptr) {
+          ComputePartialLiveness(bb);
+        }
+      });
+    }
     DoLoopLivenessUnification();
     EvaluateRegisterRequirements();
   }
diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp
index 9e7fad0..936c966 100644
--- a/source/opt/strip_debug_info_pass.cpp
+++ b/source/opt/strip_debug_info_pass.cpp
@@ -19,12 +19,60 @@
 namespace opt {
 
 Pass::Status StripDebugInfoPass::Process() {
-  bool modified = !context()->debugs1().empty() ||
-                  !context()->debugs2().empty() ||
-                  !context()->debugs3().empty();
+  bool uses_non_semantic_info = false;
+  for (auto& inst : context()->module()->extensions()) {
+    const char* ext_name =
+        reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
+    if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+      uses_non_semantic_info = true;
+    }
+  }
 
   std::vector<Instruction*> to_kill;
-  for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
+
+  // if we use non-semantic info, it may reference OpString. Do a more
+  // expensive pass checking the uses of the OpString to see if any are
+  // OpExtInst on a non-semantic instruction set. If we're not using the
+  // extension then we can do a simpler pass and kill all debug1 instructions
+  if (uses_non_semantic_info) {
+    for (auto& inst : context()->module()->debugs1()) {
+      switch (inst.opcode()) {
+        case SpvOpString: {
+          analysis::DefUseManager* def_use = context()->get_def_use_mgr();
+
+          // see if this string is used anywhere by a non-semantic instruction
+          bool no_nonsemantic_use =
+              def_use->WhileEachUser(&inst, [def_use](Instruction* use) {
+                if (use->opcode() == SpvOpExtInst) {
+                  auto ext_inst_set =
+                      def_use->GetDef(use->GetSingleWordInOperand(0u));
+                  const char* extension_name = reinterpret_cast<const char*>(
+                      &ext_inst_set->GetInOperand(0).words[0]);
+                  if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
+                    // found a non-semantic use, return false as we cannot
+                    // remove this OpString
+                    return false;
+                  }
+                }
+
+                // other instructions can't be a non-semantic use
+                return true;
+              });
+
+          if (no_nonsemantic_use) to_kill.push_back(&inst);
+
+          break;
+        }
+
+        default:
+          to_kill.push_back(&inst);
+          break;
+      }
+    }
+  } else {
+    for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
+  }
+
   for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg);
   for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg);
 
@@ -38,8 +86,11 @@
               return false;
             });
 
+  bool modified = !to_kill.empty();
+
   for (auto* inst : to_kill) context()->KillInst(inst);
 
+  // clear OpLine information
   context()->module()->ForEachInst([&modified](Instruction* inst) {
     modified |= !inst->dbg_line_insts().empty();
     inst->dbg_line_insts().clear();
diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_reflect_info_pass.cpp
index 984073f..c231ead 100644
--- a/source/opt/strip_reflect_info_pass.cpp
+++ b/source/opt/strip_reflect_info_pass.cpp
@@ -67,9 +67,54 @@
     } else if (!other_uses_for_decorate_string &&
                0 == std::strcmp(ext_name, "SPV_GOOGLE_decorate_string")) {
       to_remove.push_back(&inst);
+    } else if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+      to_remove.push_back(&inst);
     }
   }
 
+  // clear all debug data now if it hasn't been cleared already, to remove any
+  // remaining OpString that may have been referenced by non-semantic extinsts
+  for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg);
+  for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg);
+  for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg);
+
+  // remove any extended inst imports that are non semantic
+  std::unordered_set<uint32_t> non_semantic_sets;
+  for (auto& inst : context()->module()->ext_inst_imports()) {
+    assert(inst.opcode() == SpvOpExtInstImport &&
+           "Expecting an import of an extension's instruction set.");
+    const char* extension_name =
+        reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
+    if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
+      non_semantic_sets.insert(inst.result_id());
+      to_remove.push_back(&inst);
+    }
+  }
+
+  // if we removed some non-semantic sets, then iterate over the instructions in
+  // the module to remove any OpExtInst that referenced those sets
+  if (!non_semantic_sets.empty()) {
+    context()->module()->ForEachInst(
+        [&non_semantic_sets, &to_remove](Instruction* inst) {
+          if (inst->opcode() == SpvOpExtInst) {
+            if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) !=
+                non_semantic_sets.end()) {
+              to_remove.push_back(inst);
+            }
+          }
+        });
+  }
+
+  // OpName must come first, since they may refer to other debug instructions.
+  // If they are after the instructions that refer to, then they will be killed
+  // when that instruction is killed, which will lead to a double kill.
+  std::sort(to_remove.begin(), to_remove.end(),
+            [](Instruction* lhs, Instruction* rhs) -> bool {
+              if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName)
+                return true;
+              return false;
+            });
+
   for (auto* inst : to_remove) {
     modified = true;
     context()->KillInst(inst);
diff --git a/source/opt/value_number_table.cpp b/source/opt/value_number_table.cpp
index 8df34ef..82549a6 100644
--- a/source/opt/value_number_table.cpp
+++ b/source/opt/value_number_table.cpp
@@ -93,7 +93,7 @@
 
   // Phi nodes are a type of copy.  If all of the inputs have the same value
   // number, then we can assign the result of the phi the same value number.
-  if (inst->opcode() == SpvOpPhi &&
+  if (inst->opcode() == SpvOpPhi && inst->NumInOperands() > 0 &&
       dec_mgr->HaveTheSameDecorations(inst->result_id(),
                                       inst->GetSingleWordInOperand(0))) {
     value = GetValueNumber(inst->GetSingleWordInOperand(0));
diff --git a/source/opt/wrap_opkill.h b/source/opt/wrap_opkill.h
index 87a5d69..09f2dfa 100644
--- a/source/opt/wrap_opkill.h
+++ b/source/opt/wrap_opkill.h
@@ -34,8 +34,7 @@
            IRContext::kAnalysisInstrToBlockMapping |
            IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
            IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId |
-           IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants |
-           IRContext::kAnalysisTypes;
+           IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
   }
 
  private:
diff --git a/source/print.cpp b/source/print.cpp
index 1d9829d..128587a 100644
--- a/source/print.cpp
+++ b/source/print.cpp
@@ -15,7 +15,8 @@
 #include "source/print.h"
 
 #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
-    defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || defined(SPIRV_EMSCRIPTEN)
+    defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) ||                         \
+    defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA)
 namespace spvtools {
 
 clr::reset::operator const char*() { return "\x1b[0m"; }
diff --git a/source/spirv_fuzzer_options.cpp b/source/spirv_fuzzer_options.cpp
index ab8903e..b407f14 100644
--- a/source/spirv_fuzzer_options.cpp
+++ b/source/spirv_fuzzer_options.cpp
@@ -23,7 +23,8 @@
     : has_random_seed(false),
       random_seed(0),
       replay_validation_enabled(false),
-      shrinker_step_limit(kDefaultStepLimit) {}
+      shrinker_step_limit(kDefaultStepLimit),
+      fuzzer_pass_validation_enabled(false) {}
 
 SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() {
   return new spv_fuzzer_options_t();
@@ -48,3 +49,8 @@
     spv_fuzzer_options options, uint32_t shrinker_step_limit) {
   options->shrinker_step_limit = shrinker_step_limit;
 }
+
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
+    spv_fuzzer_options options) {
+  options->fuzzer_pass_validation_enabled = true;
+}
diff --git a/source/spirv_fuzzer_options.h b/source/spirv_fuzzer_options.h
index 7bb16c7..143f77f 100644
--- a/source/spirv_fuzzer_options.h
+++ b/source/spirv_fuzzer_options.h
@@ -34,6 +34,9 @@
 
   // See spvFuzzerOptionsSetShrinkerStepLimit.
   uint32_t shrinker_step_limit;
+
+  // See spvFuzzerOptionsValidateAfterEveryPass.
+  bool fuzzer_pass_validation_enabled;
 };
 
 #endif  // SOURCE_SPIRV_FUZZER_OPTIONS_H_
diff --git a/source/text.cpp b/source/text.cpp
index d88d4f7..88a8e8f 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -242,14 +242,37 @@
       // The assembler accepts the symbolic name for an extended instruction,
       // and emits its corresponding number.
       spv_ext_inst_desc extInst;
-      if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst)) {
-        return context->diagnostic()
-               << "Invalid extended instruction name '" << textValue << "'.";
-      }
-      spvInstructionAddWord(pInst, extInst->ext_inst);
+      if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst) ==
+          SPV_SUCCESS) {
+        // if we know about this extended instruction, push the numeric value
+        spvInstructionAddWord(pInst, extInst->ext_inst);
 
-      // Prepare to parse the operands for the extended instructions.
-      spvPushOperandTypes(extInst->operandTypes, pExpectedOperands);
+        // Prepare to parse the operands for the extended instructions.
+        spvPushOperandTypes(extInst->operandTypes, pExpectedOperands);
+      } else {
+        // if we don't know this extended instruction and the set isn't
+        // non-semantic, we cannot process further
+        if (!spvExtInstIsNonSemantic(pInst->extInstType)) {
+          return context->diagnostic()
+                 << "Invalid extended instruction name '" << textValue << "'.";
+        } else {
+          // for non-semantic instruction sets, as long as the text name is an
+          // integer value we can encode it since we know the form of all such
+          // extended instructions
+          spv_literal_t extInstValue;
+          if (spvTextToLiteral(textValue, &extInstValue) ||
+              extInstValue.type != SPV_LITERAL_TYPE_UINT_32) {
+            return context->diagnostic()
+                   << "Couldn't translate unknown extended instruction name '"
+                   << textValue << "' to unsigned integer.";
+          }
+
+          spvInstructionAddWord(pInst, extInstValue.value.u32);
+
+          // opcode contains an unknown number of IDs.
+          pExpectedOperands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
+        }
+      }
     } break;
 
     case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
@@ -377,7 +400,8 @@
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
-    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: {
+    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: {
       uint32_t value;
       if (grammar.parseMaskOperand(type, textValue, &value)) {
         return context->diagnostic() << "Invalid " << spvOperandTypeStr(type)
diff --git a/source/val/basic_block.h b/source/val/basic_block.h
index efbd243..876105c 100644
--- a/source/val/basic_block.h
+++ b/source/val/basic_block.h
@@ -15,8 +15,8 @@
 #ifndef SOURCE_VAL_BASIC_BLOCK_H_
 #define SOURCE_VAL_BASIC_BLOCK_H_
 
-#include <cstdint>
 #include <bitset>
+#include <cstdint>
 #include <functional>
 #include <memory>
 #include <vector>
@@ -28,7 +28,7 @@
 
 enum BlockType : uint32_t {
   kBlockTypeUndefined,
-  kBlockTypeHeader,
+  kBlockTypeSelection,
   kBlockTypeLoop,
   kBlockTypeMerge,
   kBlockTypeBreak,
diff --git a/source/val/function.cpp b/source/val/function.cpp
index 876a608..0281770 100644
--- a/source/val/function.cpp
+++ b/source/val/function.cpp
@@ -14,9 +14,8 @@
 
 #include "source/val/function.h"
 
-#include <cassert>
-
 #include <algorithm>
+#include <cassert>
 #include <sstream>
 #include <unordered_map>
 #include <unordered_set>
@@ -99,7 +98,7 @@
 spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) {
   RegisterBlock(merge_id, false);
   BasicBlock& merge_block = blocks_.at(merge_id);
-  current_block_->set_type(kBlockTypeHeader);
+  current_block_->set_type(kBlockTypeSelection);
   merge_block.set_type(kBlockTypeMerge);
   merge_block_header_[&merge_block] = current_block_;
 
@@ -344,7 +343,7 @@
     BasicBlock* header = merge_block_header_[bb];
     assert(header);
     block_depth_[bb] = GetBlockDepth(header);
-  } else if (bb_dom->is_type(kBlockTypeHeader) ||
+  } else if (bb_dom->is_type(kBlockTypeSelection) ||
              bb_dom->is_type(kBlockTypeLoop)) {
     // The dominator of the given block is a header block. So, the nesting
     // depth of this block is: 1 + nesting depth of the header.
diff --git a/source/val/instruction.h b/source/val/instruction.h
index 1fa855f..e30bfbc 100644
--- a/source/val/instruction.h
+++ b/source/val/instruction.h
@@ -21,6 +21,7 @@
 #include <utility>
 #include <vector>
 
+#include "source/ext_inst.h"
 #include "source/table.h"
 #include "spirv-tools/libspirv.h"
 
@@ -85,6 +86,11 @@
     return inst_.ext_inst_type;
   }
 
+  bool IsNonSemantic() const {
+    return opcode() == SpvOp::SpvOpExtInst &&
+           spvExtInstIsNonSemantic(inst_.ext_inst_type);
+  }
+
   // Casts the words belonging to the operand under |index| to |T| and returns.
   template <typename T>
   T GetOperandAs(size_t index) const {
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index 2695280..df38f1b 100644
--- a/source/val/validate_annotation.cpp
+++ b/source/val/validate_annotation.cpp
@@ -286,7 +286,8 @@
     auto use = pair.first;
     if (use->opcode() != SpvOpDecorate && use->opcode() != SpvOpGroupDecorate &&
         use->opcode() != SpvOpGroupMemberDecorate &&
-        use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId) {
+        use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId &&
+        !use->IsNonSemantic()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Result id of OpDecorationGroup can only "
              << "be targeted by OpName, OpGroupDecorate, "
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index 4801fc5..43a6af7 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/val/validate.h"
-
 #include <algorithm>
 #include <cassert>
 #include <functional>
@@ -34,6 +32,7 @@
 #include "source/val/basic_block.h"
 #include "source/val/construct.h"
 #include "source/val/function.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -755,6 +754,26 @@
                  << header_name << " <ID> " << header->id();
         }
       }
+
+      if (block->is_type(BlockType::kBlockTypeSelection) ||
+          block->is_type(BlockType::kBlockTypeLoop)) {
+        size_t index = (block->terminator() - &_.ordered_instructions()[0]) - 1;
+        const auto& merge_inst = _.ordered_instructions()[index];
+        if (merge_inst.opcode() == SpvOpSelectionMerge ||
+            merge_inst.opcode() == SpvOpLoopMerge) {
+          uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0);
+          auto merge_block = function->GetBlock(merge_id).first;
+          if (merge_block->reachable() &&
+              !construct_blocks.count(merge_block)) {
+            return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
+                   << "Header block " << _.getIdName(block->id())
+                   << " is contained in the " << construct_name
+                   << " construct headed by " << _.getIdName(header->id())
+                   << ", but its merge block " << _.getIdName(merge_id)
+                   << " is not";
+          }
+        }
+      }
     }
 
     // Checks rules for case constructs.
diff --git a/source/val/validate_debug.cpp b/source/val/validate_debug.cpp
index b49890f..0a25d8a 100644
--- a/source/val/validate_debug.cpp
+++ b/source/val/validate_debug.cpp
@@ -56,13 +56,6 @@
 }  // namespace
 
 spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst) {
-  if (spvIsWebGPUEnv(_.context()->target_env) &&
-      spvOpcodeIsDebug(inst->opcode())) {
-    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-           << "Debugging instructions are not allowed in the WebGPU execution "
-           << "environment.";
-  }
-
   switch (inst->opcode()) {
     case SpvOpMemberName:
       if (auto error = ValidateMemberName(_, inst)) return error;
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index d513a25..3b44833 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -1275,6 +1275,7 @@
     const auto store = use.first;
     if (store->opcode() == SpvOpFConvert) continue;
     if (spvOpcodeIsDebug(store->opcode())) continue;
+    if (store->IsNonSemantic()) continue;
     if (spvOpcodeIsDecoration(store->opcode())) continue;
     if (store->opcode() != SpvOpStore) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index 1a64605..070cc4c 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -61,8 +61,8 @@
 
 spv_result_t ValidateExtInstImport(ValidationState_t& _,
                                    const Instruction* inst) {
+  const auto name_id = 1;
   if (spvIsWebGPUEnv(_.context()->target_env)) {
-    const auto name_id = 1;
     const std::string name(reinterpret_cast<const char*>(
         inst->words().data() + inst->operands()[name_id].offset));
     if (name != "GLSL.std.450") {
@@ -72,6 +72,16 @@
     }
   }
 
+  if (!_.HasExtension(kSPV_KHR_non_semantic_info)) {
+    const std::string name(reinterpret_cast<const char*>(
+        inst->words().data() + inst->operands()[name_id].offset));
+    if (name.find("NonSemantic.") == 0) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "NonSemantic extended instruction sets cannot be declared "
+                "without SPV_KHR_non_semantic_info.";
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index b983194..f995ab3 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -87,7 +87,8 @@
   for (auto& pair : inst->uses()) {
     const auto* use = pair.first;
     if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) ==
-        acceptable.end()) {
+            acceptable.end() &&
+        !use->IsNonSemantic()) {
       return _.diag(SPV_ERROR_INVALID_ID, use)
              << "Invalid use of function result id " << _.getIdName(inst->id())
              << ".";
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp
index d80e1b8..7406330 100644
--- a/source/val/validate_id.cpp
+++ b/source/val/validate_id.cpp
@@ -167,7 +167,8 @@
           const auto opcode = inst->opcode();
           if (spvOpcodeGeneratesType(def->opcode()) &&
               !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
-              !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction &&
+              !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
+              opcode != SpvOpFunction &&
               opcode != SpvOpCooperativeMatrixLengthNV &&
               !(opcode == SpvOpSpecConstantOp &&
                 inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
@@ -175,7 +176,7 @@
                    << "Operand " << _.getIdName(operand_word)
                    << " cannot be a type";
           } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) &&
-                     !spvOpcodeIsDebug(opcode) &&
+                     !spvOpcodeIsDebug(opcode) && !inst->IsNonSemantic() &&
                      !spvOpcodeIsDecoration(opcode) &&
                      !spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi &&
                      opcode != SpvOpExtInst && opcode != SpvOpExtInstImport &&
@@ -187,6 +188,11 @@
             return _.diag(SPV_ERROR_INVALID_ID, inst)
                    << "Operand " << _.getIdName(operand_word)
                    << " requires a type";
+          } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << "Operand " << _.getIdName(operand_word)
+                   << " in semantic instruction cannot be a non-semantic "
+                      "instruction";
           } else {
             ret = SPV_SUCCESS;
           }
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index 53c2835..259befe 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -34,6 +34,30 @@
 // checked.
 spv_result_t ModuleScopedInstructions(ValidationState_t& _,
                                       const Instruction* inst, SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpExtInst:
+      if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
+        // non-semantic extinst opcodes are allowed beginning in the types
+        // section, but since they must name a return type they cannot be the
+        // first instruction in the types section. Therefore check that we are
+        // already in it.
+        if (_.current_layout_section() < kLayoutTypes) {
+          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                 << "Non-semantic OpExtInst must not appear before types "
+                 << "section";
+        }
+      } else {
+        // otherwise they must be used in a block
+        if (_.current_layout_section() < kLayoutFunctionDefinitions) {
+          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                 << spvOpcodeString(opcode) << " must appear in a block";
+        }
+      }
+      break;
+    default:
+      break;
+  }
+
   while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) {
     _.ProgressToNextLayoutSectionOrder();
 
@@ -144,6 +168,29 @@
         }
         break;
 
+      case SpvOpExtInst:
+        if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
+          // non-semantic extinst opcodes are allowed beginning in the types
+          // section, but must either be placed outside a function declaration,
+          // or inside a block.
+          if (_.current_layout_section() < kLayoutTypes) {
+            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                   << "Non-semantic OpExtInst must not appear before types "
+                   << "section";
+          } else if (_.in_function_body() && _.in_block() == false) {
+            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                   << "Non-semantic OpExtInst within function definition must "
+                      "appear in a block";
+          }
+        } else {
+          // otherwise they must be used in a block
+          if (_.in_block() == false) {
+            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                   << spvOpcodeString(opcode) << " must appear in a block";
+          }
+        }
+        break;
+
       default:
         if (_.current_layout_section() == kLayoutFunctionDeclarations &&
             _.in_function_body()) {
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 4d673b4..5924c69 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -19,29 +19,40 @@
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
+#include "spirv/unified1/spirv.h"
 
 namespace spvtools {
 namespace val {
 namespace {
 
-// True if the integer constant is > 0. |const_words| are words of the
-// constant-defining instruction (either OpConstant or
-// OpSpecConstant). typeWords are the words of the constant's-type-defining
-// OpTypeInt.
-bool AboveZero(const std::vector<uint32_t>& const_words,
-               const std::vector<uint32_t>& type_words) {
-  const uint32_t width = type_words[2];
-  const bool is_signed = type_words[3] > 0;
+// Returns, as an int64_t, the literal value from an OpConstant or the
+// default value of an OpSpecConstant, assuming it is an integral type.
+// For signed integers, relies the rule that literal value is sign extended
+// to fill out to word granularity.  Assumes that the constant value
+// has
+int64_t ConstantLiteralAsInt64(uint32_t width,
+                               const std::vector<uint32_t>& const_words) {
   const uint32_t lo_word = const_words[3];
-  if (width > 32) {
-    // The spec currently doesn't allow integers wider than 64 bits.
-    const uint32_t hi_word = const_words[4];  // Must exist, per spec.
-    if (is_signed && (hi_word >> 31)) return false;
-    return (lo_word | hi_word) > 0;
-  } else {
-    if (is_signed && (lo_word >> 31)) return false;
-    return lo_word > 0;
-  }
+  if (width <= 32) return int32_t(lo_word);
+  assert(width <= 64);
+  assert(const_words.size() > 4);
+  const uint32_t hi_word = const_words[4];  // Must exist, per spec.
+  return static_cast<int64_t>(uint64_t(lo_word) | uint64_t(hi_word) << 32);
+}
+
+// Returns, as an uint64_t, the literal value from an OpConstant or the
+// default value of an OpSpecConstant, assuming it is an integral type.
+// For signed integers, relies the rule that literal value is sign extended
+// to fill out to word granularity.  Assumes that the constant value
+// has
+int64_t ConstantLiteralAsUint64(uint32_t width,
+                                const std::vector<uint32_t>& const_words) {
+  const uint32_t lo_word = const_words[3];
+  if (width <= 32) return lo_word;
+  assert(width <= 64);
+  assert(const_words.size() > 4);
+  const uint32_t hi_word = const_words[4];  // Must exist, per spec.
+  return (uint64_t(lo_word) | uint64_t(hi_word) << 32);
 }
 
 // Validates that type declarations are unique, unless multiple declarations
@@ -258,14 +269,33 @@
 
   switch (length->opcode()) {
     case SpvOpSpecConstant:
-    case SpvOpConstant:
-      if (AboveZero(length->words(), const_result_type->words())) break;
-    // Else fall through!
-    case SpvOpConstantNull: {
+    case SpvOpConstant: {
+      auto& type_words = const_result_type->words();
+      const bool is_signed = type_words[3] > 0;
+      const uint32_t width = type_words[2];
+      const int64_t ivalue = ConstantLiteralAsInt64(width, length->words());
+      if (ivalue == 0 || (ivalue < 0 && is_signed)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "OpTypeArray Length <id> '" << _.getIdName(length_id)
+               << "' default value must be at least 1: found " << ivalue;
+      }
+      if (spvIsWebGPUEnv(_.context()->target_env)) {
+        // WebGPU has maximum integer width of 32 bits, and max array size
+        // is one more than the max signed integer representation.
+        const uint64_t max_permitted = (uint64_t(1) << 31);
+        const uint64_t uvalue = ConstantLiteralAsUint64(width, length->words());
+        if (uvalue > max_permitted) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "OpTypeArray Length <id> '" << _.getIdName(length_id)
+                 << "' size exceeds max value " << max_permitted
+                 << " permitted by WebGPU: got " << uvalue;
+        }
+      }
+    } break;
+    case SpvOpConstantNull:
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "OpTypeArray Length <id> '" << _.getIdName(length_id)
              << "' default value must be at least 1.";
-    }
     case SpvOpSpecConstantOp:
       // Assume it's OK, rather than try to evaluate the operation.
       break;
@@ -506,7 +536,7 @@
   for (auto& pair : inst->uses()) {
     const auto* use = pair.first;
     if (use->opcode() != SpvOpFunction && !spvOpcodeIsDebug(use->opcode()) &&
-        !spvOpcodeIsDecoration(use->opcode())) {
+        !use->IsNonSemantic() && !spvOpcodeIsDecoration(use->opcode())) {
       return _.diag(SPV_ERROR_INVALID_ID, use)
              << "Invalid use of function type result id "
              << _.getIdName(inst->id()) << ".";
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 20eaf88..51aebbe 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -93,6 +93,9 @@
         case SpvOpLine:
         case SpvOpNoLine:
         case SpvOpUndef:
+        // SpvOpExtInst is only allowed here for certain extended instruction
+        // sets. This will be checked separately
+        case SpvOpExtInst:
           out = true;
           break;
         default: break;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 3dca430..70999f9 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -100,8 +100,10 @@
   diagnostic_test.cpp
   enum_string_mapping_test.cpp
   enum_set_test.cpp
+  ext_inst.cldebug100_test.cpp
   ext_inst.debuginfo_test.cpp
   ext_inst.glsl_test.cpp
+  ext_inst.non_semantic_test.cpp
   ext_inst.opencl_test.cpp
   fix_word_test.cpp
   generator_magic_number_test.cpp
diff --git a/test/ext_inst.cldebug100_test.cpp b/test/ext_inst.cldebug100_test.cpp
new file mode 100644
index 0000000..4f1e106
--- /dev/null
+++ b/test/ext_inst.cldebug100_test.cpp
@@ -0,0 +1,1070 @@
+// Copyright (c) 2017-2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include "OpenCLDebugInfo100.h"
+#include "gmock/gmock.h"
+#include "source/util/string_utils.h"
+#include "spirv/unified1/spirv.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+
+// This file tests the correctness of encoding and decoding of instructions
+// involving the OpenCL.DebugInfo.100 extended instruction set.
+// Validation is not checked here.
+
+namespace spvtools {
+namespace {
+
+using spvtest::Concatenate;
+using spvtest::MakeInstruction;
+using testing::Eq;
+using utils::MakeVector;
+
+// Test values of enums vs. what is written in the spec.
+
+TEST(ExtInstCLDebugInfo, InstructionValues) {
+  EXPECT_EQ(0, OpenCLDebugInfo100DebugInfoNone);
+  EXPECT_EQ(1, OpenCLDebugInfo100DebugCompilationUnit);
+  EXPECT_EQ(2, OpenCLDebugInfo100DebugTypeBasic);
+  EXPECT_EQ(3, OpenCLDebugInfo100DebugTypePointer);
+  EXPECT_EQ(4, OpenCLDebugInfo100DebugTypeQualifier);
+  EXPECT_EQ(5, OpenCLDebugInfo100DebugTypeArray);
+  EXPECT_EQ(6, OpenCLDebugInfo100DebugTypeVector);
+  EXPECT_EQ(7, OpenCLDebugInfo100DebugTypedef);
+  EXPECT_EQ(8, OpenCLDebugInfo100DebugTypeFunction);
+  EXPECT_EQ(9, OpenCLDebugInfo100DebugTypeEnum);
+  EXPECT_EQ(10, OpenCLDebugInfo100DebugTypeComposite);
+  EXPECT_EQ(11, OpenCLDebugInfo100DebugTypeMember);
+  EXPECT_EQ(12, OpenCLDebugInfo100DebugTypeInheritance);
+  EXPECT_EQ(13, OpenCLDebugInfo100DebugTypePtrToMember);
+  EXPECT_EQ(14, OpenCLDebugInfo100DebugTypeTemplate);
+  EXPECT_EQ(15, OpenCLDebugInfo100DebugTypeTemplateParameter);
+  EXPECT_EQ(16, OpenCLDebugInfo100DebugTypeTemplateTemplateParameter);
+  EXPECT_EQ(17, OpenCLDebugInfo100DebugTypeTemplateParameterPack);
+  EXPECT_EQ(18, OpenCLDebugInfo100DebugGlobalVariable);
+  EXPECT_EQ(19, OpenCLDebugInfo100DebugFunctionDeclaration);
+  EXPECT_EQ(20, OpenCLDebugInfo100DebugFunction);
+  EXPECT_EQ(21, OpenCLDebugInfo100DebugLexicalBlock);
+  EXPECT_EQ(22, OpenCLDebugInfo100DebugLexicalBlockDiscriminator);
+  EXPECT_EQ(23, OpenCLDebugInfo100DebugScope);
+  EXPECT_EQ(24, OpenCLDebugInfo100DebugNoScope);
+  EXPECT_EQ(25, OpenCLDebugInfo100DebugInlinedAt);
+  EXPECT_EQ(26, OpenCLDebugInfo100DebugLocalVariable);
+  EXPECT_EQ(27, OpenCLDebugInfo100DebugInlinedVariable);
+  EXPECT_EQ(28, OpenCLDebugInfo100DebugDeclare);
+  EXPECT_EQ(29, OpenCLDebugInfo100DebugValue);
+  EXPECT_EQ(30, OpenCLDebugInfo100DebugOperation);
+  EXPECT_EQ(31, OpenCLDebugInfo100DebugExpression);
+  EXPECT_EQ(32, OpenCLDebugInfo100DebugMacroDef);
+  EXPECT_EQ(33, OpenCLDebugInfo100DebugMacroUndef);
+  EXPECT_EQ(34, OpenCLDebugInfo100DebugImportedEntity);
+  EXPECT_EQ(35, OpenCLDebugInfo100DebugSource);
+}
+
+TEST(ExtInstCLDebugInfo, InfoFlagValues) {
+  EXPECT_EQ(1 << 0, OpenCLDebugInfo100FlagIsProtected);
+  EXPECT_EQ(1 << 1, OpenCLDebugInfo100FlagIsPrivate);
+  EXPECT_EQ(((1 << 0) | (1 << 1)), OpenCLDebugInfo100FlagIsPublic);
+  EXPECT_EQ(1 << 2, OpenCLDebugInfo100FlagIsLocal);
+  EXPECT_EQ(1 << 3, OpenCLDebugInfo100FlagIsDefinition);
+  EXPECT_EQ(1 << 4, OpenCLDebugInfo100FlagFwdDecl);
+  EXPECT_EQ(1 << 5, OpenCLDebugInfo100FlagArtificial);
+  EXPECT_EQ(1 << 6, OpenCLDebugInfo100FlagExplicit);
+  EXPECT_EQ(1 << 7, OpenCLDebugInfo100FlagPrototyped);
+  EXPECT_EQ(1 << 8, OpenCLDebugInfo100FlagObjectPointer);
+  EXPECT_EQ(1 << 9, OpenCLDebugInfo100FlagStaticMember);
+  EXPECT_EQ(1 << 10, OpenCLDebugInfo100FlagIndirectVariable);
+  EXPECT_EQ(1 << 11, OpenCLDebugInfo100FlagLValueReference);
+  EXPECT_EQ(1 << 12, OpenCLDebugInfo100FlagRValueReference);
+  EXPECT_EQ(1 << 13, OpenCLDebugInfo100FlagIsOptimized);
+  EXPECT_EQ(1 << 14, OpenCLDebugInfo100FlagIsEnumClass);
+  EXPECT_EQ(1 << 15, OpenCLDebugInfo100FlagTypePassByValue);
+  EXPECT_EQ(1 << 16, OpenCLDebugInfo100FlagTypePassByReference);
+}
+
+TEST(ExtInstCLDebugInfo, BaseTypeAttributeEndodingValues) {
+  EXPECT_EQ(0, OpenCLDebugInfo100Unspecified);
+  EXPECT_EQ(1, OpenCLDebugInfo100Address);
+  EXPECT_EQ(2, OpenCLDebugInfo100Boolean);
+  EXPECT_EQ(3, OpenCLDebugInfo100Float);
+  EXPECT_EQ(4, OpenCLDebugInfo100Signed);
+  EXPECT_EQ(5, OpenCLDebugInfo100SignedChar);
+  EXPECT_EQ(6, OpenCLDebugInfo100Unsigned);
+  EXPECT_EQ(7, OpenCLDebugInfo100UnsignedChar);
+}
+
+TEST(ExtInstCLDebugInfo, CompositeTypeValues) {
+  EXPECT_EQ(0, OpenCLDebugInfo100Class);
+  EXPECT_EQ(1, OpenCLDebugInfo100Structure);
+  EXPECT_EQ(2, OpenCLDebugInfo100Union);
+}
+
+TEST(ExtInstCLDebugInfo, TypeQualifierValues) {
+  EXPECT_EQ(0, OpenCLDebugInfo100ConstType);
+  EXPECT_EQ(1, OpenCLDebugInfo100VolatileType);
+  EXPECT_EQ(2, OpenCLDebugInfo100RestrictType);
+  EXPECT_EQ(3, OpenCLDebugInfo100AtomicType);
+}
+
+TEST(ExtInstCLDebugInfo, DebugOperationValues) {
+  EXPECT_EQ(0, OpenCLDebugInfo100Deref);
+  EXPECT_EQ(1, OpenCLDebugInfo100Plus);
+  EXPECT_EQ(2, OpenCLDebugInfo100Minus);
+  EXPECT_EQ(3, OpenCLDebugInfo100PlusUconst);
+  EXPECT_EQ(4, OpenCLDebugInfo100BitPiece);
+  EXPECT_EQ(5, OpenCLDebugInfo100Swap);
+  EXPECT_EQ(6, OpenCLDebugInfo100Xderef);
+  EXPECT_EQ(7, OpenCLDebugInfo100StackValue);
+  EXPECT_EQ(8, OpenCLDebugInfo100Constu);
+  EXPECT_EQ(9, OpenCLDebugInfo100Fragment);
+}
+
+TEST(ExtInstCLDebugInfo, ImportedEntityValues) {
+  EXPECT_EQ(0, OpenCLDebugInfo100ImportedModule);
+  EXPECT_EQ(1, OpenCLDebugInfo100ImportedDeclaration);
+}
+
+// Test round trip through assembler and disassembler.
+
+struct InstructionCase {
+  uint32_t opcode;
+  std::string name;
+  std::string operands;
+  std::vector<uint32_t> expected_operands;
+};
+
+using ExtInstCLDebugInfo100RoundTripTest =
+    spvtest::TextToBinaryTestBase<::testing::TestWithParam<InstructionCase>>;
+using ExtInstCLDebugInfo100RoundTripTestExplicit = spvtest::TextToBinaryTest;
+
+TEST_P(ExtInstCLDebugInfo100RoundTripTest, ParameterizedExtInst) {
+  const std::string input =
+      "%1 = OpExtInstImport \"OpenCL.DebugInfo.100\"\n"
+      "%3 = OpExtInst %2 %1 " +
+      GetParam().name + GetParam().operands + "\n";
+  // First make sure it assembles correctly.
+  std::cout << input << std::endl;
+  EXPECT_THAT(CompiledInstructions(input),
+              Eq(Concatenate(
+                  {MakeInstruction(SpvOpExtInstImport, {1},
+                                   MakeVector("OpenCL.DebugInfo.100")),
+                   MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode},
+                                   GetParam().expected_operands)})))
+      << input;
+  // Now check the round trip through the disassembler.
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
+}
+
+#define EPREFIX "Debug"
+
+#define CASE_0(Enum)                                               \
+  {                                                                \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, "", {} \
+  }
+
+#define CASE_ILL(Enum, L0, L1)                              \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 " #L0 " " #L1, {                               \
+      4, L0, L1                                             \
+    }                                                       \
+  }
+
+#define CASE_IL(Enum, L0)                                                 \
+  {                                                                       \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #L0, { \
+      4, L0                                                               \
+    }                                                                     \
+  }
+
+#define CASE_I(Enum)                                                     \
+  {                                                                      \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4", { 4 } \
+  }
+
+#define CASE_II(Enum)                                                          \
+  {                                                                            \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5", { 4, 5 } \
+  }
+
+#define CASE_III(Enum)                                                     \
+  {                                                                        \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6", { \
+      4, 5, 6                                                              \
+    }                                                                      \
+  }
+
+#define CASE_IIII(Enum)                                                       \
+  {                                                                           \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6 %7", { \
+      4, 5, 6, 7                                                              \
+    }                                                                         \
+  }
+
+#define CASE_IIIII(Enum)                                                       \
+  {                                                                            \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6 %7 %8", \
+    {                                                                          \
+      4, 5, 6, 7, 8                                                            \
+    }                                                                          \
+  }
+
+#define CASE_IIIIII(Enum)                                   \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 %7 %8 %9", {                             \
+      4, 5, 6, 7, 8, 9                                      \
+    }                                                       \
+  }
+
+#define CASE_IIIIIII(Enum)                                  \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 %7 %8 %9 %10", {                         \
+      4, 5, 6, 7, 8, 9, 10                                  \
+    }                                                       \
+  }
+
+#define CASE_IIILLI(Enum, L0, L1)                           \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 " #L0 " " #L1 " %7", {                   \
+      4, 5, 6, L0, L1, 7                                    \
+    }                                                       \
+  }
+
+#define CASE_IIILLIF(Enum, L0, L1, Fstr, Fnum)              \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 " #L0 " " #L1 " %7 " Fstr, {             \
+      4, 5, 6, L0, L1, 7, Fnum                              \
+    }                                                       \
+  }
+
+#define CASE_IIILLIFL(Enum, L0, L1, Fstr, Fnum, L2)         \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 " #L0 " " #L1 " %7 " Fstr " " #L2, {     \
+      4, 5, 6, L0, L1, 7, Fnum, L2                          \
+    }                                                       \
+  }
+
+#define CASE_IIILLIL(Enum, L0, L1, L2)                      \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 " #L0 " " #L1 " %7 " #L2, {              \
+      4, 5, 6, L0, L1, 7, L2                                \
+    }                                                       \
+  }
+
+#define CASE_IE(Enum, E0)                                                 \
+  {                                                                       \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #E0, { \
+      4, uint32_t(OpenCLDebugInfo100##E0)                                 \
+    }                                                                     \
+  }
+
+#define CASE_IEIILLI(Enum, E0, L1, L2)                      \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 " #E0 " %5 %6 " #L1 " " #L2 " %7", {           \
+      4, uint32_t(OpenCLDebugInfo100##E0), 5, 6, L1, L2, 7  \
+    }                                                       \
+  }
+
+#define CASE_IIE(Enum, E0)                                                   \
+  {                                                                          \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 " #E0, { \
+      4, 5, uint32_t(OpenCLDebugInfo100##E0)                                 \
+    }                                                                        \
+  }
+
+#define CASE_ISF(Enum, S0, Fstr, Fnum)                      \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 " #S0 " " Fstr, {                              \
+      4, uint32_t(SpvStorageClass##S0), Fnum                \
+    }                                                       \
+  }
+
+#define CASE_LII(Enum, L0)                                                    \
+  {                                                                           \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #L0 " %4 %5", \
+    {                                                                         \
+      L0, 4, 5                                                                \
+    }                                                                         \
+  }
+
+#define CASE_LLIe(Enum, L0, L1, RawEnumName, RawEnumValue)  \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " " #L0 " " #L1 " %4 " RawEnumName, {               \
+      L0, L1, 4, RawEnumValue                               \
+    }                                                       \
+  }
+
+#define CASE_ILI(Enum, L0)                                                    \
+  {                                                                           \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #L0 " %5", \
+    {                                                                         \
+      4, L0, 5                                                                \
+    }                                                                         \
+  }
+
+#define CASE_ILII(Enum, L0)                                 \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 " #L0 " %5 %6", {                              \
+      4, L0, 5, 6                                           \
+    }                                                       \
+  }
+
+#define CASE_ILLII(Enum, L0, L1)                            \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 " #L0 " " #L1 " %5 %6", {                      \
+      4, L0, L1, 5, 6                                       \
+    }                                                       \
+  }
+
+#define CASE_IIILLIIF(Enum, L0, L1, Fstr, Fnum)             \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr, {          \
+      4, 5, 6, L0, L1, 7, 8, Fnum                           \
+    }                                                       \
+  }
+
+#define CASE_IIILLIIFII(Enum, L0, L1, Fstr, Fnum)            \
+  {                                                          \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,  \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10                     \
+    }                                                        \
+  }
+
+#define CASE_IIILLIIFIIII(Enum, L0, L1, Fstr, Fnum)                  \
+  {                                                                  \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,          \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12                     \
+    }                                                                \
+  }
+
+#define CASE_IIILLIIFIIIIII(Enum, L0, L1, Fstr, Fnum)                        \
+  {                                                                          \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,                  \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12 %13 %14", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12, 13, 14                     \
+    }                                                                        \
+  }
+
+#define CASE_IEILLIIIF(Enum, E0, L0, L1, Fstr, Fnum)                \
+  {                                                                 \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,         \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr, {          \
+      4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum \
+    }                                                               \
+  }
+
+#define CASE_IEILLIIIFI(Enum, E0, L0, L1, Fstr, Fnum)                  \
+  {                                                                    \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,            \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9", {       \
+      4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9 \
+    }                                                                  \
+  }
+
+#define CASE_IEILLIIIFII(Enum, E0, L0, L1, Fstr, Fnum)                     \
+  {                                                                        \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,                \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10", {       \
+      4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10 \
+    }                                                                      \
+  }
+
+#define CASE_IEILLIIIFIII(Enum, E0, L0, L1, Fstr, Fnum)                        \
+  {                                                                            \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,                    \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10 %11", {       \
+      4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10, 11 \
+    }                                                                          \
+  }
+
+#define CASE_IEILLIIIFIIII(Enum, E0, L0, L1, Fstr, Fnum)                     \
+  {                                                                          \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,                  \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10 %11 %12", { \
+      4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10,  \
+          11, 12                                                             \
+    }                                                                        \
+  }
+
+#define CASE_IIILLIIIF(Enum, L0, L1, Fstr, Fnum)            \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr, {       \
+      4, 5, 6, L0, L1, 7, 8, 9, Fnum                        \
+    }                                                       \
+  }
+
+#define CASE_IIILLIIIFI(Enum, L0, L1, Fstr, Fnum)            \
+  {                                                          \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,  \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr " %10", { \
+      4, 5, 6, L0, L1, 7, 8, 9, Fnum, 10                     \
+    }                                                        \
+  }
+
+#define CASE_IIIIF(Enum, Fstr, Fnum)                        \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 %7 " Fstr, {                             \
+      4, 5, 6, 7, Fnum                                      \
+    }                                                       \
+  }
+
+#define CASE_IIILL(Enum, L0, L1)                            \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 " #L0 " " #L1, {                         \
+      4, 5, 6, L0, L1                                       \
+    }                                                       \
+  }
+
+#define CASE_IIIILL(Enum, L0, L1)                           \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 %6 %7 " #L0 " " #L1, {                      \
+      4, 5, 6, 7, L0, L1                                    \
+    }                                                       \
+  }
+
+#define CASE_IILLI(Enum, L0, L1)                            \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 " #L0 " " #L1 " %6", {                      \
+      4, 5, L0, L1, 6                                       \
+    }                                                       \
+  }
+
+#define CASE_IILLII(Enum, L0, L1)                           \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 " #L0 " " #L1 " %6 %7", {                   \
+      4, 5, L0, L1, 6, 7                                    \
+    }                                                       \
+  }
+
+#define CASE_IILLIII(Enum, L0, L1)                          \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 " #L0 " " #L1 " %6 %7 %8", {                \
+      4, 5, L0, L1, 6, 7, 8                                 \
+    }                                                       \
+  }
+
+#define CASE_IILLIIII(Enum, L0, L1)                         \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " %4 %5 " #L0 " " #L1 " %6 %7 %8 %9", {             \
+      4, 5, L0, L1, 6, 7, 8, 9                              \
+    }                                                       \
+  }
+
+#define CASE_IIILLIIFLI(Enum, L0, L1, Fstr, Fnum, L2)            \
+  {                                                              \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,      \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9                         \
+    }                                                            \
+  }
+
+#define CASE_IIILLIIFLII(Enum, L0, L1, Fstr, Fnum, L2)               \
+  {                                                                  \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum,          \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9 %10", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9, 10                         \
+    }                                                                \
+  }
+
+#define CASE_E(Enum, E0)                                               \
+  {                                                                    \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0, { \
+      uint32_t(OpenCLDebugInfo100##E0)                                 \
+    }                                                                  \
+  }
+
+#define CASE_EI(Enum, E0)                                                    \
+  {                                                                          \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " %4", { \
+      uint32_t(OpenCLDebugInfo100##E0), 4                                    \
+    }                                                                        \
+  }
+
+#define CASE_EII(Enum, E0)                                                    \
+  {                                                                           \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " %4 %5", \
+    {                                                                         \
+      uint32_t(OpenCLDebugInfo100##E0), 4, 5                                  \
+    }                                                                         \
+  }
+
+#define CASE_EIII(Enum, E0)                                 \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " " #E0 " %4 %5 %6", {                              \
+      uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6             \
+    }                                                       \
+  }
+
+#define CASE_EIIII(Enum, E0)                                \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " " #E0 " %4 %5 %6 %7", {                           \
+      uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6, 7          \
+    }                                                       \
+  }
+
+#define CASE_EIIIII(Enum, E0)                               \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " " #E0 " %4 %5 %6 %7 %8", {                        \
+      uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6, 7, 8       \
+    }                                                       \
+  }
+
+#define CASE_EL(Enum, E0, L0)                                                  \
+  {                                                                            \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " " #L0, { \
+      uint32_t(OpenCLDebugInfo100##E0), L0                                     \
+    }                                                                          \
+  }
+
+#define CASE_ELL(Enum, E0, L0, L1)                          \
+  {                                                         \
+    uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \
+        " " #E0 " " #L0 " " #L1, {                          \
+      uint32_t(OpenCLDebugInfo100##E0), L0, L1              \
+    }                                                       \
+  }
+
+// OpenCL.DebugInfo.100 4.1 Missing Debugging Information
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInfoNone,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_0(InfoNone),  // enum value 0
+                         })));
+
+// OpenCL.DebugInfo.100 4.2 Compilation Unit
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugCompilationUnit, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_LLIe(CompilationUnit, 100, 42, "HLSL", SpvSourceLanguageHLSL),
+    })));
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugSource, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        // TODO(dneto): Should this be a list of sourc texts,
+        // to accomodate length limits?
+        CASE_I(Source),
+        CASE_II(Source),
+    })));
+
+// OpenCL.DebugInfo.100 4.3 Type instructions
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeBasic,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_IIE(TypeBasic, Unspecified),
+                             CASE_IIE(TypeBasic, Address),
+                             CASE_IIE(TypeBasic, Boolean),
+                             CASE_IIE(TypeBasic, Float),
+                             CASE_IIE(TypeBasic, Signed),
+                             CASE_IIE(TypeBasic, SignedChar),
+                             CASE_IIE(TypeBasic, Unsigned),
+                             CASE_IIE(TypeBasic, UnsignedChar),
+                         })));
+
+// The FlagIsPublic is value is (1 << 0) | (1 << 2) which is the same
+// as the bitwise-OR of FlagIsProtected and FlagIsPrivate.
+// The disassembler will emit the compound expression instead.
+// There is no simple fix for this.  This enum is not really a mask
+// for the bottom two bits.
+TEST_F(ExtInstCLDebugInfo100RoundTripTestExplicit, FlagIsPublic) {
+  const std::string prefix =
+      "%1 = OpExtInstImport \"DebugInfo\"\n"
+      "%3 = OpExtInst %2 %1 DebugTypePointer %4 Private ";
+  const std::string input = prefix + "FlagIsPublic\n";
+  const std::string expected = prefix + "FlagIsProtected|FlagIsPrivate\n";
+  // First make sure it assembles correctly.
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(Concatenate(
+          {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")),
+           MakeInstruction(SpvOpExtInst,
+                           {2, 3, 1, OpenCLDebugInfo100DebugTypePointer, 4,
+                            uint32_t(SpvStorageClassPrivate),
+                            OpenCLDebugInfo100FlagIsPublic})})))
+      << input;
+  // Now check the round trip through the disassembler.
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(expected)) << input;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugTypePointer, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+
+        //// Use each flag independently.
+        CASE_ISF(TypePointer, Private, "FlagIsProtected",
+                 uint32_t(OpenCLDebugInfo100FlagIsProtected)),
+        CASE_ISF(TypePointer, Private, "FlagIsPrivate",
+                 uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+
+        // FlagIsPublic is tested above.
+
+        CASE_ISF(TypePointer, Private, "FlagIsLocal",
+                 uint32_t(OpenCLDebugInfo100FlagIsLocal)),
+        CASE_ISF(TypePointer, Private, "FlagIsDefinition",
+                 uint32_t(OpenCLDebugInfo100FlagIsDefinition)),
+        CASE_ISF(TypePointer, Private, "FlagFwdDecl",
+                 uint32_t(OpenCLDebugInfo100FlagFwdDecl)),
+        CASE_ISF(TypePointer, Private, "FlagArtificial",
+                 uint32_t(OpenCLDebugInfo100FlagArtificial)),
+        CASE_ISF(TypePointer, Private, "FlagExplicit",
+                 uint32_t(OpenCLDebugInfo100FlagExplicit)),
+        CASE_ISF(TypePointer, Private, "FlagPrototyped",
+                 uint32_t(OpenCLDebugInfo100FlagPrototyped)),
+        CASE_ISF(TypePointer, Private, "FlagObjectPointer",
+                 uint32_t(OpenCLDebugInfo100FlagObjectPointer)),
+        CASE_ISF(TypePointer, Private, "FlagStaticMember",
+                 uint32_t(OpenCLDebugInfo100FlagStaticMember)),
+        CASE_ISF(TypePointer, Private, "FlagIndirectVariable",
+                 uint32_t(OpenCLDebugInfo100FlagIndirectVariable)),
+        CASE_ISF(TypePointer, Private, "FlagLValueReference",
+                 uint32_t(OpenCLDebugInfo100FlagLValueReference)),
+        CASE_ISF(TypePointer, Private, "FlagIsOptimized",
+                 uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+        CASE_ISF(TypePointer, Private, "FlagIsEnumClass",
+                 uint32_t(OpenCLDebugInfo100FlagIsEnumClass)),
+        CASE_ISF(TypePointer, Private, "FlagTypePassByValue",
+                 uint32_t(OpenCLDebugInfo100FlagTypePassByValue)),
+        CASE_ISF(TypePointer, Private, "FlagTypePassByReference",
+                 uint32_t(OpenCLDebugInfo100FlagTypePassByReference)),
+
+        //// Use flags in combination, and try different storage classes.
+        CASE_ISF(TypePointer, Function, "FlagIsProtected|FlagIsPrivate",
+                 uint32_t(OpenCLDebugInfo100FlagIsProtected) |
+                     uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+        CASE_ISF(
+            TypePointer, Workgroup,
+            "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
+            uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+                uint32_t(OpenCLDebugInfo100FlagFwdDecl) |
+                uint32_t(OpenCLDebugInfo100FlagIndirectVariable) |
+                uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+
+    })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeQualifier,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_IE(TypeQualifier, ConstType),
+                             CASE_IE(TypeQualifier, VolatileType),
+                             CASE_IE(TypeQualifier, RestrictType),
+                             CASE_IE(TypeQualifier, AtomicType),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeArray,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_II(TypeArray),
+                             CASE_III(TypeArray),
+                             CASE_IIII(TypeArray),
+                             CASE_IIIII(TypeArray),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeVector,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_IL(TypeVector, 2),
+                             CASE_IL(TypeVector, 3),
+                             CASE_IL(TypeVector, 4),
+                             CASE_IL(TypeVector, 16),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypedef,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_IIILLI(Typedef, 12, 13),
+                             CASE_IIILLI(Typedef, 14, 99),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeFunction,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_EI(TypeFunction, FlagIsProtected),
+                             CASE_EII(TypeFunction, FlagIsDefinition),
+                             CASE_EIII(TypeFunction, FlagArtificial),
+                             CASE_EIIII(TypeFunction, FlagExplicit),
+                             CASE_EIIIII(TypeFunction, FlagIsPrivate),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugTypeEnum, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIIFII(
+            TypeEnum, 12, 13,
+            "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
+            uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+                uint32_t(OpenCLDebugInfo100FlagFwdDecl) |
+                uint32_t(OpenCLDebugInfo100FlagIndirectVariable) |
+                uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+        CASE_IIILLIIFIIII(TypeEnum, 17, 18, "FlagStaticMember",
+                          uint32_t(OpenCLDebugInfo100FlagStaticMember)),
+        CASE_IIILLIIFIIIIII(TypeEnum, 99, 1, "FlagStaticMember",
+                            uint32_t(OpenCLDebugInfo100FlagStaticMember)),
+    })));
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugTypeComposite, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IEILLIIIF(
+            TypeComposite, Class, 12, 13,
+            "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
+            uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+                uint32_t(OpenCLDebugInfo100FlagFwdDecl) |
+                uint32_t(OpenCLDebugInfo100FlagIndirectVariable) |
+                uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+        // Cover all tag values: Class, Structure, Union
+        CASE_IEILLIIIF(TypeComposite, Class, 12, 13, "FlagIsPrivate",
+                       uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+        CASE_IEILLIIIF(TypeComposite, Structure, 12, 13, "FlagIsPrivate",
+                       uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+        CASE_IEILLIIIF(TypeComposite, Union, 12, 13, "FlagIsPrivate",
+                       uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+        // Now add members
+        CASE_IEILLIIIFI(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+                        uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+        CASE_IEILLIIIFII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+                         uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+        CASE_IEILLIIIFIII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+                          uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+        CASE_IEILLIIIFIIII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+                           uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+    })));
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugTypeMember, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIIIF(TypeMember, 12, 13, "FlagIsPrivate",
+                       uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+        CASE_IIILLIIIF(TypeMember, 99, 100, "FlagIsPrivate|FlagFwdDecl",
+                       uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+                           uint32_t(OpenCLDebugInfo100FlagFwdDecl)),
+        // Add the optional Id argument.
+        CASE_IIILLIIIFI(TypeMember, 12, 13, "FlagIsPrivate",
+                        uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+    })));
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugTypeInheritance, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIIIF(TypeInheritance, "FlagIsPrivate",
+                   uint32_t(OpenCLDebugInfo100FlagIsPrivate)),
+        CASE_IIIIF(TypeInheritance, "FlagIsPrivate|FlagFwdDecl",
+                   uint32_t(OpenCLDebugInfo100FlagIsPrivate) |
+                       uint32_t(OpenCLDebugInfo100FlagFwdDecl)),
+    })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypePtrToMember,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_II(TypePtrToMember),
+                         })));
+
+// OpenCL.DebugInfo.100 4.4 Templates
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplate,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_II(TypeTemplate),
+                             CASE_III(TypeTemplate),
+                             CASE_IIII(TypeTemplate),
+                             CASE_IIIII(TypeTemplate),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateParameter,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_IIIILL(TypeTemplateParameter, 1, 2),
+                             CASE_IIIILL(TypeTemplateParameter, 99, 102),
+                             CASE_IIIILL(TypeTemplateParameter, 10, 7),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateTemplateParameter,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_IIILL(TypeTemplateTemplateParameter, 1, 2),
+                             CASE_IIILL(TypeTemplateTemplateParameter, 99, 102),
+                             CASE_IIILL(TypeTemplateTemplateParameter, 10, 7),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateParameterPack,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_IILLI(TypeTemplateParameterPack, 1, 2),
+                             CASE_IILLII(TypeTemplateParameterPack, 99, 102),
+                             CASE_IILLIII(TypeTemplateParameterPack, 10, 7),
+                             CASE_IILLIIII(TypeTemplateParameterPack, 10, 7),
+                         })));
+
+// OpenCL.DebugInfo.100 4.5 Global Variables
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugGlobalVariable, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIIIF(GlobalVariable, 1, 2, "FlagIsOptimized",
+                       uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+        CASE_IIILLIIIF(GlobalVariable, 42, 43, "FlagIsOptimized",
+                       uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+        CASE_IIILLIIIFI(GlobalVariable, 1, 2, "FlagIsOptimized",
+                        uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+        CASE_IIILLIIIFI(GlobalVariable, 42, 43, "FlagIsOptimized",
+                        uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+    })));
+
+// OpenCL.DebugInfo.100 4.6 Functions
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugFunctionDeclaration,
+    ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIIF(FunctionDeclaration, 1, 2, "FlagIsOptimized",
+                      uint32_t(OpenCLDebugInfo100FlagIsOptimized)),
+        CASE_IIILLIIF(FunctionDeclaration, 42, 43, "FlagFwdDecl",
+                      uint32_t(OpenCLDebugInfo100FlagFwdDecl)),
+    })));
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugFunction, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIIFLI(Function, 1, 2, "FlagIsOptimized",
+                        uint32_t(OpenCLDebugInfo100FlagIsOptimized), 3),
+        CASE_IIILLIIFLI(Function, 42, 43, "FlagFwdDecl",
+                        uint32_t(OpenCLDebugInfo100FlagFwdDecl), 44),
+        // Add the optional declaration Id.
+        CASE_IIILLIIFLII(Function, 1, 2, "FlagIsOptimized",
+                         uint32_t(OpenCLDebugInfo100FlagIsOptimized), 3),
+        CASE_IIILLIIFLII(Function, 42, 43, "FlagFwdDecl",
+                         uint32_t(OpenCLDebugInfo100FlagFwdDecl), 44),
+    })));
+
+// OpenCL.DebugInfo.100 4.7 Local Information
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugLexicalBlock,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_ILLII(LexicalBlock, 1, 2),
+                             CASE_ILLII(LexicalBlock, 42, 43),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugLexicalBlockDiscriminator,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_ILI(LexicalBlockDiscriminator, 1),
+                             CASE_ILI(LexicalBlockDiscriminator, 42),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugScope,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_I(Scope),
+                             CASE_II(Scope),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugNoScope,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_0(NoScope),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInlinedAt,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_LII(InlinedAt, 1),
+                             CASE_LII(InlinedAt, 42),
+                         })));
+
+// OpenCL.DebugInfo.100 4.8 Local Variables
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugLocalVariable, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIF(LocalVariable, 1, 2, "FlagIsPrivate",
+                     OpenCLDebugInfo100FlagIsPrivate),
+        CASE_IIILLIF(LocalVariable, 4, 5, "FlagIsProtected",
+                     OpenCLDebugInfo100FlagIsProtected),
+        CASE_IIILLIFL(LocalVariable, 9, 99, "FlagIsProtected",
+                      OpenCLDebugInfo100FlagIsProtected, 195),
+        CASE_IIILLIFL(LocalVariable, 19, 199, "FlagIsPrivate",
+                      OpenCLDebugInfo100FlagIsPrivate, 195),
+    })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInlinedVariable,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_II(InlinedVariable),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugDeclare,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_III(Declare),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugDebugValue, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIII(Value),
+        CASE_IIIII(Value),
+        CASE_IIIIII(Value),
+        // Test up to 3 id parameters. We can always try more.
+        CASE_IIIIIII(Value),
+    })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugOperation,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_E(Operation, Deref),
+                             CASE_E(Operation, Plus),
+                             CASE_E(Operation, Minus),
+                             CASE_EL(Operation, PlusUconst, 1),
+                             CASE_EL(Operation, PlusUconst, 42),
+                             CASE_ELL(Operation, BitPiece, 1, 2),
+                             CASE_ELL(Operation, BitPiece, 4, 5),
+                             CASE_E(Operation, Swap),
+                             CASE_E(Operation, Xderef),
+                             CASE_E(Operation, StackValue),
+                             CASE_EL(Operation, Constu, 1),
+                             CASE_EL(Operation, Constu, 42),
+                             CASE_ELL(Operation, Fragment, 100, 200),
+                             CASE_ELL(Operation, Fragment, 8, 9),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugExpression,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_0(Expression),
+                             CASE_I(Expression),
+                             CASE_II(Expression),
+                             CASE_III(Expression),
+                             CASE_IIII(Expression),
+                             CASE_IIIII(Expression),
+                             CASE_IIIIII(Expression),
+                             CASE_IIIIIII(Expression),
+                         })));
+
+// OpenCL.DebugInfo.100 4.9 Macros
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugMacroDef,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_ILI(MacroDef, 1),
+                             CASE_ILI(MacroDef, 42),
+                             CASE_ILII(MacroDef, 1),
+                             CASE_ILII(MacroDef, 42),
+                         })));
+
+INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugMacroUndef,
+                         ExtInstCLDebugInfo100RoundTripTest,
+                         ::testing::ValuesIn(std::vector<InstructionCase>({
+                             CASE_ILI(MacroUndef, 1),
+                             CASE_ILI(MacroUndef, 42),
+                         })));
+
+// OpenCL.DebugInfo.100 4.10 Imported Entities
+
+INSTANTIATE_TEST_SUITE_P(
+    OpenCLDebugInfo100DebugImportedEntity, ExtInstCLDebugInfo100RoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        // ID Name
+        // Literal Tag
+        // ID Source
+        // ID Entity
+        // Literal Number Line
+        // Literal Number Column
+        // ID Parent
+        CASE_IEIILLI(ImportedEntity, ImportedModule, 67, 68),
+        CASE_IEIILLI(ImportedEntity, ImportedDeclaration, 42, 43),
+    })));
+
+#undef EPREFIX
+#undef CASE_0
+#undef CASE_ILL
+#undef CASE_IL
+#undef CASE_I
+#undef CASE_II
+#undef CASE_III
+#undef CASE_IIII
+#undef CASE_IIIII
+#undef CASE_IIIIII
+#undef CASE_IIIIIII
+#undef CASE_IIILLI
+#undef CASE_IIILLIL
+#undef CASE_IE
+#undef CASE_IEIILLI
+#undef CASE_IIE
+#undef CASE_ISF
+#undef CASE_LII
+#undef CASE_LLIe
+#undef CASE_ILI
+#undef CASE_ILII
+#undef CASE_ILLII
+#undef CASE_IIILLIF
+#undef CASE_IIILLIFL
+#undef CASE_IIILLIIF
+#undef CASE_IIILLIIFII
+#undef CASE_IIILLIIFIIII
+#undef CASE_IIILLIIFIIIIII
+#undef CASE_IEILLIIIF
+#undef CASE_IEILLIIIFI
+#undef CASE_IEILLIIIFII
+#undef CASE_IEILLIIIFIII
+#undef CASE_IEILLIIIFIIII
+#undef CASE_IIILLIIIF
+#undef CASE_IIILLIIIFI
+#undef CASE_IIIIF
+#undef CASE_IIILL
+#undef CASE_IIIILL
+#undef CASE_IILLI
+#undef CASE_IILLII
+#undef CASE_IILLIII
+#undef CASE_IILLIIII
+#undef CASE_IIILLIIFLI
+#undef CASE_IIILLIIFLII
+#undef CASE_E
+#undef CASE_EI
+#undef CASE_EII
+#undef CASE_EIII
+#undef CASE_EIIII
+#undef CASE_EIIIII
+#undef CASE_EL
+#undef CASE_ELL
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/ext_inst.non_semantic_test.cpp b/test/ext_inst.non_semantic_test.cpp
new file mode 100644
index 0000000..870684e
--- /dev/null
+++ b/test/ext_inst.non_semantic_test.cpp
@@ -0,0 +1,90 @@
+// Copyright (c) 2015-2016 The Khronos Group Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Assembler tests for non-semantic extended instructions
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+
+using ::testing::Eq;
+
+namespace spvtools {
+namespace {
+
+using NonSemanticRoundTripTest = RoundTripTest;
+using NonSemanticTextToBinaryTest = spvtest::TextToBinaryTest;
+
+TEST_F(NonSemanticRoundTripTest, NonSemanticInsts) {
+  std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
+%2 = OpTypeVoid
+%3 = OpExtInst %2 %1 132384681 %2
+%4 = OpTypeInt 32 0
+%5 = OpConstant %4 123
+%6 = OpString "Test string"
+%7 = OpExtInst %4 %1 82198732 %5 %6
+%8 = OpExtInstImport "NonSemantic.Testing.AnotherUnknownExtInstSet"
+%9 = OpExtInst %4 %8 613874321 %7 %5 %6
+)";
+  std::string disassembly = EncodeAndDecodeSuccessfully(
+      spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_0);
+  EXPECT_THAT(disassembly, Eq(spirv));
+}
+
+TEST_F(NonSemanticTextToBinaryTest, InvalidExtInstSetName) {
+  std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic_Testing_ExtInst"
+)";
+
+  EXPECT_THAT(
+      CompileFailure(spirv),
+      Eq("Invalid extended instruction import 'NonSemantic_Testing_ExtInst'"));
+}
+
+TEST_F(NonSemanticTextToBinaryTest, NonSemanticIntParameter) {
+  std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
+%2 = OpTypeVoid
+%3 = OpExtInst %2 %1 1 99999
+)";
+
+  EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %."));
+}
+
+TEST_F(NonSemanticTextToBinaryTest, NonSemanticFloatParameter) {
+  std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
+%2 = OpTypeVoid
+%3 = OpExtInst %2 %1 1 3.141592
+)";
+
+  EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %."));
+}
+
+TEST_F(NonSemanticTextToBinaryTest, NonSemanticStringParameter) {
+  std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Testing.ExtInst"
+%2 = OpTypeVoid
+%3 = OpExtInst %2 %1 1 "foobar"
+)";
+
+  EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %."));
+}
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index b38f35e..fb9e964 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -24,18 +24,28 @@
           fuzzer_pass_add_useful_constructs_test.cpp
           instruction_descriptor_test.cpp
           transformation_add_constant_boolean_test.cpp
+          transformation_add_constant_composite_test.cpp
           transformation_add_constant_scalar_test.cpp
           transformation_add_dead_break_test.cpp
           transformation_add_dead_continue_test.cpp
+          transformation_add_global_undef_test.cpp
+          transformation_add_global_variable_test.cpp
           transformation_add_no_contraction_decoration_test.cpp
+          transformation_add_type_array_test.cpp
           transformation_add_type_boolean_test.cpp
           transformation_add_type_float_test.cpp
+          transformation_add_type_function_test.cpp
           transformation_add_type_int_test.cpp
+          transformation_add_type_matrix_test.cpp
           transformation_add_type_pointer_test.cpp
+          transformation_add_type_struct_test.cpp
+          transformation_add_type_vector_test.cpp
           transformation_composite_construct_test.cpp
           transformation_composite_extract_test.cpp
           transformation_copy_object_test.cpp
+          transformation_merge_blocks_test.cpp
           transformation_move_block_down_test.cpp
+          transformation_outline_function_test.cpp
           transformation_replace_boolean_constant_with_constant_binary_test.cpp
           transformation_replace_constant_with_uniform_test.cpp
           transformation_replace_id_with_synonym_test.cpp
diff --git a/test/fuzz/fuzz_test_util.cpp b/test/fuzz/fuzz_test_util.cpp
index bc6d4ee..1d15ad6 100644
--- a/test/fuzz/fuzz_test_util.cpp
+++ b/test/fuzz/fuzz_test_util.cpp
@@ -14,6 +14,7 @@
 
 #include "test/fuzz/fuzz_test_util.h"
 
+#include <fstream>
 #include <iostream>
 
 #include "tools/io.h"
@@ -105,5 +106,20 @@
   }
 }
 
+void DumpTransformationsJson(
+    const protobufs::TransformationSequence& transformations,
+    const char* filename) {
+  std::string json_string;
+  auto json_options = google::protobuf::util::JsonOptions();
+  json_options.add_whitespace = true;
+  auto json_generation_status = google::protobuf::util::MessageToJsonString(
+      transformations, &json_string, json_options);
+  if (json_generation_status == google::protobuf::util::Status::OK) {
+    std::ofstream transformations_json_file(filename);
+    transformations_json_file << json_string;
+    transformations_json_file.close();
+  }
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/test/fuzz/fuzz_test_util.h b/test/fuzz/fuzz_test_util.h
index 93f37e5..9e08bf6 100644
--- a/test/fuzz/fuzz_test_util.h
+++ b/test/fuzz/fuzz_test_util.h
@@ -19,6 +19,7 @@
 
 #include <vector>
 
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
 #include "spirv-tools/libspirv.h"
@@ -100,6 +101,12 @@
 // Dumps |binary| to file |filename|. Useful for interactive debugging.
 void DumpShader(const std::vector<uint32_t>& binary, const char* filename);
 
+// Dumps |transformations| to file |filename| in JSON format. Useful for
+// interactive debugging.
+void DumpTransformationsJson(
+    const protobufs::TransformationSequence& transformations,
+    const char* filename);
+
 }  // namespace fuzz
 }  // namespace spvtools
 
diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp
index 22a26fc..fa1f096 100644
--- a/test/fuzz/fuzzer_replayer_test.cpp
+++ b/test/fuzz/fuzzer_replayer_test.cpp
@@ -62,13 +62,11 @@
   for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
     std::vector<uint32_t> fuzzer_binary_out;
     protobufs::TransformationSequence fuzzer_transformation_sequence_out;
-    spvtools::FuzzerOptions fuzzer_options;
-    spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
 
-    Fuzzer fuzzer(env);
+    Fuzzer fuzzer(env, seed, true);
     fuzzer.SetMessageConsumer(kSilentConsumer);
     auto fuzzer_result_status =
-        fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out,
+        fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
                    &fuzzer_transformation_sequence_out);
     ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
     ASSERT_TRUE(t.Validate(fuzzer_binary_out));
@@ -421,8 +419,8 @@
          %86 = OpLabel
         %184 = OpPhi %26 %27 %78 %126 %89
          %92 = OpSLessThan %8 %184 %56
-               OpLoopMerge %88 %89 None
-               OpBranchConditional %92 %87 %88
+               OpLoopMerge %1000 %89 None
+               OpBranchConditional %92 %87 %1000
          %87 = OpLabel
          %95 = OpIAdd %26 %183 %74
          %96 = OpSLessThan %8 %184 %95
@@ -464,6 +462,8 @@
          %89 = OpLabel
         %126 = OpIAdd %26 %184 %74
                OpBranch %86
+       %1000 = OpLabel
+               OpBranch %88
          %88 = OpLabel
         %128 = OpIAdd %26 %183 %74
                OpBranch %77
@@ -724,7 +724,6 @@
          %37 = OpSDiv %6 %302 %35
          %38 = OpIMul %6 %35 %37
          %40 = OpIEqual %17 %38 %302
-               OpSelectionMerge %42 None
                OpBranchConditional %40 %41 %42
          %41 = OpLabel
          %50 = OpConvertSToF %20 %302
@@ -752,7 +751,6 @@
                OpBranch %59
          %75 = OpLabel
          %78 = OpSGreaterThan %17 %304 %9
-               OpSelectionMerge %80 None
                OpBranchConditional %78 %79 %80
          %79 = OpLabel
          %83 = OpISub %6 %304 %54
diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp
index 6485070..9af863e 100644
--- a/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/test/fuzz/fuzzer_shrinker_test.cpp
@@ -165,12 +165,10 @@
   // Run the fuzzer and check that it successfully yields a valid binary.
   std::vector<uint32_t> fuzzer_binary_out;
   protobufs::TransformationSequence fuzzer_transformation_sequence_out;
-  spvtools::FuzzerOptions fuzzer_options;
-  spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
-  Fuzzer fuzzer(env);
+  Fuzzer fuzzer(env, seed, true);
   fuzzer.SetMessageConsumer(kSilentConsumer);
   auto fuzzer_result_status =
-      fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out,
+      fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
                  &fuzzer_transformation_sequence_out);
   ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
   ASSERT_TRUE(t.Validate(fuzzer_binary_out));
@@ -530,8 +528,8 @@
          %86 = OpLabel
         %184 = OpPhi %26 %27 %78 %126 %89
          %92 = OpSLessThan %8 %184 %56
-               OpLoopMerge %88 %89 None
-               OpBranchConditional %92 %87 %88
+               OpLoopMerge %1000 %89 None
+               OpBranchConditional %92 %87 %1000
          %87 = OpLabel
          %95 = OpIAdd %26 %183 %74
          %96 = OpSLessThan %8 %184 %95
@@ -573,6 +571,8 @@
          %89 = OpLabel
         %126 = OpIAdd %26 %184 %74
                OpBranch %86
+       %1000 = OpLabel
+               OpBranch %88
          %88 = OpLabel
         %128 = OpIAdd %26 %183 %74
                OpBranch %77
@@ -831,7 +831,6 @@
          %37 = OpSDiv %6 %302 %35
          %38 = OpIMul %6 %35 %37
          %40 = OpIEqual %17 %38 %302
-               OpSelectionMerge %42 None
                OpBranchConditional %40 %41 %42
          %41 = OpLabel
          %50 = OpConvertSToF %20 %302
@@ -859,7 +858,6 @@
                OpBranch %59
          %75 = OpLabel
          %78 = OpSGreaterThan %17 %304 %9
-               OpSelectionMerge %80 None
                OpBranchConditional %78 %79 %80
          %79 = OpLabel
          %83 = OpISub %6 %304 %54
diff --git a/test/fuzz/transformation_add_constant_composite_test.cpp b/test/fuzz/transformation_add_constant_composite_test.cpp
new file mode 100644
index 0000000..5ce171b
--- /dev/null
+++ b/test/fuzz/transformation_add_constant_composite_test.cpp
@@ -0,0 +1,158 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_constant_composite.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddConstantCompositeTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeMatrix %7 3
+         %11 = OpConstant %6 0
+         %12 = OpConstant %6 1
+         %14 = OpConstant %6 2
+         %15 = OpConstant %6 3
+         %17 = OpConstant %6 4
+         %18 = OpConstant %6 5
+         %21 = OpTypeInt 32 1
+         %22 = OpTypeInt 32 0
+         %23 = OpConstant %22 3
+         %24 = OpTypeArray %21 %23
+         %25 = OpTypeBool
+         %26 = OpTypeStruct %24 %25
+         %29 = OpConstant %21 1
+         %30 = OpConstant %21 2
+         %31 = OpConstant %21 3
+         %33 = OpConstantFalse %25
+         %35 = OpTypeVector %6 3
+         %38 = OpConstant %6 6
+         %39 = OpConstant %6 7
+         %40 = OpConstant %6 8
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Too few ids
+  ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101})
+                   .IsApplicable(context.get(), fact_manager));
+  // Too many ids
+  ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14})
+                   .IsApplicable(context.get(), fact_manager));
+  // Id already in use
+  ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12})
+                   .IsApplicable(context.get(), fact_manager));
+  // %39 is not a type
+  ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12})
+                   .IsApplicable(context.get(), fact_manager));
+
+  TransformationAddConstantComposite transformations[] = {
+      // %100 = OpConstantComposite %7 %11 %12
+      TransformationAddConstantComposite(100, 7, {11, 12}),
+
+      // %101 = OpConstantComposite %7 %14 %15
+      TransformationAddConstantComposite(101, 7, {14, 15}),
+
+      // %102 = OpConstantComposite %7 %17 %18
+      TransformationAddConstantComposite(102, 7, {17, 18}),
+
+      // %103 = OpConstantComposite %8 %100 %101 %102
+      TransformationAddConstantComposite(103, 8, {100, 101, 102}),
+
+      // %104 = OpConstantComposite %24 %29 %30 %31
+      TransformationAddConstantComposite(104, 24, {29, 30, 31}),
+
+      // %105 = OpConstantComposite %26 %104 %33
+      TransformationAddConstantComposite(105, 26, {104, 33}),
+
+      // %106 = OpConstantComposite %35 %38 %39 %40
+      TransformationAddConstantComposite(106, 35, {38, 39, 40})};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeMatrix %7 3
+         %11 = OpConstant %6 0
+         %12 = OpConstant %6 1
+         %14 = OpConstant %6 2
+         %15 = OpConstant %6 3
+         %17 = OpConstant %6 4
+         %18 = OpConstant %6 5
+         %21 = OpTypeInt 32 1
+         %22 = OpTypeInt 32 0
+         %23 = OpConstant %22 3
+         %24 = OpTypeArray %21 %23
+         %25 = OpTypeBool
+         %26 = OpTypeStruct %24 %25
+         %29 = OpConstant %21 1
+         %30 = OpConstant %21 2
+         %31 = OpConstant %21 3
+         %33 = OpConstantFalse %25
+         %35 = OpTypeVector %6 3
+         %38 = OpConstant %6 6
+         %39 = OpConstant %6 7
+         %40 = OpConstant %6 8
+        %100 = OpConstantComposite %7 %11 %12
+        %101 = OpConstantComposite %7 %14 %15
+        %102 = OpConstantComposite %7 %17 %18
+        %103 = OpConstantComposite %8 %100 %101 %102
+        %104 = OpConstantComposite %24 %29 %30 %31
+        %105 = OpConstantComposite %26 %104 %33
+        %106 = OpConstantComposite %35 %38 %39 %40
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_dead_continue_test.cpp b/test/fuzz/transformation_add_dead_continue_test.cpp
index 8173e72..ff93da8 100644
--- a/test/fuzz/transformation_add_dead_continue_test.cpp
+++ b/test/fuzz/transformation_add_dead_continue_test.cpp
@@ -209,117 +209,6 @@
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
-TEST(TransformationAddDeadContinueTest,
-     DoNotAllowContinueToMergeBlockOfAnotherLoop) {
-  // A loop header must dominate its merge block if that merge block is
-  // reachable. We are thus not allowed to add a dead continue that would result
-  // in violation of this property. This test checks for such a scenario.
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %16 %139
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %8 = OpTypeBool
-         %14 = OpTypeVector %6 4
-         %15 = OpTypePointer Input %14
-         %16 = OpVariable %15 Input
-        %138 = OpTypePointer Output %14
-        %139 = OpVariable %138 Output
-        %400 = OpConstantTrue %8
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpBranch %500
-        %500 = OpLabel
-               OpLoopMerge %501 %502 None
-               OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502
-        %503 = OpLabel
-               OpLoopMerge %502 %504 None
-               OpBranchConditional %400 %505 %504
-        %505 = OpLabel
-               OpBranch %502
-        %504 = OpLabel
-               OpBranch %503
-        %502 = OpLabel
-               OpBranchConditional %400 %501 %500
-        %501 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-  FactManager fact_manager;
-
-  ASSERT_FALSE(TransformationAddDeadContinue(500, true, {})
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationAddDeadContinue(500, false, {})
-                   .IsApplicable(context.get(), fact_manager));
-}
-
-TEST(TransformationAddDeadContinueTest, DoNotAllowContinueToSelectionMerge) {
-  // A selection header must dominate its merge block if that merge block is
-  // reachable. We are thus not allowed to add a dead continue that would result
-  // in violation of this property. This test checks for such a scenario.
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %16 %139
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %8 = OpTypeBool
-         %14 = OpTypeVector %6 4
-         %15 = OpTypePointer Input %14
-         %16 = OpVariable %15 Input
-        %138 = OpTypePointer Output %14
-        %139 = OpVariable %138 Output
-        %400 = OpConstantTrue %8
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpBranch %500
-        %500 = OpLabel
-               OpLoopMerge %501 %502 None
-               OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502
-        %503 = OpLabel
-               OpSelectionMerge %502 None
-               OpBranchConditional %400 %505 %504
-        %505 = OpLabel
-               OpBranch %502
-        %504 = OpLabel
-               OpBranch %502
-        %502 = OpLabel
-               OpBranchConditional %400 %501 %500
-        %501 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-  FactManager fact_manager;
-
-  ASSERT_FALSE(TransformationAddDeadContinue(500, true, {})
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationAddDeadContinue(500, false, {})
-                   .IsApplicable(context.get(), fact_manager));
-}
-
 TEST(TransformationAddDeadContinueTest, LoopNest) {
   // Checks some allowed and disallowed scenarios for a nest of loops, including
   // continuing a loop from an if or switch.
@@ -1420,7 +1309,6 @@
                OpLoopMerge %1557 %1570 None
                OpBranchConditional %395 %1562 %1557
        %1562 = OpLabel
-               OpSelectionMerge %1570 None
                OpBranchConditional %395 %1571 %1570
        %1571 = OpLabel
                OpBranch %1557
@@ -1626,11 +1514,8 @@
   ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
 }
 
-TEST(TransformationAddDeadContinueTest, DISABLED_Miscellaneous6) {
-  // A miscellaneous test that exposing a known bug in spirv-fuzz.
-
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): re-enable
-  //  this test as an when the known issue is fixed.
+TEST(TransformationAddDeadContinueTest, Miscellaneous6) {
+  // A miscellaneous test that exposed a bug in spirv-fuzz.
 
   std::string shader = R"(
                OpCapability Shader
@@ -1648,7 +1533,7 @@
                OpBranch %10
          %10 = OpLabel
                OpLoopMerge %13 %12 None
-               OpBranchConditional %9 %13 %11
+               OpBranch %11
          %11 = OpLabel
          %20 = OpCopyObject %6 %9
                OpBranch %12
diff --git a/test/fuzz/transformation_add_function_test.cpp b/test/fuzz/transformation_add_function_test.cpp
new file mode 100644
index 0000000..66130be
--- /dev/null
+++ b/test/fuzz/transformation_add_function_test.cpp
@@ -0,0 +1,447 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_function.h"
+#include "source/fuzz/instruction_message.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddFunctionTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %8 %7 %9
+         %18 = OpConstant %8 0
+         %20 = OpConstant %6 0
+         %28 = OpTypeBool
+         %37 = OpConstant %6 1
+         %42 = OpTypePointer Private %8
+         %43 = OpVariable %42 Private
+         %47 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationAddFunction transformation1(std::vector<protobufs::Instruction>(
+      {MakeInstructionMessage(
+           SpvOpFunction, 8, 13,
+           {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+            {SPV_OPERAND_TYPE_ID, {10}}}),
+       MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
+       MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
+       MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
+       MakeInstructionMessage(
+           SpvOpVariable, 9, 17,
+           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+       MakeInstructionMessage(
+           SpvOpVariable, 7, 19,
+           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+       MakeInstructionMessage(
+           SpvOpStore, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {18}}}),
+       MakeInstructionMessage(
+           SpvOpStore, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {20}}}),
+       MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
+       MakeInstructionMessage(SpvOpLabel, 0, 21, {}),
+       MakeInstructionMessage(
+           SpvOpLoopMerge, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {23}},
+            {SPV_OPERAND_TYPE_ID, {24}},
+            {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}),
+       MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}),
+       MakeInstructionMessage(SpvOpLabel, 0, 25, {}),
+       MakeInstructionMessage(SpvOpLoad, 6, 26, {{SPV_OPERAND_TYPE_ID, {19}}}),
+       MakeInstructionMessage(SpvOpLoad, 6, 27, {{SPV_OPERAND_TYPE_ID, {11}}}),
+       MakeInstructionMessage(
+           SpvOpSLessThan, 28, 29,
+           {{SPV_OPERAND_TYPE_ID, {26}}, {SPV_OPERAND_TYPE_ID, {27}}}),
+       MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
+                              {{SPV_OPERAND_TYPE_ID, {29}},
+                               {SPV_OPERAND_TYPE_ID, {22}},
+                               {SPV_OPERAND_TYPE_ID, {23}}}),
+       MakeInstructionMessage(SpvOpLabel, 0, 22, {}),
+       MakeInstructionMessage(SpvOpLoad, 8, 30, {{SPV_OPERAND_TYPE_ID, {12}}}),
+       MakeInstructionMessage(SpvOpLoad, 6, 31, {{SPV_OPERAND_TYPE_ID, {19}}}),
+       MakeInstructionMessage(SpvOpConvertSToF, 8, 32,
+                              {{SPV_OPERAND_TYPE_ID, {31}}}),
+       MakeInstructionMessage(
+           SpvOpFMul, 8, 33,
+           {{SPV_OPERAND_TYPE_ID, {30}}, {SPV_OPERAND_TYPE_ID, {32}}}),
+       MakeInstructionMessage(SpvOpLoad, 8, 34, {{SPV_OPERAND_TYPE_ID, {17}}}),
+       MakeInstructionMessage(
+           SpvOpFAdd, 8, 35,
+           {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}),
+       MakeInstructionMessage(
+           SpvOpStore, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {35}}}),
+       MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {24}}}),
+       MakeInstructionMessage(SpvOpLabel, 0, 24, {}),
+       MakeInstructionMessage(SpvOpLoad, 6, 36, {{SPV_OPERAND_TYPE_ID, {19}}}),
+       MakeInstructionMessage(
+           SpvOpIAdd, 6, 38,
+           {{SPV_OPERAND_TYPE_ID, {36}}, {SPV_OPERAND_TYPE_ID, {37}}}),
+       MakeInstructionMessage(
+           SpvOpStore, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {38}}}),
+       MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
+       MakeInstructionMessage(SpvOpLabel, 0, 23, {}),
+       MakeInstructionMessage(SpvOpLoad, 8, 39, {{SPV_OPERAND_TYPE_ID, {17}}}),
+       MakeInstructionMessage(SpvOpReturnValue, 0, 0,
+                              {{SPV_OPERAND_TYPE_ID, {39}}}),
+       MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
+
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation1 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %8 %7 %9
+         %18 = OpConstant %8 0
+         %20 = OpConstant %6 0
+         %28 = OpTypeBool
+         %37 = OpConstant %6 1
+         %42 = OpTypePointer Private %8
+         %43 = OpVariable %42 Private
+         %47 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %13 = OpFunction %8 None %10
+         %11 = OpFunctionParameter %7
+         %12 = OpFunctionParameter %9
+         %14 = OpLabel
+         %17 = OpVariable %9 Function
+         %19 = OpVariable %7 Function
+               OpStore %17 %18
+               OpStore %19 %20
+               OpBranch %21
+         %21 = OpLabel
+               OpLoopMerge %23 %24 None
+               OpBranch %25
+         %25 = OpLabel
+         %26 = OpLoad %6 %19
+         %27 = OpLoad %6 %11
+         %29 = OpSLessThan %28 %26 %27
+               OpBranchConditional %29 %22 %23
+         %22 = OpLabel
+         %30 = OpLoad %8 %12
+         %31 = OpLoad %6 %19
+         %32 = OpConvertSToF %8 %31
+         %33 = OpFMul %8 %30 %32
+         %34 = OpLoad %8 %17
+         %35 = OpFAdd %8 %34 %33
+               OpStore %17 %35
+               OpBranch %24
+         %24 = OpLabel
+         %36 = OpLoad %6 %19
+         %38 = OpIAdd %6 %36 %37
+               OpStore %19 %38
+               OpBranch %21
+         %23 = OpLabel
+         %39 = OpLoad %8 %17
+               OpReturnValue %39
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation1, context.get()));
+
+  TransformationAddFunction transformation2(std::vector<protobufs::Instruction>(
+      {MakeInstructionMessage(
+           SpvOpFunction, 2, 15,
+           {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
+            {SPV_OPERAND_TYPE_ID, {3}}}),
+       MakeInstructionMessage(SpvOpLabel, 0, 16, {}),
+       MakeInstructionMessage(
+           SpvOpVariable, 7, 44,
+           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+       MakeInstructionMessage(
+           SpvOpVariable, 9, 45,
+           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+       MakeInstructionMessage(
+           SpvOpVariable, 7, 48,
+           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+       MakeInstructionMessage(
+           SpvOpVariable, 9, 49,
+           {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
+       MakeInstructionMessage(
+           SpvOpStore, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {44}}, {SPV_OPERAND_TYPE_ID, {20}}}),
+       MakeInstructionMessage(
+           SpvOpStore, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {18}}}),
+       MakeInstructionMessage(SpvOpFunctionCall, 8, 46,
+                              {{SPV_OPERAND_TYPE_ID, {13}},
+                               {SPV_OPERAND_TYPE_ID, {44}},
+                               {SPV_OPERAND_TYPE_ID, {45}}}),
+       MakeInstructionMessage(
+           SpvOpStore, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {48}}, {SPV_OPERAND_TYPE_ID, {37}}}),
+       MakeInstructionMessage(
+           SpvOpStore, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {49}}, {SPV_OPERAND_TYPE_ID, {47}}}),
+       MakeInstructionMessage(SpvOpFunctionCall, 8, 50,
+                              {{SPV_OPERAND_TYPE_ID, {13}},
+                               {SPV_OPERAND_TYPE_ID, {48}},
+                               {SPV_OPERAND_TYPE_ID, {49}}}),
+       MakeInstructionMessage(
+           SpvOpFAdd, 8, 51,
+           {{SPV_OPERAND_TYPE_ID, {46}}, {SPV_OPERAND_TYPE_ID, {50}}}),
+       MakeInstructionMessage(
+           SpvOpStore, 0, 0,
+           {{SPV_OPERAND_TYPE_ID, {43}}, {SPV_OPERAND_TYPE_ID, {51}}}),
+       MakeInstructionMessage(SpvOpReturn, 0, 0, {}),
+       MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
+
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation2 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %8 %7 %9
+         %18 = OpConstant %8 0
+         %20 = OpConstant %6 0
+         %28 = OpTypeBool
+         %37 = OpConstant %6 1
+         %42 = OpTypePointer Private %8
+         %43 = OpVariable %42 Private
+         %47 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %13 = OpFunction %8 None %10
+         %11 = OpFunctionParameter %7
+         %12 = OpFunctionParameter %9
+         %14 = OpLabel
+         %17 = OpVariable %9 Function
+         %19 = OpVariable %7 Function
+               OpStore %17 %18
+               OpStore %19 %20
+               OpBranch %21
+         %21 = OpLabel
+               OpLoopMerge %23 %24 None
+               OpBranch %25
+         %25 = OpLabel
+         %26 = OpLoad %6 %19
+         %27 = OpLoad %6 %11
+         %29 = OpSLessThan %28 %26 %27
+               OpBranchConditional %29 %22 %23
+         %22 = OpLabel
+         %30 = OpLoad %8 %12
+         %31 = OpLoad %6 %19
+         %32 = OpConvertSToF %8 %31
+         %33 = OpFMul %8 %30 %32
+         %34 = OpLoad %8 %17
+         %35 = OpFAdd %8 %34 %33
+               OpStore %17 %35
+               OpBranch %24
+         %24 = OpLabel
+         %36 = OpLoad %6 %19
+         %38 = OpIAdd %6 %36 %37
+               OpStore %19 %38
+               OpBranch %21
+         %23 = OpLabel
+         %39 = OpLoad %8 %17
+               OpReturnValue %39
+               OpFunctionEnd
+         %15 = OpFunction %2 None %3
+         %16 = OpLabel
+         %44 = OpVariable %7 Function
+         %45 = OpVariable %9 Function
+         %48 = OpVariable %7 Function
+         %49 = OpVariable %9 Function
+               OpStore %44 %20
+               OpStore %45 %18
+         %46 = OpFunctionCall %8 %13 %44 %45
+               OpStore %48 %37
+               OpStore %49 %47
+         %50 = OpFunctionCall %8 %13 %48 %49
+         %51 = OpFAdd %8 %46 %50
+               OpStore %43 %51
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation2, context.get()));
+}
+
+TEST(TransformationAddFunctionTest, InapplicableTransformations) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFloat 32
+          %9 = OpTypePointer Function %8
+         %10 = OpTypeFunction %8 %7 %9
+         %18 = OpConstant %8 0
+         %20 = OpConstant %6 0
+         %28 = OpTypeBool
+         %37 = OpConstant %6 1
+         %42 = OpTypePointer Private %8
+         %43 = OpVariable %42 Private
+         %47 = OpConstant %8 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %13 = OpFunction %8 None %10
+         %11 = OpFunctionParameter %7
+         %12 = OpFunctionParameter %9
+         %14 = OpLabel
+         %17 = OpVariable %9 Function
+         %19 = OpVariable %7 Function
+               OpStore %17 %18
+               OpStore %19 %20
+               OpBranch %21
+         %21 = OpLabel
+               OpLoopMerge %23 %24 None
+               OpBranch %25
+         %25 = OpLabel
+         %26 = OpLoad %6 %19
+         %27 = OpLoad %6 %11
+         %29 = OpSLessThan %28 %26 %27
+               OpBranchConditional %29 %22 %23
+         %22 = OpLabel
+         %30 = OpLoad %8 %12
+         %31 = OpLoad %6 %19
+         %32 = OpConvertSToF %8 %31
+         %33 = OpFMul %8 %30 %32
+         %34 = OpLoad %8 %17
+         %35 = OpFAdd %8 %34 %33
+               OpStore %17 %35
+               OpBranch %24
+         %24 = OpLabel
+         %36 = OpLoad %6 %19
+         %38 = OpIAdd %6 %36 %37
+               OpStore %19 %38
+               OpBranch %21
+         %23 = OpLabel
+         %39 = OpLoad %8 %17
+               OpReturnValue %39
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // No instructions
+  ASSERT_FALSE(
+      TransformationAddFunction(std::vector<protobufs::Instruction>({}))
+          .IsApplicable(context.get(), fact_manager));
+
+  // No function begin
+  ASSERT_FALSE(
+      TransformationAddFunction(
+          std::vector<protobufs::Instruction>(
+              {MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
+               MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
+               MakeInstructionMessage(SpvOpLabel, 0, 14, {})}))
+          .IsApplicable(context.get(), fact_manager));
+
+  // No OpLabel
+  ASSERT_FALSE(
+      TransformationAddFunction(
+          std::vector<protobufs::Instruction>(
+              {MakeInstructionMessage(SpvOpFunction, 8, 13,
+                                      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                                        {SpvFunctionControlMaskNone}},
+                                       {SPV_OPERAND_TYPE_ID, {10}}}),
+               MakeInstructionMessage(SpvOpReturnValue, 0, 0,
+                                      {{SPV_OPERAND_TYPE_ID, {39}}}),
+               MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}))
+          .IsApplicable(context.get(), fact_manager));
+
+  // Abrupt end of instructions
+  ASSERT_FALSE(TransformationAddFunction(
+                   std::vector<protobufs::Instruction>({MakeInstructionMessage(
+                       SpvOpFunction, 8, 13,
+                       {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                         {SpvFunctionControlMaskNone}},
+                        {SPV_OPERAND_TYPE_ID, {10}}})}))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // No function end
+  ASSERT_FALSE(
+      TransformationAddFunction(
+          std::vector<protobufs::Instruction>(
+              {MakeInstructionMessage(SpvOpFunction, 8, 13,
+                                      {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
+                                        {SpvFunctionControlMaskNone}},
+                                       {SPV_OPERAND_TYPE_ID, {10}}}),
+               MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
+               MakeInstructionMessage(SpvOpReturnValue, 0, 0,
+                                      {{SPV_OPERAND_TYPE_ID, {39}}})}))
+          .IsApplicable(context.get(), fact_manager));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_global_undef_test.cpp b/test/fuzz/transformation_add_global_undef_test.cpp
new file mode 100644
index 0000000..c14f7e9
--- /dev/null
+++ b/test/fuzz/transformation_add_global_undef_test.cpp
@@ -0,0 +1,118 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_global_undef.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddGlobalUndefTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Id already in use
+  ASSERT_FALSE(TransformationAddGlobalUndef(4, 11).IsApplicable(context.get(),
+                                                                fact_manager));
+  // %1 is not a type
+  ASSERT_FALSE(TransformationAddGlobalUndef(100, 1).IsApplicable(context.get(),
+                                                                 fact_manager));
+
+  // %3 is a function type
+  ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable(context.get(),
+                                                                 fact_manager));
+
+  TransformationAddGlobalUndef transformations[] = {
+      // %100 = OpUndef %6
+      TransformationAddGlobalUndef(100, 6),
+
+      // %101 = OpUndef %7
+      TransformationAddGlobalUndef(101, 7),
+
+      // %102 = OpUndef %8
+      TransformationAddGlobalUndef(102, 8),
+
+      // %103 = OpUndef %9
+      TransformationAddGlobalUndef(103, 9),
+
+      // %104 = OpUndef %10
+      TransformationAddGlobalUndef(104, 10),
+
+      // %105 = OpUndef %11
+      TransformationAddGlobalUndef(105, 11)};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+        %100 = OpUndef %6
+        %101 = OpUndef %7
+        %102 = OpUndef %8
+        %103 = OpUndef %9
+        %104 = OpUndef %10
+        %105 = OpUndef %11
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp
new file mode 100644
index 0000000..eda6828
--- /dev/null
+++ b/test/fuzz/transformation_add_global_variable_test.cpp
@@ -0,0 +1,275 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_global_variable.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddGlobalVariableTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypePointer Function %6
+         %10 = OpTypePointer Private %6
+         %20 = OpTypePointer Uniform %6
+         %11 = OpTypePointer Function %7
+         %12 = OpTypePointer Private %7
+         %13 = OpTypePointer Private %8
+         %14 = OpVariable %10 Private
+         %15 = OpVariable %20 Uniform
+         %16 = OpConstant %7 1
+         %17 = OpTypePointer Private %10
+         %18 = OpTypeBool
+         %19 = OpTypePointer Private %18
+         %21 = OpConstantTrue %18
+         %22 = OpConstantFalse %18
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Id already in use
+  ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0).IsApplicable(
+      context.get(), fact_manager));
+  // %1 is not a type
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0).IsApplicable(
+      context.get(), fact_manager));
+
+  // %7 is not a pointer type
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0).IsApplicable(
+      context.get(), fact_manager));
+
+  // %9 does not have Private storage class
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0).IsApplicable(
+      context.get(), fact_manager));
+
+  // %15 does not have Private storage class
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %10 is a pointer to float, while %16 is an int constant
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %10 is a Private pointer to float, while %15 is a variable with type
+  // Uniform float pointer
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %12 is a Private pointer to int, while %10 is a variable with type
+  // Private float pointer
+  ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK
+  // since the initializer's type should be the *pointee* type.
+  ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // This would work in principle, but logical addressing does not allow
+  // a pointer to a pointer.
+  ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14)
+                   .IsApplicable(context.get(), fact_manager));
+
+  TransformationAddGlobalVariable transformations[] = {
+      // %100 = OpVariable %12 Private
+      TransformationAddGlobalVariable(100, 12, 0),
+
+      // %101 = OpVariable %10 Private
+      TransformationAddGlobalVariable(101, 10, 0),
+
+      // %102 = OpVariable %13 Private
+      TransformationAddGlobalVariable(102, 13, 0),
+
+      // %103 = OpVariable %12 Private %16
+      TransformationAddGlobalVariable(103, 12, 16),
+
+      // %104 = OpVariable %19 Private %21
+      TransformationAddGlobalVariable(104, 19, 21),
+
+      // %105 = OpVariable %19 Private %22
+      TransformationAddGlobalVariable(105, 19, 22)};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypePointer Function %6
+         %10 = OpTypePointer Private %6
+         %20 = OpTypePointer Uniform %6
+         %11 = OpTypePointer Function %7
+         %12 = OpTypePointer Private %7
+         %13 = OpTypePointer Private %8
+         %14 = OpVariable %10 Private
+         %15 = OpVariable %20 Uniform
+         %16 = OpConstant %7 1
+         %17 = OpTypePointer Private %10
+         %18 = OpTypeBool
+         %19 = OpTypePointer Private %18
+         %21 = OpConstantTrue %18
+         %22 = OpConstantFalse %18
+        %100 = OpVariable %12 Private
+        %101 = OpVariable %10 Private
+        %102 = OpVariable %13 Private
+        %103 = OpVariable %12 Private %16
+        %104 = OpVariable %19 Private %21
+        %105 = OpVariable %19 Private %22
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) {
+  // This checks that when global variables are added to a SPIR-V 1.4+ module,
+  // they are also added to entry points of that module.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "m1"
+               OpEntryPoint Vertex %5 "m2"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypePointer Function %6
+         %10 = OpTypePointer Private %6
+         %20 = OpTypePointer Uniform %6
+         %11 = OpTypePointer Function %7
+         %12 = OpTypePointer Private %7
+         %13 = OpTypePointer Private %8
+         %14 = OpVariable %10 Private
+         %15 = OpVariable %20 Uniform
+         %16 = OpConstant %7 1
+         %17 = OpTypePointer Private %10
+         %18 = OpTypeBool
+         %19 = OpTypePointer Private %18
+         %21 = OpConstantTrue %18
+          %4 = OpFunction %2 None %3
+         %30 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %5 = OpFunction %2 None %3
+         %31 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationAddGlobalVariable transformations[] = {
+      // %100 = OpVariable %12 Private
+      TransformationAddGlobalVariable(100, 12, 0),
+
+      // %101 = OpVariable %12 Private %16
+      TransformationAddGlobalVariable(101, 12, 16),
+
+      // %102 = OpVariable %19 Private %21
+      TransformationAddGlobalVariable(102, 19, 21)};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "m1" %100 %101 %102
+               OpEntryPoint Vertex %5 "m2" %100 %101 %102
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypePointer Function %6
+         %10 = OpTypePointer Private %6
+         %20 = OpTypePointer Uniform %6
+         %11 = OpTypePointer Function %7
+         %12 = OpTypePointer Private %7
+         %13 = OpTypePointer Private %8
+         %14 = OpVariable %10 Private
+         %15 = OpVariable %20 Uniform
+         %16 = OpConstant %7 1
+         %17 = OpTypePointer Private %10
+         %18 = OpTypeBool
+         %19 = OpTypePointer Private %18
+         %21 = OpConstantTrue %18
+        %100 = OpVariable %12 Private
+        %101 = OpVariable %12 Private %16
+        %102 = OpVariable %19 Private %21
+          %4 = OpFunction %2 None %3
+         %30 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %5 = OpFunction %2 None %3
+         %31 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_array_test.cpp b/test/fuzz/transformation_add_type_array_test.cpp
new file mode 100644
index 0000000..2bcbe73
--- /dev/null
+++ b/test/fuzz/transformation_add_type_array_test.cpp
@@ -0,0 +1,136 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_array.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeArrayTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+         %12 = OpConstant %7 3
+         %13 = OpConstant %7 0
+         %14 = OpConstant %7 -1
+         %15 = OpTypeInt 32 0
+         %16 = OpConstant %15 5
+         %17 = OpConstant %15 0
+         %18 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Id already in use
+  ASSERT_FALSE(TransformationAddTypeArray(4, 10, 16).IsApplicable(
+      context.get(), fact_manager));
+  // %1 is not a type
+  ASSERT_FALSE(TransformationAddTypeArray(100, 1, 16)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %3 is a function type
+  ASSERT_FALSE(TransformationAddTypeArray(100, 3, 16)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %2 is not a constant
+  ASSERT_FALSE(TransformationAddTypeArray(100, 11, 2)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %18 is not an integer
+  ASSERT_FALSE(TransformationAddTypeArray(100, 11, 18)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %13 is signed 0
+  ASSERT_FALSE(TransformationAddTypeArray(100, 11, 13)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %14 is negative
+  ASSERT_FALSE(TransformationAddTypeArray(100, 11, 14)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %17 is unsigned 0
+  ASSERT_FALSE(TransformationAddTypeArray(100, 11, 17)
+                   .IsApplicable(context.get(), fact_manager));
+
+  TransformationAddTypeArray transformations[] = {
+      // %100 = OpTypeArray %10 %16
+      TransformationAddTypeArray(100, 10, 16),
+
+      // %101 = OpTypeArray %7 %12
+      TransformationAddTypeArray(101, 7, 12)};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+         %12 = OpConstant %7 3
+         %13 = OpConstant %7 0
+         %14 = OpConstant %7 -1
+         %15 = OpTypeInt 32 0
+         %16 = OpConstant %15 5
+         %17 = OpConstant %15 0
+         %18 = OpConstant %6 1
+        %100 = OpTypeArray %10 %16
+        %101 = OpTypeArray %7 %12
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_function_test.cpp b/test/fuzz/transformation_add_type_function_test.cpp
new file mode 100644
index 0000000..46bd436
--- /dev/null
+++ b/test/fuzz/transformation_add_type_function_test.cpp
@@ -0,0 +1,134 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_function.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeFunctionTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %12 = OpTypeFloat 32
+         %13 = OpTypeStruct %6 %12
+         %14 = OpTypePointer Function %13
+        %200 = OpTypePointer Function %13
+         %15 = OpTypeVector %12 3
+         %16 = OpTypePointer Function %15
+         %17 = OpTypeVector %12 2
+         %18 = OpTypeFunction %17 %14 %16
+         %23 = OpConstant %12 1
+         %24 = OpConstantComposite %17 %23 %23
+         %27 = OpConstant %6 3
+         %30 = OpConstant %6 1
+         %31 = OpConstant %12 2
+         %32 = OpConstantComposite %13 %30 %31
+         %33 = OpConstantComposite %15 %23 %23 %23
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Id already in use
+  ASSERT_FALSE(TransformationAddTypeFunction(4, 12, {12, 16, 14})
+                   .IsApplicable(context.get(), fact_manager));
+  // %1 is not a type
+  ASSERT_FALSE(TransformationAddTypeFunction(100, 1, {12, 16, 14})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // %18 is a function type
+  ASSERT_FALSE(TransformationAddTypeFunction(100, 12, {18})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // A function of this signature already exists
+  ASSERT_FALSE(TransformationAddTypeFunction(100, 17, {14, 16})
+                   .IsApplicable(context.get(), fact_manager));
+
+  TransformationAddTypeFunction transformations[] = {
+      // %100 = OpTypeFunction %12 %12 %16 %14
+      TransformationAddTypeFunction(100, 12, {12, 16, 14}),
+
+      // %101 = OpTypeFunction %12
+      TransformationAddTypeFunction(101, 12, {}),
+
+      // %102 = OpTypeFunction %17 %200 %16
+      TransformationAddTypeFunction(102, 17, {200, 16})};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %12 = OpTypeFloat 32
+         %13 = OpTypeStruct %6 %12
+         %14 = OpTypePointer Function %13
+        %200 = OpTypePointer Function %13
+         %15 = OpTypeVector %12 3
+         %16 = OpTypePointer Function %15
+         %17 = OpTypeVector %12 2
+         %18 = OpTypeFunction %17 %14 %16
+         %23 = OpConstant %12 1
+         %24 = OpConstantComposite %17 %23 %23
+         %27 = OpConstant %6 3
+         %30 = OpConstant %6 1
+         %31 = OpConstant %12 2
+         %32 = OpConstantComposite %13 %30 %31
+         %33 = OpConstantComposite %15 %23 %23 %23
+        %100 = OpTypeFunction %12 %12 %16 %14
+        %101 = OpTypeFunction %12
+        %102 = OpTypeFunction %17 %200 %16
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_matrix_test.cpp b/test/fuzz/transformation_add_type_matrix_test.cpp
new file mode 100644
index 0000000..84f27e9
--- /dev/null
+++ b/test/fuzz/transformation_add_type_matrix_test.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_matrix.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeMatrixTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Id already in use
+  ASSERT_FALSE(TransformationAddTypeMatrix(4, 9, 2).IsApplicable(context.get(),
+                                                                 fact_manager));
+  // %1 is not a type
+  ASSERT_FALSE(TransformationAddTypeMatrix(100, 1, 2).IsApplicable(
+      context.get(), fact_manager));
+
+  // %11 is not a floating-point vector
+  ASSERT_FALSE(TransformationAddTypeMatrix(100, 11, 2)
+                   .IsApplicable(context.get(), fact_manager));
+
+  TransformationAddTypeMatrix transformations[] = {
+      // %100 = OpTypeMatrix %8 2
+      TransformationAddTypeMatrix(100, 8, 2),
+
+      // %101 = OpTypeMatrix %8 3
+      TransformationAddTypeMatrix(101, 8, 3),
+
+      // %102 = OpTypeMatrix %8 4
+      TransformationAddTypeMatrix(102, 8, 4),
+
+      // %103 = OpTypeMatrix %9 2
+      TransformationAddTypeMatrix(103, 9, 2),
+
+      // %104 = OpTypeMatrix %9 3
+      TransformationAddTypeMatrix(104, 9, 3),
+
+      // %105 = OpTypeMatrix %9 4
+      TransformationAddTypeMatrix(105, 9, 4),
+
+      // %106 = OpTypeMatrix %10 2
+      TransformationAddTypeMatrix(106, 10, 2),
+
+      // %107 = OpTypeMatrix %10 3
+      TransformationAddTypeMatrix(107, 10, 3),
+
+      // %108 = OpTypeMatrix %10 4
+      TransformationAddTypeMatrix(108, 10, 4)};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+        %100 = OpTypeMatrix %8 2
+        %101 = OpTypeMatrix %8 3
+        %102 = OpTypeMatrix %8 4
+        %103 = OpTypeMatrix %9 2
+        %104 = OpTypeMatrix %9 3
+        %105 = OpTypeMatrix %9 4
+        %106 = OpTypeMatrix %10 2
+        %107 = OpTypeMatrix %10 3
+        %108 = OpTypeMatrix %10 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_struct_test.cpp b/test/fuzz/transformation_add_type_struct_test.cpp
new file mode 100644
index 0000000..ae68c9a
--- /dev/null
+++ b/test/fuzz/transformation_add_type_struct_test.cpp
@@ -0,0 +1,110 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_struct.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeStructTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Id already in use
+  ASSERT_FALSE(TransformationAddTypeStruct(4, {}).IsApplicable(context.get(),
+                                                               fact_manager));
+  // %1 is not a type
+  ASSERT_FALSE(TransformationAddTypeStruct(100, {1}).IsApplicable(
+      context.get(), fact_manager));
+
+  // %3 is a function type
+  ASSERT_FALSE(TransformationAddTypeStruct(100, {3}).IsApplicable(
+      context.get(), fact_manager));
+
+  TransformationAddTypeStruct transformations[] = {
+      // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11
+      TransformationAddTypeStruct(100, {6, 7, 8, 9, 10, 11}),
+
+      // %101 = OpTypeStruct
+      TransformationAddTypeStruct(101, {}),
+
+      // %102 = OpTypeStruct %6
+      TransformationAddTypeStruct(102, {6}),
+
+      // %103 = OpTypeStruct %6 %6
+      TransformationAddTypeStruct(103, {6, 6})};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeVector %6 2
+          %9 = OpTypeVector %6 3
+         %10 = OpTypeVector %6 4
+         %11 = OpTypeVector %7 2
+        %100 = OpTypeStruct %6 %7 %8 %9 %10 %11
+        %101 = OpTypeStruct
+        %102 = OpTypeStruct %6
+        %103 = OpTypeStruct %6 %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_add_type_vector_test.cpp b/test/fuzz/transformation_add_type_vector_test.cpp
new file mode 100644
index 0000000..6ac4498
--- /dev/null
+++ b/test/fuzz/transformation_add_type_vector_test.cpp
@@ -0,0 +1,102 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_type_vector.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypeVectorTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeInt 32 0
+          %9 = OpTypeBool
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Id already in use
+  ASSERT_FALSE(TransformationAddTypeVector(4, 6, 2).IsApplicable(context.get(),
+                                                                 fact_manager));
+  // %1 is not a type
+  ASSERT_FALSE(TransformationAddTypeVector(100, 1, 2).IsApplicable(
+      context.get(), fact_manager));
+
+  TransformationAddTypeVector transformations[] = {
+      // %100 = OpTypeVector %6 2
+      TransformationAddTypeVector(100, 6, 2),
+
+      // %101 = OpTypeVector %7 3
+      TransformationAddTypeVector(101, 7, 3),
+
+      // %102 = OpTypeVector %8 4
+      TransformationAddTypeVector(102, 8, 4),
+
+      // %103 = OpTypeVector %9 2
+      TransformationAddTypeVector(103, 9, 2)};
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+  }
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 1
+          %8 = OpTypeInt 32 0
+          %9 = OpTypeBool
+        %100 = OpTypeVector %6 2
+        %101 = OpTypeVector %7 3
+        %102 = OpTypeVector %8 4
+        %103 = OpTypeVector %9 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp
index 9d3fde0..b489f71 100644
--- a/test/fuzz/transformation_copy_object_test.cpp
+++ b/test/fuzz/transformation_copy_object_test.cpp
@@ -291,7 +291,7 @@
          %31 = OpLabel
          %42 = OpAccessChain %36 %18 %41
          %43 = OpLoad %11 %42
-               OpSelectionMerge %47 None
+               OpSelectionMerge %45 None
                OpSwitch %43 %46 0 %44 1 %45
          %46 = OpLabel
          %69 = OpIAdd %11 %96 %27
diff --git a/test/fuzz/transformation_merge_blocks_test.cpp b/test/fuzz/transformation_merge_blocks_test.cpp
new file mode 100644
index 0000000..e2b4aa6
--- /dev/null
+++ b/test/fuzz/transformation_merge_blocks_test.cpp
@@ -0,0 +1,675 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_merge_blocks.h"
+
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationMergeBlocksTest, BlockDoesNotExist) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  ASSERT_FALSE(
+      TransformationMergeBlocks(3).IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationMergeBlocks(7).IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationMergeBlocksTest, DoNotMergeFirstBlockHasMultipleSuccessors) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %7 = OpTypeBool
+          %8 = OpConstantTrue %7
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %8 %6 %9
+          %6 = OpLabel
+               OpBranch %10
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  ASSERT_FALSE(
+      TransformationMergeBlocks(6).IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationMergeBlocksTest,
+     DoNotMergeSecondBlockHasMultiplePredecessors) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %7 = OpTypeBool
+          %8 = OpConstantTrue %7
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %8 %6 %9
+          %6 = OpLabel
+               OpBranch %10
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  ASSERT_FALSE(
+      TransformationMergeBlocks(10).IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionMerge) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %7 = OpTypeBool
+          %8 = OpConstantTrue %7
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %8 %6 %9
+          %6 = OpLabel
+               OpBranch %11
+          %9 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationMergeBlocks transformation(10);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %7 = OpTypeBool
+          %8 = OpConstantTrue %7
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %11 None
+               OpBranchConditional %8 %6 %9
+          %6 = OpLabel
+               OpBranch %11
+          %9 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopMerge) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %7 = OpTypeBool
+          %8 = OpConstantTrue %7
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+         %12 = OpLabel
+               OpBranch %5
+          %5 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranch %6
+          %6 = OpLabel
+               OpBranchConditional %8 %9 %11
+          %9 = OpLabel
+               OpBranch %10
+         %11 = OpLabel
+               OpBranch %5
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationMergeBlocks transformation(10);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %7 = OpTypeBool
+          %8 = OpConstantTrue %7
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+         %12 = OpLabel
+               OpBranch %5
+          %5 = OpLabel
+               OpLoopMerge %9 %11 None
+               OpBranch %6
+          %6 = OpLabel
+               OpBranchConditional %8 %9 %11
+          %9 = OpLabel
+               OpReturn
+         %11 = OpLabel
+               OpBranch %5
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopContinue) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %7 = OpTypeBool
+          %8 = OpConstantTrue %7
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpBranch %5
+          %5 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranch %6
+          %6 = OpLabel
+               OpSelectionMerge %9 None
+               OpBranchConditional %8 %9 %12
+         %12 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpBranch %5
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationMergeBlocks transformation(11);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %7 = OpTypeBool
+          %8 = OpConstantTrue %7
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+         %13 = OpLabel
+               OpBranch %5
+          %5 = OpLabel
+               OpLoopMerge %10 %12 None
+               OpBranch %6
+          %6 = OpLabel
+               OpSelectionMerge %9 None
+               OpBranchConditional %8 %9 %12
+         %12 = OpLabel
+               OpBranch %5
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockStartsWithOpPhi) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %7 = OpTypeBool
+          %8 = OpUndef %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+          %9 = OpPhi %7 %8 %5
+         %10 = OpCopyObject %7 %9
+               OpBranch %11
+         %11 = OpLabel
+         %12 = OpCopyObject %7 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationMergeBlocks transformation(6);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %7 = OpTypeBool
+          %8 = OpUndef %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %10 = OpCopyObject %7 %8
+               OpBranch %11
+         %11 = OpLabel
+         %12 = OpCopyObject %7 %8
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, BasicMerge) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %100
+        %100 = OpLabel
+               OpStore %10 %11
+         %12 = OpLoad %6 %10
+         %13 = OpLoad %6 %8
+               OpBranch %101
+        %101 = OpLabel
+         %14 = OpIAdd %6 %13 %12
+               OpStore %8 %14
+         %15 = OpLoad %6 %8
+               OpBranch %102
+        %102 = OpLabel
+         %16 = OpLoad %6 %10
+         %17 = OpIMul %6 %16 %15
+               OpBranch %103
+        %103 = OpLabel
+               OpStore %10 %17
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  for (auto& transformation :
+       {TransformationMergeBlocks(100), TransformationMergeBlocks(101),
+        TransformationMergeBlocks(102), TransformationMergeBlocks(103)}) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %12 = OpLoad %6 %10
+         %13 = OpLoad %6 %8
+         %14 = OpIAdd %6 %13 %12
+               OpStore %8 %14
+         %15 = OpLoad %6 %8
+         %16 = OpLoad %6 %10
+         %17 = OpIMul %6 %16 %15
+               OpStore %10 %17
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionHeader) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %50 = OpTypeBool
+         %51 = OpConstantTrue %50
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %100
+        %100 = OpLabel
+               OpStore %10 %11
+         %12 = OpLoad %6 %10
+         %13 = OpLoad %6 %8
+               OpBranch %101
+        %101 = OpLabel
+               OpSelectionMerge %103 None
+               OpBranchConditional %51 %102 %103
+        %102 = OpLabel
+         %14 = OpIAdd %6 %13 %12
+               OpStore %8 %14
+               OpBranch %103
+        %103 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  for (auto& transformation :
+       {TransformationMergeBlocks(101), TransformationMergeBlocks(100)}) {
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %50 = OpTypeBool
+         %51 = OpConstantTrue %50
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %12 = OpLoad %6 %10
+         %13 = OpLoad %6 %8
+               OpSelectionMerge %103 None
+               OpBranchConditional %51 %102 %103
+        %102 = OpLabel
+         %14 = OpIAdd %6 %13 %12
+               OpStore %8 %14
+               OpBranch %103
+        %103 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationMergeBlocksTest,
+     MergeWhenFirstBlockIsLoopMergeFollowedByUnconditionalBranch) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %50 = OpTypeBool
+         %51 = OpConstantTrue %50
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %100
+        %100 = OpLabel
+               OpLoopMerge %102 %103 None
+               OpBranch %101
+        %101 = OpLabel
+        %200 = OpCopyObject %6 %9
+               OpBranchConditional %51 %102 %103
+        %103 = OpLabel
+               OpBranch %100
+        %102 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationMergeBlocks transformation(101);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %11 = OpConstant %6 3
+         %50 = OpTypeBool
+         %51 = OpConstantTrue %50
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %100
+        %100 = OpLabel
+        %200 = OpCopyObject %6 %9
+               OpLoopMerge %102 %103 None
+               OpBranchConditional %51 %102 %103
+        %103 = OpLabel
+               OpBranch %100
+        %102 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp
new file mode 100644
index 0000000..4f828b6
--- /dev/null
+++ b/test/fuzz/transformation_outline_function_test.cpp
@@ -0,0 +1,2045 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_outline_function.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationOutlineFunctionTest, TrivialOutline) {
+  // This tests outlining of a single, empty basic block.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
+                                               100, 101, 102, 103,
+                                               /* not relevant */ 201, {}, {});
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+        %103 = OpFunctionCall %2 %101
+               OpReturn
+               OpFunctionEnd
+        %101 = OpFunction %2 None %3
+        %102 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineIfRegionStartsWithOpVariable) {
+  // This checks that we do not outline the first block of a function if it
+  // contains OpVariable.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %7 = OpTypeBool
+          %8 = OpTypePointer Function %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %6 = OpVariable %8 Function
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200,
+                                               100, 101, 102, 103,
+                                               /* not relevant */ 201, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineInterestingControlFlowNoState) {
+  // This tests outlining of some non-trivial control flow, but such that the
+  // basic blocks in the control flow do not actually do anything.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeBool
+         %21 = OpConstantTrue %20
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpBranch %7
+          %7 = OpLabel
+               OpSelectionMerge %9 None
+               OpBranchConditional %21 %8 %9
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %12 %11 None
+               OpBranch %10
+         %10 = OpLabel
+               OpBranchConditional %21 %11 %12
+         %11 = OpLabel
+               OpBranch %9
+         %12 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 13, /* not relevant */
+                                               200, 100, 101, 102, 103,
+                                               /* not relevant */ 201, {}, {});
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeBool
+         %21 = OpConstantTrue %20
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+        %103 = OpFunctionCall %2 %101
+               OpReturn
+               OpFunctionEnd
+        %101 = OpFunction %2 None %3
+        %102 = OpLabel
+               OpBranch %7
+          %7 = OpLabel
+               OpSelectionMerge %9 None
+               OpBranchConditional %21 %8 %9
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %12 %11 None
+               OpBranch %10
+         %10 = OpLabel
+               OpBranchConditional %21 %11 %12
+         %11 = OpLabel
+               OpBranch %9
+         %12 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesUnusedIds) {
+  // This tests outlining of a single basic block that does some computation,
+  // but that does not use nor generate ids required outside of the outlined
+  // region.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+          %7 = OpCopyObject %20 %21
+          %8 = OpCopyObject %20 %21
+          %9 = OpIAdd %20 %7 %8
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 6, /* not relevant */ 200,
+                                               100, 101, 102, 103,
+                                               /* not relevant */ 201, {}, {});
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+        %103 = OpFunctionCall %2 %101
+               OpReturn
+               OpFunctionEnd
+        %101 = OpFunction %2 None %3
+        %102 = OpLabel
+          %7 = OpCopyObject %20 %21
+          %8 = OpCopyObject %20 %21
+          %9 = OpIAdd %20 %7 %8
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesSingleUsedId) {
+  // This tests outlining of a block that generates an id that is used in a
+  // later block.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+          %7 = OpCopyObject %20 %21
+          %8 = OpCopyObject %20 %21
+          %9 = OpIAdd %20 %7 %8
+               OpBranch %10
+         %10 = OpLabel
+         %11 = OpCopyObject %20 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 6, 99, 100, 101, 102, 103,
+                                               105, {}, {{9, 104}});
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+          %3 = OpTypeFunction %2
+         %99 = OpTypeStruct %20
+        %100 = OpTypeFunction %99
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+        %103 = OpFunctionCall %99 %101
+          %9 = OpCompositeExtract %20 %103 0
+               OpBranch %10
+         %10 = OpLabel
+         %11 = OpCopyObject %20 %9
+               OpReturn
+               OpFunctionEnd
+        %101 = OpFunction %99 None %100
+        %102 = OpLabel
+          %7 = OpCopyObject %20 %21
+          %8 = OpCopyObject %20 %21
+        %104 = OpIAdd %20 %7 %8
+        %105 = OpCompositeConstruct %99 %104
+               OpReturnValue %105
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineDiamondThatGeneratesSeveralIds) {
+  // This tests outlining of several blocks that generate a number of ids that
+  // are used in later blocks.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+         %22 = OpTypeBool
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+          %7 = OpCopyObject %20 %21
+          %8 = OpCopyObject %20 %21
+          %9 = OpSLessThan %22 %7 %8
+               OpSelectionMerge %12 None
+               OpBranchConditional %9 %10 %11
+         %10 = OpLabel
+         %13 = OpIAdd %20 %7 %8
+               OpBranch %12
+         %11 = OpLabel
+         %14 = OpIAdd %20 %7 %7
+               OpBranch %12
+         %12 = OpLabel
+         %15 = OpPhi %20 %13 %10 %14 %11
+               OpBranch %80
+         %80 = OpLabel
+               OpBranch %16
+         %16 = OpLabel
+         %17 = OpCopyObject %20 %15
+         %18 = OpCopyObject %22 %9
+         %19 = OpIAdd %20 %7 %8
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      6, 80, 100, 101, 102, 103, 104, 105, {},
+      {{15, 106}, {9, 107}, {7, 108}, {8, 109}});
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+         %22 = OpTypeBool
+          %3 = OpTypeFunction %2
+        %100 = OpTypeStruct %20 %20 %22 %20
+        %101 = OpTypeFunction %100
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+        %104 = OpFunctionCall %100 %102
+          %7 = OpCompositeExtract %20 %104 0
+          %8 = OpCompositeExtract %20 %104 1
+          %9 = OpCompositeExtract %22 %104 2
+         %15 = OpCompositeExtract %20 %104 3
+               OpBranch %16
+         %16 = OpLabel
+         %17 = OpCopyObject %20 %15
+         %18 = OpCopyObject %22 %9
+         %19 = OpIAdd %20 %7 %8
+               OpReturn
+               OpFunctionEnd
+        %102 = OpFunction %100 None %101
+        %103 = OpLabel
+        %108 = OpCopyObject %20 %21
+        %109 = OpCopyObject %20 %21
+        %107 = OpSLessThan %22 %108 %109
+               OpSelectionMerge %12 None
+               OpBranchConditional %107 %10 %11
+         %10 = OpLabel
+         %13 = OpIAdd %20 %108 %109
+               OpBranch %12
+         %11 = OpLabel
+         %14 = OpIAdd %20 %108 %108
+               OpBranch %12
+         %12 = OpLabel
+        %106 = OpPhi %20 %13 %10 %14 %11
+               OpBranch %80
+         %80 = OpLabel
+        %105 = OpCompositeConstruct %100 %108 %109 %107 %106
+               OpReturnValue %105
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesASingleId) {
+  // This tests outlining of a block that uses an id defined earlier.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %7 = OpCopyObject %20 %21
+               OpBranch %6
+          %6 = OpLabel
+          %8 = OpCopyObject %20 %7
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104,
+                                               105, {{7, 106}}, {});
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+          %3 = OpTypeFunction %2
+        %101 = OpTypeFunction %2 %20
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %7 = OpCopyObject %20 %21
+               OpBranch %6
+          %6 = OpLabel
+        %104 = OpFunctionCall %2 %102 %7
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %102 = OpFunction %2 None %101
+        %106 = OpFunctionParameter %20
+        %103 = OpLabel
+          %8 = OpCopyObject %20 %106
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAVariable) {
+  // This tests outlining of a block that uses a variable.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+          %3 = OpTypeFunction %2
+         %12 = OpTypePointer Function %20
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %13 = OpVariable %12 Function
+               OpBranch %6
+          %6 = OpLabel
+          %8 = OpLoad %20 %13
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104,
+                                               105, {{13, 106}}, {});
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeInt 32 1
+         %21 = OpConstant %20 5
+          %3 = OpTypeFunction %2
+         %12 = OpTypePointer Function %20
+        %101 = OpTypeFunction %2 %12
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %13 = OpVariable %12 Function
+               OpBranch %6
+          %6 = OpLabel
+        %104 = OpFunctionCall %2 %102 %13
+               OpBranch %10
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %102 = OpFunction %2 None %101
+        %106 = OpFunctionParameter %12
+        %103 = OpLabel
+          %8 = OpLoad %20 %106
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAParameter) {
+  // This tests outlining of a block that uses a function parameter.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "foo(i1;"
+               OpName %9 "x"
+               OpName %18 "param"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7
+         %13 = OpConstant %6 1
+         %17 = OpConstant %6 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %18 = OpVariable %7 Function
+               OpStore %18 %17
+         %19 = OpFunctionCall %6 %10 %18
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %6 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+         %12 = OpLoad %6 %9
+         %14 = OpIAdd %6 %12 %13
+               OpReturnValue %14
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(11, 11, 100, 101, 102, 103, 104,
+                                               105, {{9, 106}}, {{14, 107}});
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "foo(i1;"
+               OpName %9 "x"
+               OpName %18 "param"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7
+         %13 = OpConstant %6 1
+         %17 = OpConstant %6 3
+        %100 = OpTypeStruct %6
+        %101 = OpTypeFunction %100 %7
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %18 = OpVariable %7 Function
+               OpStore %18 %17
+         %19 = OpFunctionCall %6 %10 %18
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %6 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+        %104 = OpFunctionCall %100 %102 %9
+         %14 = OpCompositeExtract %6 %104 0
+               OpReturnValue %14
+               OpFunctionEnd
+        %102 = OpFunction %100 None %101
+        %106 = OpFunctionParameter %7
+        %103 = OpLabel
+         %12 = OpLoad %6 %106
+        %107 = OpIAdd %6 %12 %13
+        %105 = OpCompositeConstruct %100 %107
+               OpReturnValue %105
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineIfLoopMergeIsOutsideRegion) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %9 = OpTypeBool
+         %10 = OpConstantTrue %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpLoopMerge %7 %8 None
+               OpBranch %8
+          %8 = OpLabel
+               OpBranchConditional %10 %6 %7
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104,
+                                               105, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesReturn) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeBool
+         %21 = OpConstantTrue %20
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpBranch %7
+          %7 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %21 %8 %9
+          %8 = OpLabel
+               OpReturn
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpBranch %12
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
+                                               100, 101, 102, 103,
+                                               /* not relevant */ 201, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesKill) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeBool
+         %21 = OpConstantTrue %20
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpBranch %7
+          %7 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %21 %8 %9
+          %8 = OpLabel
+               OpKill
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpBranch %12
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
+                                               100, 101, 102, 103,
+                                               /* not relevant */ 201, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineIfRegionInvolvesUnreachable) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+         %20 = OpTypeBool
+         %21 = OpConstantTrue %20
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpBranch %7
+          %7 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %21 %8 %9
+          %8 = OpLabel
+               OpBranch %10
+          %9 = OpLabel
+               OpUnreachable
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpBranch %12
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200,
+                                               100, 101, 102, 103,
+                                               /* not relevant */ 201, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineIfSelectionMergeIsOutsideRegion) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %9 = OpTypeBool
+         %10 = OpConstantTrue %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpSelectionMerge %7 None
+               OpBranchConditional %10 %8 %7
+          %8 = OpLabel
+               OpBranch %7
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104,
+                                               105, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineIfLoopHeadIsOutsideRegion) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %9 = OpTypeBool
+         %10 = OpConstantTrue %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpLoopMerge %8 %11 None
+               OpBranch %7
+          %7 = OpLabel
+               OpBranchConditional %10 %11 %8
+         %11 = OpLabel
+               OpBranch %6
+          %8 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(7, 8, 100, 101, 102, 103, 104,
+                                               105, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineIfLoopContinueIsOutsideRegion) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %9 = OpTypeBool
+         %10 = OpConstantTrue %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpLoopMerge %7 %8 None
+               OpBranch %7
+          %8 = OpLabel
+               OpBranch %6
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104,
+                                               105, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineWithLoopCarriedPhiDependence) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %9 = OpTypeBool
+         %10 = OpConstantTrue %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+         %12 = OpPhi %9 %10 %5 %13 %8
+               OpLoopMerge %7 %8 None
+               OpBranch %8
+          %8 = OpLabel
+         %13 = OpCopyObject %9 %10
+               OpBranchConditional %10 %6 %7
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104,
+                                               105, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineSelectionHeaderNotInRegion) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %10 None
+               OpBranchConditional %7 %8 %8
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(8, 11, 100, 101, 102, 103, 104,
+                                               105, {}, {});
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnVoid) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %20 = OpTypeInt 32 0
+         %21 = OpConstant %20 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %22 = OpCopyObject %20 %21
+               OpBranch %54
+         %54 = OpLabel
+               OpBranch %57
+         %57 = OpLabel
+         %23 = OpCopyObject %20 %22
+               OpBranch %58
+         %58 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 54,
+      /*exit_block*/ 58,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {{22, 206}},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %20 = OpTypeInt 32 0
+         %21 = OpConstant %20 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+        %201 = OpTypeFunction %2 %20
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %22 = OpCopyObject %20 %21
+               OpBranch %54
+         %54 = OpLabel
+        %204 = OpFunctionCall %2 %202 %22
+               OpReturn
+               OpFunctionEnd
+        %202 = OpFunction %2 None %201
+        %206 = OpFunctionParameter %20
+        %203 = OpLabel
+               OpBranch %57
+         %57 = OpLabel
+         %23 = OpCopyObject %20 %206
+               OpBranch %58
+         %58 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnValue) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %20 = OpTypeInt 32 0
+         %21 = OpConstant %20 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %30 = OpTypeFunction %20
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %6 = OpFunctionCall %20 %100
+               OpReturn
+               OpFunctionEnd
+        %100 = OpFunction %20 None %30
+          %8 = OpLabel
+         %31 = OpCopyObject %20 %21
+               OpBranch %9
+          %9 = OpLabel
+         %32 = OpCopyObject %20 %31
+               OpBranch %10
+         %10 = OpLabel
+               OpReturnValue %32
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 9,
+      /*exit_block*/ 10,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {{31, 206}},
+      /*output_id_to_fresh_id*/ {{32, 207}});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %20 = OpTypeInt 32 0
+         %21 = OpConstant %20 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %30 = OpTypeFunction %20
+        %200 = OpTypeStruct %20
+        %201 = OpTypeFunction %200 %20
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %6 = OpFunctionCall %20 %100
+               OpReturn
+               OpFunctionEnd
+        %100 = OpFunction %20 None %30
+          %8 = OpLabel
+         %31 = OpCopyObject %20 %21
+               OpBranch %9
+          %9 = OpLabel
+        %204 = OpFunctionCall %200 %202 %31
+         %32 = OpCompositeExtract %20 %204 0
+               OpReturnValue %32
+               OpFunctionEnd
+        %202 = OpFunction %200 None %201
+        %206 = OpFunctionParameter %20
+        %203 = OpLabel
+        %207 = OpCopyObject %20 %206
+               OpBranch %10
+         %10 = OpLabel
+        %205 = OpCompositeConstruct %200 %207
+               OpReturnValue %205
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     OutlineRegionEndingWithConditionalBranch) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %20 = OpTypeBool
+         %21 = OpConstantTrue %20
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %54
+         %54 = OpLabel
+          %6 = OpCopyObject %20 %21
+               OpSelectionMerge %8 None
+               OpBranchConditional %6 %7 %8
+          %7 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 54,
+      /*exit_block*/ 54,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {{}},
+      /*output_id_to_fresh_id*/ {{6, 206}});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %20 = OpTypeBool
+         %21 = OpConstantTrue %20
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+        %200 = OpTypeStruct %20
+        %201 = OpTypeFunction %200
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %54
+         %54 = OpLabel
+        %204 = OpFunctionCall %200 %202
+          %6 = OpCompositeExtract %20 %204 0
+               OpSelectionMerge %8 None
+               OpBranchConditional %6 %7 %8
+          %7 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %202 = OpFunction %200 None %201
+        %203 = OpLabel
+        %206 = OpCopyObject %20 %21
+        %205 = OpCompositeConstruct %200 %206
+               OpReturnValue %205
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     OutlineRegionEndingWithConditionalBranch2) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %20 = OpTypeBool
+         %21 = OpConstantTrue %20
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %6 = OpCopyObject %20 %21
+               OpBranch %54
+         %54 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %6 %7 %8
+          %7 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 54,
+      /*exit_block*/ 54,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %20 = OpTypeBool
+         %21 = OpConstantTrue %20
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %6 = OpCopyObject %20 %21
+               OpBranch %54
+         %54 = OpLabel
+        %204 = OpFunctionCall %2 %202
+               OpSelectionMerge %8 None
+               OpBranchConditional %6 %7 %8
+          %7 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpReturn
+               OpFunctionEnd
+        %202 = OpFunction %2 None %3
+        %203 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatStartsWithOpPhi) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %21
+         %21 = OpLabel
+         %22 = OpPhi %6 %7 %5
+         %23 = OpCopyObject %6 %22
+               OpBranch %24
+         %24 = OpLabel
+         %25 = OpCopyObject %6 %23
+         %26 = OpCopyObject %6 %22
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 21,
+      /*exit_block*/ 21,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 204,
+      /*new_caller_result_id*/ 205,
+      /*new_callee_result_id*/ 206,
+      /*input_id_to_fresh_id*/ {{22, 207}},
+      /*output_id_to_fresh_id*/ {{23, 208}});
+
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineRegionThatStartsWithLoopHeader) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %21
+         %21 = OpLabel
+               OpLoopMerge %22 %23 None
+               OpBranch %24
+         %24 = OpLabel
+               OpBranchConditional %7 %22 %23
+         %23 = OpLabel
+               OpBranch %21
+         %22 = OpLabel
+               OpBranch %25
+         %25 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 21,
+      /*exit_block*/ 24,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 204,
+      /*new_caller_result_id*/ 205,
+      /*new_callee_result_id*/ 206,
+      /*input_id_to_fresh_id*/ {},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest,
+     DoNotOutlineRegionThatEndsWithLoopMerge) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %21
+         %21 = OpLabel
+               OpLoopMerge %22 %23 None
+               OpBranch %24
+         %24 = OpLabel
+               OpBranchConditional %7 %22 %23
+         %23 = OpLabel
+               OpBranch %21
+         %22 = OpLabel
+               OpBranch %25
+         %25 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 5,
+      /*exit_block*/ 22,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 204,
+      /*new_caller_result_id*/ 205,
+      /*new_callee_result_id*/ 206,
+      /*input_id_to_fresh_id*/ {},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) {
+  // An access chain result is a pointer, but it cannot be passed as a function
+  // parameter, as it is not a memory object.
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Function %7
+          %9 = OpTypePointer Function %6
+         %18 = OpTypeInt 32 0
+         %19 = OpConstant %18 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %10 = OpVariable %8 Function
+               OpBranch %11
+         %11 = OpLabel
+         %12 = OpAccessChain %9 %10 %19
+               OpBranch %13
+         %13 = OpLabel
+         %14 = OpLoad %6 %12
+               OpBranch %15
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 13,
+      /*exit_block*/ 15,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 204,
+      /*new_caller_result_id*/ 205,
+      /*new_callee_result_id*/ 206,
+      /*input_id_to_fresh_id*/ {{12, 207}},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, Miscellaneous1) {
+  // This tests outlining of some non-trivial code.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %85
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %28 "buf"
+               OpMemberName %28 0 "u1"
+               OpMemberName %28 1 "u2"
+               OpName %30 ""
+               OpName %85 "color"
+               OpMemberDecorate %28 0 Offset 0
+               OpMemberDecorate %28 1 Offset 4
+               OpDecorate %28 Block
+               OpDecorate %30 DescriptorSet 0
+               OpDecorate %30 Binding 0
+               OpDecorate %85 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstant %6 3
+         %13 = OpConstant %6 4
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %28 = OpTypeStruct %6 %6
+         %29 = OpTypePointer Uniform %28
+         %30 = OpVariable %29 Uniform
+         %31 = OpTypePointer Uniform %6
+         %35 = OpTypeBool
+         %39 = OpConstant %15 1
+         %84 = OpTypePointer Output %7
+         %85 = OpVariable %84 Output
+        %114 = OpConstant %15 8
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+        %103 = OpPhi %15 %18 %5 %106 %43
+        %102 = OpPhi %7 %14 %5 %107 %43
+        %101 = OpPhi %15 %18 %5 %40 %43
+         %32 = OpAccessChain %31 %30 %18
+         %33 = OpLoad %6 %32
+         %34 = OpConvertFToS %15 %33
+         %36 = OpSLessThan %35 %101 %34
+               OpLoopMerge %24 %43 None
+               OpBranchConditional %36 %23 %24
+         %23 = OpLabel
+         %40 = OpIAdd %15 %101 %39
+               OpBranch %150
+        %150 = OpLabel
+               OpBranch %41
+         %41 = OpLabel
+        %107 = OpPhi %7 %102 %150 %111 %65
+        %106 = OpPhi %15 %103 %150 %110 %65
+        %104 = OpPhi %15 %40 %150 %81 %65
+         %47 = OpAccessChain %31 %30 %39
+         %48 = OpLoad %6 %47
+         %49 = OpConvertFToS %15 %48
+         %50 = OpSLessThan %35 %104 %49
+               OpLoopMerge %1000 %65 None
+               OpBranchConditional %50 %42 %1000
+         %42 = OpLabel
+         %60 = OpIAdd %15 %106 %114
+         %63 = OpSGreaterThan %35 %104 %60
+               OpBranchConditional %63 %64 %65
+         %64 = OpLabel
+         %71 = OpCompositeExtract %6 %107 0
+         %72 = OpFAdd %6 %71 %11
+         %97 = OpCompositeInsert %7 %72 %107 0
+         %76 = OpCompositeExtract %6 %107 3
+         %77 = OpConvertFToS %15 %76
+         %79 = OpIAdd %15 %60 %77
+               OpBranch %65
+         %65 = OpLabel
+        %111 = OpPhi %7 %107 %42 %97 %64
+        %110 = OpPhi %15 %60 %42 %79 %64
+         %81 = OpIAdd %15 %104 %39
+               OpBranch %41
+       %1000 = OpLabel
+               OpBranch %1001
+       %1001 = OpLabel
+               OpBranch %43
+         %43 = OpLabel
+               OpBranch %22
+         %24 = OpLabel
+         %87 = OpCompositeExtract %6 %102 0
+         %91 = OpConvertSToF %6 %103
+         %92 = OpCompositeConstruct %7 %87 %11 %91 %10
+               OpStore %85 %92
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 150,
+      /*exit_block*/ 1001,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}},
+      /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %85
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %28 "buf"
+               OpMemberName %28 0 "u1"
+               OpMemberName %28 1 "u2"
+               OpName %30 ""
+               OpName %85 "color"
+               OpMemberDecorate %28 0 Offset 0
+               OpMemberDecorate %28 1 Offset 4
+               OpDecorate %28 Block
+               OpDecorate %30 DescriptorSet 0
+               OpDecorate %30 Binding 0
+               OpDecorate %85 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstant %6 3
+         %13 = OpConstant %6 4
+         %14 = OpConstantComposite %7 %10 %11 %12 %13
+         %15 = OpTypeInt 32 1
+         %18 = OpConstant %15 0
+         %28 = OpTypeStruct %6 %6
+         %29 = OpTypePointer Uniform %28
+         %30 = OpVariable %29 Uniform
+         %31 = OpTypePointer Uniform %6
+         %35 = OpTypeBool
+         %39 = OpConstant %15 1
+         %84 = OpTypePointer Output %7
+         %85 = OpVariable %84 Output
+        %114 = OpConstant %15 8
+        %200 = OpTypeStruct %7 %15
+        %201 = OpTypeFunction %200 %15 %7 %15
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %22
+         %22 = OpLabel
+        %103 = OpPhi %15 %18 %5 %106 %43
+        %102 = OpPhi %7 %14 %5 %107 %43
+        %101 = OpPhi %15 %18 %5 %40 %43
+         %32 = OpAccessChain %31 %30 %18
+         %33 = OpLoad %6 %32
+         %34 = OpConvertFToS %15 %33
+         %36 = OpSLessThan %35 %101 %34
+               OpLoopMerge %24 %43 None
+               OpBranchConditional %36 %23 %24
+         %23 = OpLabel
+         %40 = OpIAdd %15 %101 %39
+               OpBranch %150
+        %150 = OpLabel
+        %204 = OpFunctionCall %200 %202 %103 %102 %40
+        %107 = OpCompositeExtract %7 %204 0
+        %106 = OpCompositeExtract %15 %204 1
+               OpBranch %43
+         %43 = OpLabel
+               OpBranch %22
+         %24 = OpLabel
+         %87 = OpCompositeExtract %6 %102 0
+         %91 = OpConvertSToF %6 %103
+         %92 = OpCompositeConstruct %7 %87 %11 %91 %10
+               OpStore %85 %92
+               OpReturn
+               OpFunctionEnd
+        %202 = OpFunction %200 None %201
+        %301 = OpFunctionParameter %15
+        %300 = OpFunctionParameter %7
+        %302 = OpFunctionParameter %15
+        %203 = OpLabel
+               OpBranch %41
+         %41 = OpLabel
+        %401 = OpPhi %7 %300 %203 %111 %65
+        %400 = OpPhi %15 %301 %203 %110 %65
+        %104 = OpPhi %15 %302 %203 %81 %65
+         %47 = OpAccessChain %31 %30 %39
+         %48 = OpLoad %6 %47
+         %49 = OpConvertFToS %15 %48
+         %50 = OpSLessThan %35 %104 %49
+               OpLoopMerge %1000 %65 None
+               OpBranchConditional %50 %42 %1000
+         %42 = OpLabel
+         %60 = OpIAdd %15 %400 %114
+         %63 = OpSGreaterThan %35 %104 %60
+               OpBranchConditional %63 %64 %65
+         %64 = OpLabel
+         %71 = OpCompositeExtract %6 %401 0
+         %72 = OpFAdd %6 %71 %11
+         %97 = OpCompositeInsert %7 %72 %401 0
+         %76 = OpCompositeExtract %6 %401 3
+         %77 = OpConvertFToS %15 %76
+         %79 = OpIAdd %15 %60 %77
+               OpBranch %65
+         %65 = OpLabel
+        %111 = OpPhi %7 %401 %42 %97 %64
+        %110 = OpPhi %15 %60 %42 %79 %64
+         %81 = OpIAdd %15 %104 %39
+               OpBranch %41
+       %1000 = OpLabel
+               OpBranch %1001
+       %1001 = OpLabel
+        %205 = OpCompositeConstruct %200 %401 %400
+               OpReturnValue %205
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationOutlineFunctionTest, Miscellaneous2) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %21 = OpTypeBool
+        %167 = OpConstantTrue %21
+        %168 = OpConstantFalse %21
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %34
+         %34 = OpLabel
+               OpLoopMerge %36 %37 None
+               OpBranchConditional %168 %37 %38
+         %38 = OpLabel
+               OpBranchConditional %168 %37 %36
+         %37 = OpLabel
+               OpBranch %34
+         %36 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 38,
+      /*exit_block*/ 36,
+      /*new_function_struct_return_type_id*/ 200,
+      /*new_function_type_id*/ 201,
+      /*new_function_id*/ 202,
+      /*new_function_region_entry_block*/ 203,
+      /*new_caller_result_id*/ 204,
+      /*new_callee_result_id*/ 205,
+      /*input_id_to_fresh_id*/ {},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
+}
+
+TEST(TransformationOutlineFunctionTest, Miscellaneous3) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %6 "main"
+               OpExecutionMode %6 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %21 = OpTypeBool
+        %167 = OpConstantTrue %21
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+               OpBranch %80
+         %80 = OpLabel
+               OpBranch %14
+         %14 = OpLabel
+               OpLoopMerge %16 %17 None
+               OpBranch %18
+         %18 = OpLabel
+               OpBranchConditional %167 %15 %16
+         %15 = OpLabel
+               OpBranch %17
+         %16 = OpLabel
+               OpBranch %81
+         %81 = OpLabel
+               OpReturn
+         %17 = OpLabel
+               OpBranch %14
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationOutlineFunction transformation(
+      /*entry_block*/ 80,
+      /*exit_block*/ 81,
+      /*new_function_struct_return_type_id*/ 300,
+      /*new_function_type_id*/ 301,
+      /*new_function_id*/ 302,
+      /*new_function_region_entry_block*/ 304,
+      /*new_caller_result_id*/ 305,
+      /*new_callee_result_id*/ 306,
+      /*input_id_to_fresh_id*/ {},
+      /*output_id_to_fresh_id*/ {});
+
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+  transformation.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %6 "main"
+               OpExecutionMode %6 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %21 = OpTypeBool
+        %167 = OpConstantTrue %21
+          %6 = OpFunction %2 None %3
+          %7 = OpLabel
+               OpBranch %80
+         %80 = OpLabel
+        %305 = OpFunctionCall %2 %302
+               OpReturn
+               OpFunctionEnd
+        %302 = OpFunction %2 None %3
+        %304 = OpLabel
+               OpBranch %14
+         %14 = OpLabel
+               OpLoopMerge %16 %17 None
+               OpBranch %18
+         %18 = OpLabel
+               OpBranchConditional %167 %15 %16
+         %15 = OpLabel
+               OpBranch %17
+         %16 = OpLabel
+               OpBranch %81
+         %81 = OpLabel
+               OpReturn
+         %17 = OpLabel
+               OpBranch %14
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index 75e117a..41b6116 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -159,18 +159,20 @@
          %65 = OpAccessChain %13 %11 %64
          %66 = OpLoad %6 %65
          %67 = OpSGreaterThan %29 %84 %66
-               OpSelectionMerge %69 None
+               OpSelectionMerge %1000 None
                OpBranchConditional %67 %68 %72
          %68 = OpLabel
          %71 = OpIAdd %6 %84 %26
-               OpBranch %69
+               OpBranch %1000
          %72 = OpLabel
          %74 = OpIAdd %6 %84 %64
         %205 = OpCopyObject %6 %74
-               OpBranch %69
-         %69 = OpLabel
+               OpBranch %1000
+       %1000 = OpLabel
          %86 = OpPhi %6 %71 %68 %74 %72
         %301 = OpPhi %6 %71 %68 %15 %72
+               OpBranch %69
+         %69 = OpLabel
                OpBranch %20
          %22 = OpLabel
          %75 = OpAccessChain %46 %42 %50
@@ -421,18 +423,20 @@
          %65 = OpAccessChain %13 %11 %64
          %66 = OpLoad %6 %65
          %67 = OpSGreaterThan %29 %84 %66
-               OpSelectionMerge %69 None
+               OpSelectionMerge %1000 None
                OpBranchConditional %67 %68 %72
          %68 = OpLabel
          %71 = OpIAdd %6 %84 %26
-               OpBranch %69
+               OpBranch %1000
          %72 = OpLabel
          %74 = OpIAdd %6 %84 %64
         %205 = OpCopyObject %6 %74
-               OpBranch %69
-         %69 = OpLabel
+               OpBranch %1000
+       %1000 = OpLabel
          %86 = OpPhi %6 %71 %68 %205 %72
         %301 = OpPhi %6 %71 %68 %15 %72
+               OpBranch %69
+         %69 = OpLabel
                OpBranch %20
          %22 = OpLabel
          %75 = OpAccessChain %46 %42 %50
diff --git a/test/fuzzers/spvtools_as_fuzzer.cpp b/test/fuzzers/spvtools_as_fuzzer.cpp
index 1b1de00..8cecb05 100644
--- a/test/fuzzers/spvtools_as_fuzzer.cpp
+++ b/test/fuzzers/spvtools_as_fuzzer.cpp
@@ -36,7 +36,7 @@
   input_str.resize(char_count);
   memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t));
 
-  spv_binary binary;
+  spv_binary binary = nullptr;
   spv_diagnostic diagnostic = nullptr;
   spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
                              SPV_TEXT_TO_BINARY_OPTION_NONE, &binary,
@@ -66,5 +66,7 @@
     binary = nullptr;
   }
 
+  spvContextDestroy(context);
+
   return 0;
 }
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index 9e5197d..e7303ba 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -5932,7 +5932,6 @@
 %42 = OpLabel
 %43 = OpLoad %int %i
 %44 = OpSLessThan %bool %43 %int_1
-OpSelectionMerge %45 None
 OpBranchConditional %44 %46 %40
 %46 = OpLabel
 %47 = OpLoad %int %i
diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp
index f87fd80..11fba73 100644
--- a/test/opt/block_merge_test.cpp
+++ b/test/opt/block_merge_test.cpp
@@ -447,10 +447,53 @@
 OpLoopMerge %merge %continue None
 OpBranch %inner_header
 %inner_header = OpLabel
-OpSelectionMerge %continue None
-OpBranchConditional %true %then %continue
+OpSelectionMerge %if_merge None
+OpBranchConditional %true %then %if_merge
 %then = OpLabel
 OpBranch %continue
+%if_merge = OpLabel
+OpBranch %continue
+%continue = OpLabel
+OpBranchConditional %false %merge %header
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<BlockMergePass>(text, true);
+}
+
+TEST_F(BlockMergeTest, CannotMergeContinue) {
+  const std::string text = R"(
+; CHECK: OpBranch [[loop_header:%\w+]]
+; CHECK: [[loop_header]] = OpLabel
+; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue:%\w+]]
+; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_header:%\w+]]
+; CHECK: [[if_header]] = OpLabel
+; CHECK-NEXT: OpSelectionMerge
+; CHECK: [[continue]] = OpLabel
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %func "func"
+OpExecutionMode %func OriginUpperLeft
+%void = OpTypeVoid
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%false = OpConstantFalse  %bool
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%entry = OpLabel
+OpBranch %header
+%header = OpLabel
+OpLoopMerge %merge %continue None
+OpBranchConditional %true %inner_header %merge
+%inner_header = OpLabel
+OpSelectionMerge %if_merge None
+OpBranchConditional %true %then %if_merge
+%then = OpLabel
+OpBranch %continue
+%if_merge = OpLabel
+OpBranch %continue
 %continue = OpLabel
 OpBranchConditional %false %merge %header
 %merge = OpLabel
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp
index 3ac8009..c138154 100644
--- a/test/opt/convert_relaxed_to_half_test.cpp
+++ b/test/opt/convert_relaxed_to_half_test.cpp
@@ -997,7 +997,7 @@
 %99 = OpFConvert %v4half %15
 OpBranch %65
 %65 = OpLabel
-%96 = OpPhi %v4half %99 %5 %76 %71
+%96 = OpPhi %v4half %99 %5 %100 %71
 %95 = OpPhi %int %int_0 %5 %78 %71
 %70 = OpSLessThan %bool %95 %int_10
 OpLoopMerge %66 %71 None
@@ -1222,6 +1222,115 @@
                                            defs_after + func_after, true, true);
 }
 
+TEST_F(ConvertToHalfTest, ConvertToHalfWithClosure) {
+  // Include as many contiguous composite instructions as possible into
+  // half-precision computations
+  //
+  // Compiled with glslang -V -Os
+  //
+  // clang-format off
+  //
+  // #version 410 core
+  //
+  //   precision mediump float;
+  //
+  // layout(location = 1) in vec3 foo;
+  // layout(location = 2) in mat2 bar;
+  // layout(location = 1) out vec3 res;
+  //
+  // vec3 func(vec3 tap, mat2 M) {
+  //   return vec3(M * tap.xy, 1.0);
+  // }
+  //
+  // void main() {
+  //   res = func(foo, bar);
+  // }
+  //
+  // clang-format on
+
+  const std::string defs =
+      R"(OpCapability Shader
+; CHECK: OpCapability Float16
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %res %foo %bar
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 410
+OpName %main "main"
+OpName %res "res"
+OpName %foo "foo"
+OpName %bar "bar"
+OpDecorate %res RelaxedPrecision
+; CHECK-NOT: OpDecorate %res RelaxedPrecision
+OpDecorate %res Location 1
+OpDecorate %foo RelaxedPrecision
+; CHECK-NOT: OpDecorate %foo RelaxedPrecision
+OpDecorate %foo Location 1
+OpDecorate %bar RelaxedPrecision
+; CHECK-NOT: OpDecorate %bar RelaxedPrecision
+OpDecorate %bar Location 2
+OpDecorate %34 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %41 RelaxedPrecision
+OpDecorate %42 RelaxedPrecision
+; CHECK-NOT: OpDecorate %34 RelaxedPrecision
+; CHECK-NOT: OpDecorate %36 RelaxedPrecision
+; CHECK-NOT: OpDecorate %41 RelaxedPrecision
+; CHECK-NOT: OpDecorate %42 RelaxedPrecision
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v3float = OpTypeVector %float 3
+%v2float = OpTypeVector %float 2
+%mat2v2float = OpTypeMatrix %v2float 2
+%float_1 = OpConstant %float 1
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%res = OpVariable %_ptr_Output_v3float Output
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+%foo = OpVariable %_ptr_Input_v3float Input
+%_ptr_Input_mat2v2float = OpTypePointer Input %mat2v2float
+%bar = OpVariable %_ptr_Input_mat2v2float Input
+)";
+
+  const std::string func =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%34 = OpLoad %v3float %foo
+%36 = OpLoad %mat2v2float %bar
+; CHECK: %48 = OpFConvert %v3half %34
+; CHECK: %49 = OpFConvert %v3half %34
+%41 = OpVectorShuffle %v2float %34 %34 0 1
+; CHECK-NOT: %41 = OpVectorShuffle %v2float %34 %34 0 1
+; CHECK: %41 = OpVectorShuffle %v2half %48 %49 0 1
+%42 = OpMatrixTimesVector %v2float %36 %41
+; CHECK-NOT: %42 = OpMatrixTimesVector %v2float %36 %41
+; CHECK: %55 = OpCompositeExtract %v2float %36 0
+; CHECK: %56 = OpFConvert %v2half %55
+; CHECK: %57 = OpCompositeExtract %v2float %36 1
+; CHECK: %58 = OpFConvert %v2half %57
+; CHECK: %59 = OpCompositeConstruct %mat2v2half %56 %58
+; CHECK: %52 = OpCopyObject %mat2v2float %36
+; CHECK: %42 = OpMatrixTimesVector %v2half %59 %41
+%43 = OpCompositeExtract %float %42 0
+%44 = OpCompositeExtract %float %42 1
+; CHECK-NOT: %43 = OpCompositeExtract %float %42 0
+; CHECK-NOT: %44 = OpCompositeExtract %float %42 1
+; CHECK: %43 = OpCompositeExtract %half %42 0
+; CHECK: %44 = OpCompositeExtract %half %42 1
+%45 = OpCompositeConstruct %v3float %43 %44 %float_1
+; CHECK-NOT: %45 = OpCompositeConstruct %v3float %43 %44 %float_1
+; CHECK: %53 = OpFConvert %float %43
+; CHECK: %54 = OpFConvert %float %44
+; CHECK: %45 = OpCompositeConstruct %v3float %53 %54 %float_1
+OpStore %res %45
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<ConvertToHalfPass>(defs + func, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp
index 5d41bdc..e612867 100644
--- a/test/opt/dead_branch_elim_test.cpp
+++ b/test/opt/dead_branch_elim_test.cpp
@@ -2798,7 +2798,9 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
+  // The selection merge in the loop naming the continue target as merge is
+  // invalid, but handled by this pass so validation is disabled.
+  SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, false);
 }
 
 TEST_F(DeadBranchElimTest, SelectionMergeWithNestedLoop) {
@@ -2942,7 +2944,9 @@
 OpFunctionEnd
 )";
 
-  SinglePassRunAndMatch<DeadBranchElimPass>(body, true);
+  // The selection merge in the loop naming the continue target as merge is
+  // invalid, but handled by this pass so validation is disabled.
+  SinglePassRunAndMatch<DeadBranchElimPass>(body, false);
 }
 
 TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) {
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index 68a7d18..26d1220 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -3396,7 +3396,17 @@
             "%2 = OpCompositeConstruct %v2int %103 %103\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        2, VEC2_0_ID)
+        2, VEC2_0_ID),
+    // Test case 5: Don't segfault when trying to fold an OpCompositeConstruct
+    // for an empty struct, and we reached the id limit.
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%empty_struct = OpTypeStruct\n" +
+            "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%4194303 = OpCompositeConstruct %empty_struct\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        4194303, 0)
 ));
 
 INSTANTIATE_TEST_SUITE_P(PhiFoldingTest, GeneralInstructionFoldingTest,
@@ -6111,6 +6121,23 @@
       "%4 = OpFSub %float %float_2 %3\n" +
       "OpReturn\n" +
       "OpFunctionEnd\n",
+    4, true),
+  // Test case 13: merge subtract of subtract with mixed types.
+  // 2 - (1 - x) = x + 1
+  InstructionFoldingCase<bool>(
+    Header() +
+      "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+      "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" +
+      "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
+      "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_int Function\n" +
+      "%2 = OpLoad %int %var\n" +
+      "%3 = OpISub %int %uint_1 %2\n" +
+      "%4 = OpISub %int %int_2 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
     4, true)
 ));
 
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp
index 58bd404..d38571e 100644
--- a/test/opt/graphics_robust_access_test.cpp
+++ b/test/opt/graphics_robust_access_test.cpp
@@ -271,7 +271,7 @@
        %uint_4 = OpConstant %uint 4
        )"
             << MainPrefix() << R"(
-       %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%uint_3")
+       %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%int_3")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
@@ -329,7 +329,7 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_3 = OpConstant %int 3
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -354,7 +354,7 @@
        ; CHECK-DAG: %short_3 = OpConstant %short 3
        ; CHECK-NOT: = OpTypeInt 32
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] UClamp %i %short_0 %short_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] SClamp %i %short_0 %short_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -375,11 +375,11 @@
             << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-NOT: = OpTypeInt 32
-       ; CHECK-DAG: %ushort_0 = OpConstant %ushort 0
-       ; CHECK-DAG: %ushort_3 = OpConstant %ushort 3
+       ; CHECK-DAG: %short_0 = OpConstant %short 0
+       ; CHECK-DAG: %short_3 = OpConstant %short 3
        ; CHECK-NOT: = OpTypeInt 32
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] UClamp %i %ushort_0 %ushort_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %short_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -404,7 +404,7 @@
        ; CHECK-DAG: %long_3 = OpConstant %long 3
        ; CHECK-NOT: = OpTypeInt 32
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -425,11 +425,11 @@
             << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-NOT: = OpTypeInt 32
-       ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
-       ; CHECK-DAG: %ulong_3 = OpConstant %ulong 3
+       ; CHECK-DAG: %long_0 = OpConstant %long 0
+       ; CHECK-DAG: %long_3 = OpConstant %long 3
        ; CHECK-NOT: = OpTypeInt 32
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_3
        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
             << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -486,10 +486,9 @@
        %uint_1 = OpConstant %uint 1
        %uint_4 = OpConstant %uint 4
        )" << MainPrefix() << R"(
-       ; CHECK: %uint_3 = OpConstant %uint 3
+       ; CHECK: %int_3 = OpConstant %int 3
        %var = OpVariable %var_ty Function)"
-            << ACCheck(ac, "%uint_4 %uint_1", "%uint_3 %uint_1")
-            << MainSuffix();
+            << ACCheck(ac, "%uint_4 %uint_1", "%int_3 %uint_1") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
 }
@@ -529,7 +528,7 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_3 = OpConstant %int 3
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i %uint_1", "%[[clamp]] %uint_1") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -585,7 +584,7 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_199 = OpConstant %int 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -606,11 +605,11 @@
        %i = OpUndef %short
        )" << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK-DAG: %uint_0 = OpConstant %uint 0
-       ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999
+       ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %int_69999 = OpConstant %int 69999
        ; CHECK: OpLabel
        ; CHECK: %[[i_ext:\w+]] = OpSConvert %uint %i
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -631,11 +630,11 @@
        %i = OpUndef %ushort
        )" << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK-DAG: %uint_0 = OpConstant %uint 0
-       ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999
+       ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %int_69999 = OpConstant %int 69999
        ; CHECK: OpLabel
        ; CHECK: %[[i_ext:\w+]] = OpUConvert %uint %i
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -656,10 +655,10 @@
        %i = OpUndef %uint
        )" << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK-DAG: %uint_0 = OpConstant %uint 0
-       ; CHECK-DAG: %uint_199 = OpConstant %uint 199
+       ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %int_199 = OpConstant %int 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %uint_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %int_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -683,7 +682,7 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_199 = OpConstant %int 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -707,7 +706,7 @@
        ; CHECK-DAG: %long_0 = OpConstant %long 0
        ; CHECK-DAG: %long_199 = OpConstant %long 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -728,16 +727,91 @@
        %i = OpUndef %ulong
        )" << MainPrefix() << R"(
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
-       ; CHECK-DAG: %ulong_199 = OpConstant %ulong 199
+       ; CHECK-DAG: %long_0 = OpConstant %long 0
+       ; CHECK-DAG: %long_199 = OpConstant %long 199
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_199
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_199
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
   }
 }
 
+TEST_F(GraphicsRobustAccessTest,
+       ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax) {
+  for (auto* ac : AccessChains()) {
+    std::ostringstream shaders;
+    shaders << "OpCapability Int16\n"
+            << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort()
+            << TypesInt() << TypesFloat() << R"(
+       %uint_50000 = OpConstant %uint 50000
+       %arr = OpTypeArray %float %uint_50000
+       %var_ty = OpTypePointer Function %arr
+       %ptr_ty = OpTypePointer Function %float
+       %i = OpUndef %ushort
+       )" << MainPrefix() << R"(
+       ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
+       ; CHECK-DAG: %short_0 = OpConstant %short 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %short 32767
+       ; CHECK: OpLabel
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %[[intmax]]
+       %var = OpVariable %var_ty Function)"
+            << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
+    SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
+  }
+}
+
+TEST_F(GraphicsRobustAccessTest,
+       ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax) {
+  for (auto* ac : AccessChains()) {
+    std::ostringstream shaders;
+    shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
+            << TypesFloat() << R"(
+       %uint_3000000000 = OpConstant %uint 3000000000
+       %arr = OpTypeArray %float %uint_3000000000
+       %var_ty = OpTypePointer Function %arr
+       %ptr_ty = OpTypePointer Function %float
+       %i = OpUndef %uint
+       )" << MainPrefix() << R"(
+       ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
+       ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
+       ; CHECK: OpLabel
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %[[intmax]]
+       %var = OpVariable %var_ty Function)"
+            << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
+    SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
+  }
+}
+
+TEST_F(GraphicsRobustAccessTest,
+       ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax) {
+  for (auto* ac : AccessChains()) {
+    std::ostringstream shaders;
+    shaders << "OpCapability Int64\n"
+            << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
+            << TypesLong()
+            << TypesFloat()
+            // 2^63 == 9,223,372,036,854,775,807
+            << R"(
+       %ulong_9223372036854775999 = OpConstant %ulong 9223372036854775999
+       %arr = OpTypeArray %float %ulong_9223372036854775999
+       %var_ty = OpTypePointer Function %arr
+       %ptr_ty = OpTypePointer Function %float
+       %i = OpUndef %ulong
+       )"
+            << MainPrefix() << R"(
+       ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
+       ; CHECK-DAG: %long_0 = OpConstant %long 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %long 9223372036854775807
+       ; CHECK: OpLabel
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %[[intmax]]
+       %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
+            << MainSuffix();
+    SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
+  }
+}
+
 TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) {
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -753,9 +827,11 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
+       ; CHECK-DAG: %[[uint_intmax:\w+]] = OpConstant %uint 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[max:\w+]] = OpISub %uint %spec200 %uint_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %uint_5 %uint_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[uint_intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %uint_5 %uint_0 %[[smin]]
        %var = OpVariable %var_ty Function)"
             << ACCheck(ac, "%uint_5", "%[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -908,11 +984,13 @@
        %int_0 = OpConstant %int 0
        %int_2 = OpConstant %int 2
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
-       ; CHECK: %int_1 = OpConstant %int 1
+       ; CHECK-DAG: %int_1 = OpConstant %int 1
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %int_0 %int_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %int_0 %int_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%int_2 %int_0", "%int_2 %[[clamp]]")
             << MainSuffix();
@@ -937,11 +1015,13 @@
        ; CHECK: %uint = OpTypeInt 32 0
        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
        ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
             << MainSuffix();
@@ -966,11 +1046,13 @@
        ; CHECK: %uint = OpTypeInt 32 0
        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
        ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
             << MainSuffix();
@@ -993,10 +1075,12 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %int_1 = OpConstant %int 1
        ; CHECK-DAG: %int_0 = OpConstant %int 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
             << MainSuffix();
@@ -1019,10 +1103,12 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %uint_0 %[[smin]]
        )"
             << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
             << MainSuffix();
@@ -1046,11 +1132,13 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %long_0 = OpConstant %long 0
        ; CHECK-DAG: %long_1 = OpConstant %long 1
+       ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %long 9223372036854775807
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
        ; CHECK: %[[max:\w+]] = OpISub %long %[[arrlen_ext]] %long_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %long %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %[[smin]]
        )" << MainPrefix()
             << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -1073,11 +1161,13 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
        ; CHECK-DAG: %ulong_1 = OpConstant %ulong 1
+       ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %ulong 9223372036854775807
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
        ; CHECK: %[[max:\w+]] = OpISub %ulong %[[arrlen_ext]] %ulong_1
-       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
+       ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %ulong_0 %[[smin]]
        )" << MainPrefix()
             << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
@@ -1109,11 +1199,13 @@
        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_3 = OpConstant %int 3
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
-       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %int_3
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
+       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %int_3
        )" << MainPrefix()
             << ACCheck(ac, "%int_2 %i %int_1 %j",
                        "%int_2 %[[clamp_i]] %int_1 %[[clamp_j]]")
@@ -1148,6 +1240,7 @@
        ; CHECK-DAG: %[[ssbo_p:\w+]] = OpTypePointer Uniform %ssbo_s
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_9 = OpConstant %int 9
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
        ; This access chain is manufatured only so we can compute the array length.
        ; Note that the %int_9 is already clamped
@@ -1155,7 +1248,8 @@
             << R"( %[[ssbo_p]] %var %int_9
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %[[ssbo_base]] 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
        )" << MainPrefix()
             << ACCheck(ac, "%int_17 %int_2 %i %int_1 %int_2",
                        "%int_9 %int_2 %[[clamp_i]] %int_1 %int_2")
@@ -1195,8 +1289,9 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_9 = OpConstant %int 9
        ; CHECK-DAG: %int_3 = OpConstant %int 3
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9
+       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
        ; CHECK: %ac_ssbo = )" << ac
             << R"( %ssbo_pty %var %[[clamp_i]]
        ; CHECK: %ac_rtarr = )"
@@ -1207,8 +1302,9 @@
        ; definition to find the base pointer %ac_ssbo.
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]]
-       ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
+       ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
        ; CHECK: %ac = )" << ac
             << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
        ; CHECK-NOT: AccessChain
@@ -1258,8 +1354,9 @@
        ; CHECK-DAG: %int_0 = OpConstant %int 0
        ; CHECK-DAG: %int_9 = OpConstant %int 9
        ; CHECK-DAG: %int_3 = OpConstant %int 3
+       ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
        ; CHECK: OpLabel
-       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9
+       ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
        ; CHECK: %ac_ssbo = )" << ac
             << R"( %ssbo_pty %var %[[clamp_i]]
        ; CHECK: %bb1 = OpLabel
@@ -1272,8 +1369,9 @@
        ; definition to find the base pointer %ac_ssbo.
        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
-       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]]
-       ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3
+       ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
+       ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
+       ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
        ; CHECK: %ac = )" << ac
             << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
        ; CHECK-NOT: AccessChain
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index fac49ca..f44c04a 100644
--- a/test/opt/inline_test.cpp
+++ b/test/opt/inline_test.cpp
@@ -1819,7 +1819,7 @@
 %9 = OpLabel
 OpBranch %10
 %10 = OpLabel
-OpLoopMerge %12 %13 None
+OpLoopMerge %12 %10 None
 OpBranch %13
 %13 = OpLabel
 OpBranchConditional %true %10 %12
@@ -1980,7 +1980,7 @@
 OpBranch %13
 %13 = OpLabel
 %14 = OpCopyObject %bool %false
-OpLoopMerge %16 %19 None
+OpLoopMerge %16 %13 None
 OpBranch %17
 %17 = OpLabel
 %18 = OpCopyObject %bool %true
@@ -2145,7 +2145,7 @@
 %19 = OpLabel
 %20 = OpCopyObject %int %int_2
 %25 = OpCopyObject %int %int_0
-OpLoopMerge %23 %26 None
+OpLoopMerge %23 %19 None
 OpBranch %26
 %27 = OpLabel
 %28 = OpCopyObject %int %int_1
diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp
index b4db2d5..d867b01 100644
--- a/test/opt/inst_bindless_check_test.cpp
+++ b/test/opt/inst_bindless_check_test.cpp
@@ -14,7 +14,6 @@
 // limitations under the License.
 
 // Bindless Check Instrumentation Tests.
-// Tests ending with V2 use version 2 record format.
 
 #include <string>
 #include <vector>
@@ -29,235 +28,6 @@
 
 using InstBindlessTest = PassTest<::testing::Test>;
 
-TEST_F(InstBindlessTest, Simple) {
-  // Texture2D g_tColor[128];
-  //
-  // layout(push_constant) cbuffer PerViewConstantBuffer_t
-  // {
-  //   uint g_nDataIdx;
-  // };
-  //
-  // SamplerState g_sAniso;
-  //
-  // struct PS_INPUT
-  // {
-  //   float2 vTextureCoords : TEXCOORD2;
-  // };
-  //
-  // struct PS_OUTPUT
-  // {
-  //   float4 vColor : SV_Target0;
-  // };
-  //
-  // PS_OUTPUT MainPs(PS_INPUT i)
-  // {
-  //   PS_OUTPUT ps_output;
-  //   ps_output.vColor =
-  //       g_tColor[ g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy);
-  //   return ps_output;
-  // }
-
-  const std::string entry_before =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-)";
-
-  const std::string entry_after =
-      R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-)";
-
-  const std::string names_annots =
-      R"(OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-)";
-
-  const std::string new_annots =
-      R"(OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_55 Block
-OpMemberDecorate %_struct_55 0 Offset 0
-OpMemberDecorate %_struct_55 1 Offset 4
-OpDecorate %57 DescriptorSet 7
-OpDecorate %57 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-)";
-
-  const std::string consts_types_vars =
-      R"(%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%16 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_16_uint_128 = OpTypeArray %16 %uint_128
-%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
-%24 = OpTypeSampler
-%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24
-%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant
-%26 = OpTypeSampledImage %16
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string new_consts_types_vars =
-      R"(%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%48 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_55 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55
-%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_56 = OpConstant %uint 56
-%103 = OpConstantNull %v4float
-)";
-
-  const std::string func_pt1 =
-      R"(%MainPs = OpFunction %void None %10
-%29 = OpLabel
-%30 = OpLoad %v2float %i_vTextureCoords
-%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%32 = OpLoad %uint %31
-%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32
-%34 = OpLoad %16 %33
-%35 = OpLoad %24 %g_sAniso
-%36 = OpSampledImage %26 %34 %35
-)";
-
-  const std::string func_pt2_before =
-      R"(%37 = OpImageSampleImplicitLod %v4float %36 %30
-OpStore %_entryPointOutput_vColor %37
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_pt2_after =
-      R"(%40 = OpULessThan %bool %32 %uint_128
-OpSelectionMerge %41 None
-OpBranchConditional %40 %42 %43
-%42 = OpLabel
-%44 = OpLoad %16 %33
-%45 = OpSampledImage %26 %44 %35
-%46 = OpImageSampleImplicitLod %v4float %45 %30
-OpBranch %41
-%43 = OpLabel
-%102 = OpFunctionCall %void %47 %uint_56 %uint_0 %32 %uint_128
-OpBranch %41
-%41 = OpLabel
-%104 = OpPhi %v4float %46 %42 %103 %43
-OpStore %_entryPointOutput_vColor %104
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string output_func =
-      R"(%47 = OpFunction %void None %48
-%49 = OpFunctionParameter %uint
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpLabel
-%59 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0
-%62 = OpAtomicIAdd %uint %59 %uint_4 %uint_0 %uint_9
-%63 = OpIAdd %uint %62 %uint_9
-%64 = OpArrayLength %uint %57 1
-%65 = OpULessThanEqual %bool %63 %64
-OpSelectionMerge %66 None
-OpBranchConditional %65 %67 %66
-%67 = OpLabel
-%68 = OpIAdd %uint %62 %uint_0
-%70 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %68
-OpStore %70 %uint_9
-%72 = OpIAdd %uint %62 %uint_1
-%73 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %72
-OpStore %73 %uint_23
-%75 = OpIAdd %uint %62 %uint_2
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75
-OpStore %76 %49
-%78 = OpIAdd %uint %62 %uint_3
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %78
-OpStore %79 %uint_4
-%82 = OpLoad %v4float %gl_FragCoord
-%84 = OpBitcast %v4uint %82
-%85 = OpCompositeExtract %uint %84 0
-%86 = OpIAdd %uint %62 %uint_4
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %86
-OpStore %87 %85
-%88 = OpCompositeExtract %uint %84 1
-%90 = OpIAdd %uint %62 %uint_5
-%91 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %90
-OpStore %91 %88
-%93 = OpIAdd %uint %62 %uint_6
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %93
-OpStore %94 %50
-%96 = OpIAdd %uint %62 %uint_7
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %96
-OpStore %97 %51
-%99 = OpIAdd %uint %62 %uint_8
-%100 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %99
-OpStore %100 %52
-OpBranch %66
-%66 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      entry_before + names_annots + consts_types_vars + func_pt1 +
-          func_pt2_before,
-      entry_after + names_annots + new_annots + consts_types_vars +
-          new_consts_types_vars + func_pt1 + func_pt2_after + output_func,
-      true, true, 7u, 23u, false, false, 1u);
-}
-
 TEST_F(InstBindlessTest, NoInstrumentConstIndexInbounds) {
   // Texture2D g_tColor[128];
   //
@@ -336,1872 +106,7 @@
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(before, before, true, true, 7u,
-                                               23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentMultipleInstructions) {
-  // Texture2D g_tColor[128];
-  //
-  // layout(push_constant) cbuffer PerViewConstantBuffer_t
-  // {
-  //   uint g_nDataIdx;
-  //   uint g_nDataIdx2;
-  // };
-  //
-  // SamplerState g_sAniso;
-  //
-  // struct PS_INPUT
-  // {
-  //   float2 vTextureCoords : TEXCOORD2;
-  // };
-  //
-  // struct PS_OUTPUT
-  // {
-  //   float4 vColor : SV_Target0;
-  // };
-  //
-  // PS_OUTPUT MainPs(PS_INPUT i)
-  // {
-  //   PS_OUTPUT ps_output;
-  //
-  //   float t  = g_tColor[g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy);
-  //   float t2 = g_tColor[g_nDataIdx2].Sample(g_sAniso, i.vTextureCoords.xy);
-  //   ps_output.vColor = t + t2;
-  //   return ps_output;
-  // }
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%int_1 = OpConstant %int 1
-%17 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_17_uint_128 = OpTypeArray %17 %uint_128
-%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17
-%25 = OpTypeSampler
-%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25
-%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant
-%27 = OpTypeSampledImage %17
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_63 Block
-OpMemberDecorate %_struct_63 0 Offset 0
-OpMemberDecorate %_struct_63 1 Offset 4
-OpDecorate %65 DescriptorSet 7
-OpDecorate %65 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%int_1 = OpConstant %int 1
-%17 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_17_uint_128 = OpTypeArray %17 %uint_128
-%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17
-%25 = OpTypeSampler
-%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25
-%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant
-%27 = OpTypeSampledImage %17
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%56 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
-%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_58 = OpConstant %uint 58
-%111 = OpConstantNull %v4float
-%uint_64 = OpConstant %uint 64
-)";
-
-  const std::string func_before =
-      R"(%MainPs = OpFunction %void None %10
-%30 = OpLabel
-%31 = OpLoad %v2float %i_vTextureCoords
-%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%33 = OpLoad %uint %32
-%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33
-%35 = OpLoad %17 %34
-%36 = OpLoad %25 %g_sAniso
-%37 = OpSampledImage %27 %35 %36
-%38 = OpImageSampleImplicitLod %v4float %37 %31
-%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
-%40 = OpLoad %uint %39
-%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40
-%42 = OpLoad %17 %41
-%43 = OpSampledImage %27 %42 %36
-%44 = OpImageSampleImplicitLod %v4float %43 %31
-%45 = OpFAdd %v4float %38 %44
-OpStore %_entryPointOutput_vColor %45
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%MainPs = OpFunction %void None %10
-%30 = OpLabel
-%31 = OpLoad %v2float %i_vTextureCoords
-%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%33 = OpLoad %uint %32
-%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33
-%35 = OpLoad %17 %34
-%36 = OpLoad %25 %g_sAniso
-%37 = OpSampledImage %27 %35 %36
-%48 = OpULessThan %bool %33 %uint_128
-OpSelectionMerge %49 None
-OpBranchConditional %48 %50 %51
-%50 = OpLabel
-%52 = OpLoad %17 %34
-%53 = OpSampledImage %27 %52 %36
-%54 = OpImageSampleImplicitLod %v4float %53 %31
-OpBranch %49
-%51 = OpLabel
-%110 = OpFunctionCall %void %55 %uint_58 %uint_0 %33 %uint_128
-OpBranch %49
-%49 = OpLabel
-%112 = OpPhi %v4float %54 %50 %111 %51
-%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
-%40 = OpLoad %uint %39
-%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40
-%42 = OpLoad %17 %41
-%43 = OpSampledImage %27 %42 %36
-%113 = OpULessThan %bool %40 %uint_128
-OpSelectionMerge %114 None
-OpBranchConditional %113 %115 %116
-%115 = OpLabel
-%117 = OpLoad %17 %41
-%118 = OpSampledImage %27 %117 %36
-%119 = OpImageSampleImplicitLod %v4float %118 %31
-OpBranch %114
-%116 = OpLabel
-%121 = OpFunctionCall %void %55 %uint_64 %uint_0 %40 %uint_128
-OpBranch %114
-%114 = OpLabel
-%122 = OpPhi %v4float %119 %115 %111 %116
-%45 = OpFAdd %v4float %112 %122
-OpStore %_entryPointOutput_vColor %45
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string output_func =
-      R"(%55 = OpFunction %void None %56
-%57 = OpFunctionParameter %uint
-%58 = OpFunctionParameter %uint
-%59 = OpFunctionParameter %uint
-%60 = OpFunctionParameter %uint
-%61 = OpLabel
-%67 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
-%70 = OpAtomicIAdd %uint %67 %uint_4 %uint_0 %uint_9
-%71 = OpIAdd %uint %70 %uint_9
-%72 = OpArrayLength %uint %65 1
-%73 = OpULessThanEqual %bool %71 %72
-OpSelectionMerge %74 None
-OpBranchConditional %73 %75 %74
-%75 = OpLabel
-%76 = OpIAdd %uint %70 %uint_0
-%78 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %76
-OpStore %78 %uint_9
-%80 = OpIAdd %uint %70 %uint_1
-%81 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %80
-OpStore %81 %uint_23
-%83 = OpIAdd %uint %70 %uint_2
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %83
-OpStore %84 %57
-%86 = OpIAdd %uint %70 %uint_3
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %86
-OpStore %87 %uint_4
-%90 = OpLoad %v4float %gl_FragCoord
-%92 = OpBitcast %v4uint %90
-%93 = OpCompositeExtract %uint %92 0
-%94 = OpIAdd %uint %70 %uint_4
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
-OpStore %95 %93
-%96 = OpCompositeExtract %uint %92 1
-%98 = OpIAdd %uint %70 %uint_5
-%99 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %98
-OpStore %99 %96
-%101 = OpIAdd %uint %70 %uint_6
-%102 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %101
-OpStore %102 %58
-%104 = OpIAdd %uint %70 %uint_7
-%105 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %104
-OpStore %105 %59
-%107 = OpIAdd %uint %70 %uint_8
-%108 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %107
-OpStore %108 %60
-OpBranch %74
-%74 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentOpImage) {
-  // This test verifies that the pass will correctly instrument shader
-  // using OpImage. This test was created by editing the SPIR-V
-  // from the Simple test.
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpCapability StorageImageReadWithoutFormat
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%v2int = OpTypeVector %int 2
-%int_0 = OpConstant %int 0
-%20 = OpTypeImage %float 2D 0 0 0 0 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%39 = OpTypeSampledImage %20
-%_arr_39_uint_128 = OpTypeArray %39 %uint_128
-%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_39 = OpTypePointer UniformConstant %39
-%_ptr_Input_v2int = OpTypePointer Input %v2int
-%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpCapability StorageImageReadWithoutFormat
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_51 Block
-OpMemberDecorate %_struct_51 0 Offset 0
-OpMemberDecorate %_struct_51 1 Offset 4
-OpDecorate %53 DescriptorSet 7
-OpDecorate %53 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%v2int = OpTypeVector %int 2
-%int_0 = OpConstant %int 0
-%15 = OpTypeImage %float 2D 0 0 0 0 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%18 = OpTypeSampledImage %15
-%_arr_18_uint_128 = OpTypeArray %18 %uint_128
-%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
-%_ptr_Input_v2int = OpTypePointer Input %v2int
-%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%44 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_51 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_51 = OpTypePointer StorageBuffer %_struct_51
-%53 = OpVariable %_ptr_StorageBuffer__struct_51 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_51 = OpConstant %uint 51
-%99 = OpConstantNull %v4float
-)";
-
-  const std::string func_before =
-      R"(%MainPs = OpFunction %void None %3
-%5 = OpLabel
-%53 = OpLoad %v2int %i_vTextureCoords
-%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%64 = OpLoad %uint %63
-%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64
-%66 = OpLoad %39 %65
-%75 = OpImage %20 %66
-%71 = OpImageRead %v4float %75 %53
-OpStore %_entryPointOutput_vColor %71
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%MainPs = OpFunction %void None %9
-%26 = OpLabel
-%27 = OpLoad %v2int %i_vTextureCoords
-%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%29 = OpLoad %uint %28
-%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29
-%31 = OpLoad %18 %30
-%32 = OpImage %15 %31
-%36 = OpULessThan %bool %29 %uint_128
-OpSelectionMerge %37 None
-OpBranchConditional %36 %38 %39
-%38 = OpLabel
-%40 = OpLoad %18 %30
-%41 = OpImage %15 %40
-%42 = OpImageRead %v4float %41 %27
-OpBranch %37
-%39 = OpLabel
-%98 = OpFunctionCall %void %43 %uint_51 %uint_0 %29 %uint_128
-OpBranch %37
-%37 = OpLabel
-%100 = OpPhi %v4float %42 %38 %99 %39
-OpStore %_entryPointOutput_vColor %100
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string output_func =
-      R"(%43 = OpFunction %void None %44
-%45 = OpFunctionParameter %uint
-%46 = OpFunctionParameter %uint
-%47 = OpFunctionParameter %uint
-%48 = OpFunctionParameter %uint
-%49 = OpLabel
-%55 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_0
-%58 = OpAtomicIAdd %uint %55 %uint_4 %uint_0 %uint_9
-%59 = OpIAdd %uint %58 %uint_9
-%60 = OpArrayLength %uint %53 1
-%61 = OpULessThanEqual %bool %59 %60
-OpSelectionMerge %62 None
-OpBranchConditional %61 %63 %62
-%63 = OpLabel
-%64 = OpIAdd %uint %58 %uint_0
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %64
-OpStore %66 %uint_9
-%68 = OpIAdd %uint %58 %uint_1
-%69 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %68
-OpStore %69 %uint_23
-%71 = OpIAdd %uint %58 %uint_2
-%72 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %71
-OpStore %72 %45
-%74 = OpIAdd %uint %58 %uint_3
-%75 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %74
-OpStore %75 %uint_4
-%78 = OpLoad %v4float %gl_FragCoord
-%80 = OpBitcast %v4uint %78
-%81 = OpCompositeExtract %uint %80 0
-%82 = OpIAdd %uint %58 %uint_4
-%83 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %82
-OpStore %83 %81
-%84 = OpCompositeExtract %uint %80 1
-%86 = OpIAdd %uint %58 %uint_5
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %86
-OpStore %87 %84
-%89 = OpIAdd %uint %58 %uint_6
-%90 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %89
-OpStore %90 %46
-%92 = OpIAdd %uint %58 %uint_7
-%93 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %92
-OpStore %93 %47
-%95 = OpIAdd %uint %58 %uint_8
-%96 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %95
-OpStore %96 %48
-OpBranch %62
-%62 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentSampledImage) {
-  // This test verifies that the pass will correctly instrument shader
-  // using sampled image. This test was created by editing the SPIR-V
-  // from the Simple test.
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%20 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%39 = OpTypeSampledImage %20
-%_arr_39_uint_128 = OpTypeArray %39 %uint_128
-%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_39 = OpTypePointer UniformConstant %39
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_49 Block
-OpMemberDecorate %_struct_49 0 Offset 0
-OpMemberDecorate %_struct_49 1 Offset 4
-OpDecorate %51 DescriptorSet 7
-OpDecorate %51 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%15 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%18 = OpTypeSampledImage %15
-%_arr_18_uint_128 = OpTypeArray %18 %uint_128
-%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%42 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_49 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_49 = OpTypePointer StorageBuffer %_struct_49
-%51 = OpVariable %_ptr_StorageBuffer__struct_49 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_49 = OpConstant %uint 49
-%97 = OpConstantNull %v4float
-)";
-
-  const std::string func_before =
-      R"(%MainPs = OpFunction %void None %3
-%5 = OpLabel
-%53 = OpLoad %v2float %i_vTextureCoords
-%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%64 = OpLoad %uint %63
-%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64
-%66 = OpLoad %39 %65
-%71 = OpImageSampleImplicitLod %v4float %66 %53
-OpStore %_entryPointOutput_vColor %71
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%MainPs = OpFunction %void None %9
-%26 = OpLabel
-%27 = OpLoad %v2float %i_vTextureCoords
-%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%29 = OpLoad %uint %28
-%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29
-%31 = OpLoad %18 %30
-%35 = OpULessThan %bool %29 %uint_128
-OpSelectionMerge %36 None
-OpBranchConditional %35 %37 %38
-%37 = OpLabel
-%39 = OpLoad %18 %30
-%40 = OpImageSampleImplicitLod %v4float %39 %27
-OpBranch %36
-%38 = OpLabel
-%96 = OpFunctionCall %void %41 %uint_49 %uint_0 %29 %uint_128
-OpBranch %36
-%36 = OpLabel
-%98 = OpPhi %v4float %40 %37 %97 %38
-OpStore %_entryPointOutput_vColor %98
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string output_func =
-      R"(%41 = OpFunction %void None %42
-%43 = OpFunctionParameter %uint
-%44 = OpFunctionParameter %uint
-%45 = OpFunctionParameter %uint
-%46 = OpFunctionParameter %uint
-%47 = OpLabel
-%53 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_0
-%56 = OpAtomicIAdd %uint %53 %uint_4 %uint_0 %uint_9
-%57 = OpIAdd %uint %56 %uint_9
-%58 = OpArrayLength %uint %51 1
-%59 = OpULessThanEqual %bool %57 %58
-OpSelectionMerge %60 None
-OpBranchConditional %59 %61 %60
-%61 = OpLabel
-%62 = OpIAdd %uint %56 %uint_0
-%64 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %62
-OpStore %64 %uint_9
-%66 = OpIAdd %uint %56 %uint_1
-%67 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %66
-OpStore %67 %uint_23
-%69 = OpIAdd %uint %56 %uint_2
-%70 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %69
-OpStore %70 %43
-%72 = OpIAdd %uint %56 %uint_3
-%73 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %72
-OpStore %73 %uint_4
-%76 = OpLoad %v4float %gl_FragCoord
-%78 = OpBitcast %v4uint %76
-%79 = OpCompositeExtract %uint %78 0
-%80 = OpIAdd %uint %56 %uint_4
-%81 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %80
-OpStore %81 %79
-%82 = OpCompositeExtract %uint %78 1
-%84 = OpIAdd %uint %56 %uint_5
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %84
-OpStore %85 %82
-%87 = OpIAdd %uint %56 %uint_6
-%88 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %87
-OpStore %88 %44
-%90 = OpIAdd %uint %56 %uint_7
-%91 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %90
-OpStore %91 %45
-%93 = OpIAdd %uint %56 %uint_8
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %93
-OpStore %94 %46
-OpBranch %60
-%60 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentImageWrite) {
-  // This test verifies that the pass will correctly instrument shader
-  // doing bindless image write. This test was created by editing the SPIR-V
-  // from the Simple test.
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpCapability StorageImageWriteWithoutFormat
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%v2int = OpTypeVector %int 2
-%int_0 = OpConstant %int 0
-%20 = OpTypeImage %float 2D 0 0 0 0 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%80 = OpConstantNull %v4float
-%_arr_20_uint_128 = OpTypeArray %20 %uint_128
-%_ptr_UniformConstant__arr_20_uint_128 = OpTypePointer UniformConstant %_arr_20_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_20_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
-%_ptr_Input_v2int = OpTypePointer Input %v2int
-%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpCapability StorageImageWriteWithoutFormat
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_48 Block
-OpMemberDecorate %_struct_48 0 Offset 0
-OpMemberDecorate %_struct_48 1 Offset 4
-OpDecorate %50 DescriptorSet 7
-OpDecorate %50 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%v2int = OpTypeVector %int 2
-%int_0 = OpConstant %int 0
-%16 = OpTypeImage %float 2D 0 0 0 0 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%19 = OpConstantNull %v4float
-%_arr_16_uint_128 = OpTypeArray %16 %uint_128
-%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
-%_ptr_Input_v2int = OpTypePointer Input %v2int
-%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%41 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_48 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_48 = OpTypePointer StorageBuffer %_struct_48
-%50 = OpVariable %_ptr_StorageBuffer__struct_48 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_51 = OpConstant %uint 51
-)";
-
-  const std::string func_before =
-      R"(%MainPs = OpFunction %void None %3
-%5 = OpLabel
-%53 = OpLoad %v2int %i_vTextureCoords
-%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%64 = OpLoad %uint %63
-%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64
-%66 = OpLoad %20 %65
-OpImageWrite %66 %53 %80
-OpStore %_entryPointOutput_vColor %80
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%MainPs = OpFunction %void None %9
-%27 = OpLabel
-%28 = OpLoad %v2int %i_vTextureCoords
-%29 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%30 = OpLoad %uint %29
-%31 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %30
-%32 = OpLoad %16 %31
-%35 = OpULessThan %bool %30 %uint_128
-OpSelectionMerge %36 None
-OpBranchConditional %35 %37 %38
-%37 = OpLabel
-%39 = OpLoad %16 %31
-OpImageWrite %39 %28 %19
-OpBranch %36
-%38 = OpLabel
-%95 = OpFunctionCall %void %40 %uint_51 %uint_0 %30 %uint_128
-OpBranch %36
-%36 = OpLabel
-OpStore %_entryPointOutput_vColor %19
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string output_func =
-      R"(%40 = OpFunction %void None %41
-%42 = OpFunctionParameter %uint
-%43 = OpFunctionParameter %uint
-%44 = OpFunctionParameter %uint
-%45 = OpFunctionParameter %uint
-%46 = OpLabel
-%52 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_0
-%55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_9
-%56 = OpIAdd %uint %55 %uint_9
-%57 = OpArrayLength %uint %50 1
-%58 = OpULessThanEqual %bool %56 %57
-OpSelectionMerge %59 None
-OpBranchConditional %58 %60 %59
-%60 = OpLabel
-%61 = OpIAdd %uint %55 %uint_0
-%63 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %61
-OpStore %63 %uint_9
-%65 = OpIAdd %uint %55 %uint_1
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %65
-OpStore %66 %uint_23
-%68 = OpIAdd %uint %55 %uint_2
-%69 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %68
-OpStore %69 %42
-%71 = OpIAdd %uint %55 %uint_3
-%72 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %71
-OpStore %72 %uint_4
-%75 = OpLoad %v4float %gl_FragCoord
-%77 = OpBitcast %v4uint %75
-%78 = OpCompositeExtract %uint %77 0
-%79 = OpIAdd %uint %55 %uint_4
-%80 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %79
-OpStore %80 %78
-%81 = OpCompositeExtract %uint %77 1
-%83 = OpIAdd %uint %55 %uint_5
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %83
-OpStore %84 %81
-%86 = OpIAdd %uint %55 %uint_6
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %86
-OpStore %87 %43
-%89 = OpIAdd %uint %55 %uint_7
-%90 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %89
-OpStore %90 %44
-%92 = OpIAdd %uint %55 %uint_8
-%93 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %92
-OpStore %93 %45
-OpBranch %59
-%59 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, InstrumentVertexSimple) {
-  // This test verifies that the pass will correctly instrument shader
-  // doing bindless image write. This test was created by editing the SPIR-V
-  // from the Simple test.
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpCapability Sampled1D
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Vertex %main "main" %_ %coords2D
-OpSource GLSL 450
-OpName %main "main"
-OpName %lod "lod"
-OpName %coords1D "coords1D"
-OpName %gl_PerVertex "gl_PerVertex"
-OpMemberName %gl_PerVertex 0 "gl_Position"
-OpMemberName %gl_PerVertex 1 "gl_PointSize"
-OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
-OpMemberName %gl_PerVertex 3 "gl_CullDistance"
-OpName %_ ""
-OpName %texSampler1D "texSampler1D"
-OpName %foo "foo"
-OpMemberName %foo 0 "g_idx"
-OpName %__0 ""
-OpName %coords2D "coords2D"
-OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
-OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
-OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
-OpDecorate %gl_PerVertex Block
-OpDecorate %texSampler1D DescriptorSet 0
-OpDecorate %texSampler1D Binding 3
-OpMemberDecorate %foo 0 Offset 0
-OpDecorate %foo Block
-OpDecorate %__0 DescriptorSet 0
-OpDecorate %__0 Binding 5
-OpDecorate %coords2D Location 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Function_float = OpTypePointer Function %float
-%float_3 = OpConstant %float 3
-%float_1_78900003 = OpConstant %float 1.78900003
-%v4float = OpTypeVector %float 4
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%_arr_float_uint_1 = OpTypeArray %float %uint_1
-%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-%_ = OpVariable %_ptr_Output_gl_PerVertex Output
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%21 = OpTypeImage %float 1D 0 0 0 1 Unknown
-%22 = OpTypeSampledImage %21
-%uint_128 = OpConstant %uint 128
-%_arr_22_uint_128 = OpTypeArray %22 %uint_128
-%_ptr_UniformConstant__arr_22_uint_128 = OpTypePointer UniformConstant %_arr_22_uint_128
-%texSampler1D = OpVariable %_ptr_UniformConstant__arr_22_uint_128 UniformConstant
-%foo = OpTypeStruct %int
-%_ptr_Uniform_foo = OpTypePointer Uniform %foo
-%__0 = OpVariable %_ptr_Uniform_foo Uniform
-%_ptr_Uniform_int = OpTypePointer Uniform %int
-%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%v2float = OpTypeVector %float 2
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%coords2D = OpVariable %_ptr_Input_v2float Input
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpCapability Sampled1D
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Vertex %main "main" %_ %coords2D %gl_VertexIndex %gl_InstanceIndex
-OpSource GLSL 450
-OpName %main "main"
-OpName %lod "lod"
-OpName %coords1D "coords1D"
-OpName %gl_PerVertex "gl_PerVertex"
-OpMemberName %gl_PerVertex 0 "gl_Position"
-OpMemberName %gl_PerVertex 1 "gl_PointSize"
-OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
-OpMemberName %gl_PerVertex 3 "gl_CullDistance"
-OpName %_ ""
-OpName %texSampler1D "texSampler1D"
-OpName %foo "foo"
-OpMemberName %foo 0 "g_idx"
-OpName %__0 ""
-OpName %coords2D "coords2D"
-OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
-OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
-OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
-OpDecorate %gl_PerVertex Block
-OpDecorate %texSampler1D DescriptorSet 0
-OpDecorate %texSampler1D Binding 3
-OpMemberDecorate %foo 0 Offset 0
-OpDecorate %foo Block
-OpDecorate %__0 DescriptorSet 0
-OpDecorate %__0 Binding 5
-OpDecorate %coords2D Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_61 Block
-OpMemberDecorate %_struct_61 0 Offset 0
-OpMemberDecorate %_struct_61 1 Offset 4
-OpDecorate %63 DescriptorSet 7
-OpDecorate %63 Binding 0
-OpDecorate %gl_VertexIndex BuiltIn VertexIndex
-OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
-%void = OpTypeVoid
-%12 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Function_float = OpTypePointer Function %float
-%float_3 = OpConstant %float 3
-%float_1_78900003 = OpConstant %float 1.78900003
-%v4float = OpTypeVector %float 4
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%_arr_float_uint_1 = OpTypeArray %float %uint_1
-%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-%_ = OpVariable %_ptr_Output_gl_PerVertex Output
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%24 = OpTypeImage %float 1D 0 0 0 1 Unknown
-%25 = OpTypeSampledImage %24
-%uint_128 = OpConstant %uint 128
-%_arr_25_uint_128 = OpTypeArray %25 %uint_128
-%_ptr_UniformConstant__arr_25_uint_128 = OpTypePointer UniformConstant %_arr_25_uint_128
-%texSampler1D = OpVariable %_ptr_UniformConstant__arr_25_uint_128 UniformConstant
-%foo = OpTypeStruct %int
-%_ptr_Uniform_foo = OpTypePointer Uniform %foo
-%__0 = OpVariable %_ptr_Uniform_foo Uniform
-%_ptr_Uniform_int = OpTypePointer Uniform %int
-%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%v2float = OpTypeVector %float 2
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%coords2D = OpVariable %_ptr_Input_v2float Input
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%54 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_61 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_61 = OpTypePointer StorageBuffer %_struct_61
-%63 = OpVariable %_ptr_StorageBuffer__struct_61 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_uint = OpTypePointer Input %uint
-%gl_VertexIndex = OpVariable %_ptr_Input_uint Input
-%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_74 = OpConstant %uint 74
-%106 = OpConstantNull %v4float
-)";
-
-  const std::string func_before =
-      R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%lod = OpVariable %_ptr_Function_float Function
-%coords1D = OpVariable %_ptr_Function_float Function
-OpStore %lod %float_3
-OpStore %coords1D %float_1_78900003
-%31 = OpAccessChain %_ptr_Uniform_int %__0 %int_0
-%32 = OpLoad %int %31
-%34 = OpAccessChain %_ptr_UniformConstant_22 %texSampler1D %32
-%35 = OpLoad %22 %34
-%36 = OpLoad %float %coords1D
-%37 = OpLoad %float %lod
-%38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37
-%40 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-OpStore %40 %38
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%main = OpFunction %void None %12
-%35 = OpLabel
-%lod = OpVariable %_ptr_Function_float Function
-%coords1D = OpVariable %_ptr_Function_float Function
-OpStore %lod %float_3
-OpStore %coords1D %float_1_78900003
-%36 = OpAccessChain %_ptr_Uniform_int %__0 %int_0
-%37 = OpLoad %int %36
-%38 = OpAccessChain %_ptr_UniformConstant_25 %texSampler1D %37
-%39 = OpLoad %25 %38
-%40 = OpLoad %float %coords1D
-%41 = OpLoad %float %lod
-%46 = OpULessThan %bool %37 %uint_128
-OpSelectionMerge %47 None
-OpBranchConditional %46 %48 %49
-%48 = OpLabel
-%50 = OpLoad %25 %38
-%51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41
-OpBranch %47
-%49 = OpLabel
-%52 = OpBitcast %uint %37
-%105 = OpFunctionCall %void %53 %uint_74 %uint_0 %52 %uint_128
-OpBranch %47
-%47 = OpLabel
-%107 = OpPhi %v4float %51 %48 %106 %49
-%43 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-OpStore %43 %107
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string output_func =
-      R"(%53 = OpFunction %void None %54
-%55 = OpFunctionParameter %uint
-%56 = OpFunctionParameter %uint
-%57 = OpFunctionParameter %uint
-%58 = OpFunctionParameter %uint
-%59 = OpLabel
-%65 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_0
-%68 = OpAtomicIAdd %uint %65 %uint_4 %uint_0 %uint_9
-%69 = OpIAdd %uint %68 %uint_9
-%70 = OpArrayLength %uint %63 1
-%71 = OpULessThanEqual %bool %69 %70
-OpSelectionMerge %72 None
-OpBranchConditional %71 %73 %72
-%73 = OpLabel
-%74 = OpIAdd %uint %68 %uint_0
-%75 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %74
-OpStore %75 %uint_9
-%77 = OpIAdd %uint %68 %uint_1
-%78 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %77
-OpStore %78 %uint_23
-%80 = OpIAdd %uint %68 %uint_2
-%81 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %80
-OpStore %81 %55
-%83 = OpIAdd %uint %68 %uint_3
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %83
-OpStore %84 %uint_0
-%87 = OpLoad %uint %gl_VertexIndex
-%88 = OpIAdd %uint %68 %uint_4
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %88
-OpStore %89 %87
-%91 = OpLoad %uint %gl_InstanceIndex
-%93 = OpIAdd %uint %68 %uint_5
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %93
-OpStore %94 %91
-%96 = OpIAdd %uint %68 %uint_6
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %96
-OpStore %97 %56
-%99 = OpIAdd %uint %68 %uint_7
-%100 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %99
-OpStore %100 %57
-%102 = OpIAdd %uint %68 %uint_8
-%103 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %102
-OpStore %103 %58
-OpBranch %72
-%72 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, MultipleDebugFunctions) {
-  // Same source as Simple, but compiled -g and not optimized, especially not
-  // inlined. The OpSource has had the source extracted for the sake of brevity.
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-%2 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-%1 = OpString "foo5.frag"
-OpSource HLSL 500 %1
-OpName %MainPs "MainPs"
-OpName %PS_INPUT "PS_INPUT"
-OpMemberName %PS_INPUT 0 "vTextureCoords"
-OpName %PS_OUTPUT "PS_OUTPUT"
-OpMemberName %PS_OUTPUT 0 "vColor"
-OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;"
-OpName %i "i"
-OpName %ps_output "ps_output"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_0 "i"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpName %param "param"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 1
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-%void = OpTypeVoid
-%4 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%PS_INPUT = OpTypeStruct %v2float
-%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
-%v4float = OpTypeVector %float 4
-%PS_OUTPUT = OpTypeStruct %v4float
-%13 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
-%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%21 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_21_uint_128 = OpTypeArray %21 %uint_128
-%_ptr_UniformConstant__arr_21_uint_128 = OpTypePointer UniformConstant %_arr_21_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_21_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
-%36 = OpTypeSampler
-%_ptr_UniformConstant_36 = OpTypePointer UniformConstant %36
-%g_sAniso = OpVariable %_ptr_UniformConstant_36 UniformConstant
-%40 = OpTypeSampledImage %21
-%_ptr_Function_v2float = OpTypePointer Function %v2float
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-%5 = OpString "foo5.frag"
-OpSource HLSL 500 %5
-OpName %MainPs "MainPs"
-OpName %PS_INPUT "PS_INPUT"
-OpMemberName %PS_INPUT 0 "vTextureCoords"
-OpName %PS_OUTPUT "PS_OUTPUT"
-OpMemberName %PS_OUTPUT 0 "vColor"
-OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;"
-OpName %i "i"
-OpName %ps_output "ps_output"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_0 "i"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpName %param "param"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 1
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_77 Block
-OpMemberDecorate %_struct_77 0 Offset 0
-OpMemberDecorate %_struct_77 1 Offset 4
-OpDecorate %79 DescriptorSet 7
-OpDecorate %79 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%18 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%PS_INPUT = OpTypeStruct %v2float
-%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
-%v4float = OpTypeVector %float 4
-%PS_OUTPUT = OpTypeStruct %v4float
-%23 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
-%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%27 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_27_uint_128 = OpTypeArray %27 %uint_128
-%_ptr_UniformConstant__arr_27_uint_128 = OpTypePointer UniformConstant %_arr_27_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_27_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27
-%35 = OpTypeSampler
-%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35
-%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant
-%37 = OpTypeSampledImage %27
-%_ptr_Function_v2float = OpTypePointer Function %v2float
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%70 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_77 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77
-%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_93 = OpConstant %uint 93
-%125 = OpConstantNull %v4float
-)";
-
-  const std::string func1_before =
-      R"(%MainPs = OpFunction %void None %4
-%6 = OpLabel
-%i_0 = OpVariable %_ptr_Function_PS_INPUT Function
-%param = OpVariable %_ptr_Function_PS_INPUT Function
-OpLine %1 21 0
-%54 = OpLoad %v2float %i_vTextureCoords
-%55 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0
-OpStore %55 %54
-%59 = OpLoad %PS_INPUT %i_0
-OpStore %param %59
-%60 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param
-%61 = OpCompositeExtract %v4float %60 0
-OpStore %_entryPointOutput_vColor %61
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func1_after =
-      R"(%MainPs = OpFunction %void None %18
-%42 = OpLabel
-%i_0 = OpVariable %_ptr_Function_PS_INPUT Function
-%param = OpVariable %_ptr_Function_PS_INPUT Function
-OpLine %5 21 0
-%43 = OpLoad %v2float %i_vTextureCoords
-%44 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0
-OpStore %44 %43
-%45 = OpLoad %PS_INPUT %i_0
-OpStore %param %45
-%46 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param
-%47 = OpCompositeExtract %v4float %46 0
-OpStore %_entryPointOutput_vColor %47
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func2_before =
-      R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %13
-%i = OpFunctionParameter %_ptr_Function_PS_INPUT
-%16 = OpLabel
-%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
-OpLine %1 24 0
-%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%32 = OpLoad %uint %31
-%34 = OpAccessChain %_ptr_UniformConstant_21 %g_tColor %32
-%35 = OpLoad %21 %34
-%39 = OpLoad %36 %g_sAniso
-%41 = OpSampledImage %40 %35 %39
-%43 = OpAccessChain %_ptr_Function_v2float %i %int_0
-%44 = OpLoad %v2float %43
-%45 = OpImageSampleImplicitLod %v4float %41 %44
-%47 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
-OpStore %47 %45
-OpLine %1 25 0
-%48 = OpLoad %PS_OUTPUT %ps_output
-OpReturnValue %48
-OpFunctionEnd
-)";
-
-  const std::string func2_after =
-      R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %23
-%i = OpFunctionParameter %_ptr_Function_PS_INPUT
-%48 = OpLabel
-%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
-OpLine %5 24 0
-%49 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%50 = OpLoad %uint %49
-%51 = OpAccessChain %_ptr_UniformConstant_27 %g_tColor %50
-%52 = OpLoad %27 %51
-%53 = OpLoad %35 %g_sAniso
-%54 = OpSampledImage %37 %52 %53
-%55 = OpAccessChain %_ptr_Function_v2float %i %int_0
-%56 = OpLoad %v2float %55
-%62 = OpULessThan %bool %50 %uint_128
-OpSelectionMerge %63 None
-OpBranchConditional %62 %64 %65
-%64 = OpLabel
-%66 = OpLoad %27 %51
-%67 = OpSampledImage %37 %66 %53
-%68 = OpImageSampleImplicitLod %v4float %67 %56
-OpBranch %63
-%65 = OpLabel
-%124 = OpFunctionCall %void %69 %uint_93 %uint_0 %50 %uint_128
-OpBranch %63
-%63 = OpLabel
-%126 = OpPhi %v4float %68 %64 %125 %65
-%58 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
-OpStore %58 %126
-OpLine %5 25 0
-%59 = OpLoad %PS_OUTPUT %ps_output
-OpReturnValue %59
-OpFunctionEnd
-)";
-
-  const std::string output_func =
-      R"(%69 = OpFunction %void None %70
-%71 = OpFunctionParameter %uint
-%72 = OpFunctionParameter %uint
-%73 = OpFunctionParameter %uint
-%74 = OpFunctionParameter %uint
-%75 = OpLabel
-%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0
-%84 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_9
-%85 = OpIAdd %uint %84 %uint_9
-%86 = OpArrayLength %uint %79 1
-%87 = OpULessThanEqual %bool %85 %86
-OpSelectionMerge %88 None
-OpBranchConditional %87 %89 %88
-%89 = OpLabel
-%90 = OpIAdd %uint %84 %uint_0
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %90
-OpStore %92 %uint_9
-%94 = OpIAdd %uint %84 %uint_1
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94
-OpStore %95 %uint_23
-%97 = OpIAdd %uint %84 %uint_2
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %97
-OpStore %98 %71
-%100 = OpIAdd %uint %84 %uint_3
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %100
-OpStore %101 %uint_4
-%104 = OpLoad %v4float %gl_FragCoord
-%106 = OpBitcast %v4uint %104
-%107 = OpCompositeExtract %uint %106 0
-%108 = OpIAdd %uint %84 %uint_4
-%109 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %108
-OpStore %109 %107
-%110 = OpCompositeExtract %uint %106 1
-%112 = OpIAdd %uint %84 %uint_5
-%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112
-OpStore %113 %110
-%115 = OpIAdd %uint %84 %uint_6
-%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115
-OpStore %116 %72
-%118 = OpIAdd %uint %84 %uint_7
-%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118
-OpStore %119 %73
-%121 = OpIAdd %uint %84 %uint_8
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121
-OpStore %122 %74
-OpBranch %88
-%88 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func1_before + func2_before,
-      defs_after + func1_after + func2_after + output_func, true, true, 7u, 23u,
-      false, false, 1u);
-}
-
-TEST_F(InstBindlessTest, RuntimeArray) {
-  // This test verifies that the pass will correctly instrument shader
-  // with runtime descriptor array. This test was created by editing the
-  // SPIR-V from the Simple test.
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpCapability RuntimeDescriptorArray
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 1
-OpDecorate %g_tColor Binding 2
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 1
-OpDecorate %g_sAniso Binding 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%20 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%_rarr_20 = OpTypeRuntimeArray %20
-%_ptr_UniformConstant__arr_20 = OpTypePointer UniformConstant %_rarr_20
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_20 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
-%35 = OpTypeSampler
-%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35
-%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant
-%39 = OpTypeSampledImage %20
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpCapability RuntimeDescriptorArray
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 1
-OpDecorate %g_tColor Binding 2
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 1
-OpDecorate %g_sAniso Binding 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_46 Block
-OpMemberDecorate %_struct_46 0 Offset 0
-OpDecorate %48 DescriptorSet 7
-OpDecorate %48 Binding 1
-OpDecorate %_struct_71 Block
-OpMemberDecorate %_struct_71 0 Offset 0
-OpMemberDecorate %_struct_71 1 Offset 4
-OpDecorate %73 DescriptorSet 7
-OpDecorate %73 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%16 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%_runtimearr_16 = OpTypeRuntimeArray %16
-%_ptr_UniformConstant__runtimearr_16 = OpTypePointer UniformConstant %_runtimearr_16
-%g_tColor = OpVariable %_ptr_UniformConstant__runtimearr_16 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
-%24 = OpTypeSampler
-%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24
-%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant
-%26 = OpTypeSampledImage %16
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%uint_2 = OpConstant %uint 2
-%41 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_46 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_46 = OpTypePointer StorageBuffer %_struct_46
-%48 = OpVariable %_ptr_StorageBuffer__struct_46 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%65 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_71 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_71 = OpTypePointer StorageBuffer %_struct_71
-%73 = OpVariable %_ptr_StorageBuffer__struct_71 StorageBuffer
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_59 = OpConstant %uint 59
-%116 = OpConstantNull %v4float
-%119 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
-  const std::string func_before =
-      R"(%MainPs = OpFunction %void None %3
-%5 = OpLabel
-%53 = OpLoad %v2float %i_vTextureCoords
-%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%64 = OpLoad %uint %63
-%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64
-%66 = OpLoad %20 %65
-%67 = OpLoad %35 %g_sAniso
-%68 = OpSampledImage %39 %66 %67
-%71 = OpImageSampleImplicitLod %v4float %68 %53
-OpStore %_entryPointOutput_vColor %71
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%MainPs = OpFunction %void None %10
-%29 = OpLabel
-%30 = OpLoad %v2float %i_vTextureCoords
-%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%32 = OpLoad %uint %31
-%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32
-%34 = OpLoad %16 %33
-%35 = OpLoad %24 %g_sAniso
-%36 = OpSampledImage %26 %34 %35
-%55 = OpFunctionCall %uint %40 %uint_2 %uint_2
-%57 = OpULessThan %bool %32 %55
-OpSelectionMerge %58 None
-OpBranchConditional %57 %59 %60
-%59 = OpLabel
-%61 = OpLoad %16 %33
-%62 = OpSampledImage %26 %61 %35
-%136 = OpFunctionCall %uint %118 %uint_0 %uint_1 %uint_2 %32
-%137 = OpINotEqual %bool %136 %uint_0
-OpSelectionMerge %138 None
-OpBranchConditional %137 %139 %140
-%139 = OpLabel
-%141 = OpLoad %16 %33
-%142 = OpSampledImage %26 %141 %35
-%143 = OpImageSampleImplicitLod %v4float %142 %30
-OpBranch %138
-%140 = OpLabel
-%144 = OpFunctionCall %void %64 %uint_59 %uint_1 %32 %uint_0
-OpBranch %138
-%138 = OpLabel
-%145 = OpPhi %v4float %143 %139 %116 %140
-OpBranch %58
-%60 = OpLabel
-%115 = OpFunctionCall %void %64 %uint_59 %uint_0 %32 %55
-OpBranch %58
-%58 = OpLabel
-%117 = OpPhi %v4float %145 %138 %116 %60
-OpStore %_entryPointOutput_vColor %117
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string new_funcs =
-      R"(%40 = OpFunction %uint None %41
-%42 = OpFunctionParameter %uint
-%43 = OpFunctionParameter %uint
-%44 = OpLabel
-%50 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %42
-%51 = OpLoad %uint %50
-%52 = OpIAdd %uint %51 %43
-%53 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %52
-%54 = OpLoad %uint %53
-OpReturnValue %54
-OpFunctionEnd
-%64 = OpFunction %void None %65
-%66 = OpFunctionParameter %uint
-%67 = OpFunctionParameter %uint
-%68 = OpFunctionParameter %uint
-%69 = OpFunctionParameter %uint
-%70 = OpLabel
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_0
-%77 = OpAtomicIAdd %uint %74 %uint_4 %uint_0 %uint_9
-%78 = OpIAdd %uint %77 %uint_9
-%79 = OpArrayLength %uint %73 1
-%80 = OpULessThanEqual %bool %78 %79
-OpSelectionMerge %81 None
-OpBranchConditional %80 %82 %81
-%82 = OpLabel
-%83 = OpIAdd %uint %77 %uint_0
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %83
-OpStore %84 %uint_9
-%86 = OpIAdd %uint %77 %uint_1
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %86
-OpStore %87 %uint_23
-%88 = OpIAdd %uint %77 %uint_2
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %88
-OpStore %89 %66
-%91 = OpIAdd %uint %77 %uint_3
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %91
-OpStore %92 %uint_4
-%95 = OpLoad %v4float %gl_FragCoord
-%97 = OpBitcast %v4uint %95
-%98 = OpCompositeExtract %uint %97 0
-%99 = OpIAdd %uint %77 %uint_4
-%100 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %99
-OpStore %100 %98
-%101 = OpCompositeExtract %uint %97 1
-%103 = OpIAdd %uint %77 %uint_5
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %103
-OpStore %104 %101
-%106 = OpIAdd %uint %77 %uint_6
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %106
-OpStore %107 %67
-%109 = OpIAdd %uint %77 %uint_7
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %109
-OpStore %110 %68
-%112 = OpIAdd %uint %77 %uint_8
-%113 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %112
-OpStore %113 %69
-OpBranch %81
-%81 = OpLabel
-OpReturn
-OpFunctionEnd
-%118 = OpFunction %uint None %119
-%120 = OpFunctionParameter %uint
-%121 = OpFunctionParameter %uint
-%122 = OpFunctionParameter %uint
-%123 = OpFunctionParameter %uint
-%124 = OpLabel
-%125 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %120
-%126 = OpLoad %uint %125
-%127 = OpIAdd %uint %126 %121
-%128 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %127
-%129 = OpLoad %uint %128
-%130 = OpIAdd %uint %129 %122
-%131 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %130
-%132 = OpLoad %uint %131
-%133 = OpIAdd %uint %132 %123
-%134 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %133
-%135 = OpLoad %uint %134
-OpReturnValue %135
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true);
+                                               23u, false, false);
 }
 
 TEST_F(InstBindlessTest, NoInstrumentNonBindless) {
@@ -2278,1960 +183,10 @@
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(whole_file, whole_file, true,
-                                               true, 7u, 23u, false, false, 1u);
+                                               true, 7u, 23u, false, false);
 }
 
-TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) {
-  // This test verifies that the pass will correctly instrument vanilla
-  // texture sample on a scalar descriptor with an initialization check if the
-  // input_init_enable argument is set to true. This can happen when the
-  // descriptor indexing extension is enabled in the API but the SPIR-V
-  // does not have the extension enabled because it does not contain a
-  // runtime array. This is the same shader as NoInstrumentNonBindless.
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-%void = OpTypeVoid
-%8 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%12 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
-%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant
-%14 = OpTypeSampler
-%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
-%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant
-%16 = OpTypeSampledImage %12
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_35 Block
-OpMemberDecorate %_struct_35 0 Offset 0
-OpDecorate %37 DescriptorSet 7
-OpDecorate %37 Binding 1
-OpDecorate %_struct_67 Block
-OpMemberDecorate %_struct_67 0 Offset 0
-OpMemberDecorate %_struct_67 1 Offset 4
-OpDecorate %69 DescriptorSet 7
-OpDecorate %69 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%8 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%12 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
-%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant
-%14 = OpTypeSampler
-%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
-%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant
-%16 = OpTypeSampledImage %12
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%28 = OpTypeFunction %uint %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_35 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_35 = OpTypePointer StorageBuffer %_struct_35
-%37 = OpVariable %_ptr_StorageBuffer__struct_35 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%uint_1 = OpConstant %uint 1
-%61 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_67 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_67 = OpTypePointer StorageBuffer %_struct_67
-%69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_39 = OpConstant %uint 39
-%113 = OpConstantNull %v4float
-)";
-
-  const std::string func_before =
-      R"(%MainPs = OpFunction %void None %8
-%19 = OpLabel
-%20 = OpLoad %v2float %i_vTextureCoords
-%21 = OpLoad %12 %g_tColor
-%22 = OpLoad %14 %g_sAniso
-%23 = OpSampledImage %16 %21 %22
-%24 = OpImageSampleImplicitLod %v4float %23 %20
-OpStore %_entryPointOutput_vColor %24
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%MainPs = OpFunction %void None %8
-%19 = OpLabel
-%20 = OpLoad %v2float %i_vTextureCoords
-%21 = OpLoad %12 %g_tColor
-%22 = OpLoad %14 %g_sAniso
-%23 = OpSampledImage %16 %21 %22
-%50 = OpFunctionCall %uint %27 %uint_0 %uint_0 %uint_0 %uint_0
-%52 = OpINotEqual %bool %50 %uint_0
-OpSelectionMerge %54 None
-OpBranchConditional %52 %55 %56
-%55 = OpLabel
-%57 = OpLoad %12 %g_tColor
-%58 = OpSampledImage %16 %57 %22
-%59 = OpImageSampleImplicitLod %v4float %58 %20
-OpBranch %54
-%56 = OpLabel
-%112 = OpFunctionCall %void %60 %uint_39 %uint_1 %uint_0 %uint_0
-OpBranch %54
-%54 = OpLabel
-%114 = OpPhi %v4float %59 %55 %113 %56
-OpStore %_entryPointOutput_vColor %114
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string new_funcs =
-      R"(%27 = OpFunction %uint None %28
-%29 = OpFunctionParameter %uint
-%30 = OpFunctionParameter %uint
-%31 = OpFunctionParameter %uint
-%32 = OpFunctionParameter %uint
-%33 = OpLabel
-%39 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %29
-%40 = OpLoad %uint %39
-%41 = OpIAdd %uint %40 %30
-%42 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %41
-%43 = OpLoad %uint %42
-%44 = OpIAdd %uint %43 %31
-%45 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %44
-%46 = OpLoad %uint %45
-%47 = OpIAdd %uint %46 %32
-%48 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %47
-%49 = OpLoad %uint %48
-OpReturnValue %49
-OpFunctionEnd
-%60 = OpFunction %void None %61
-%62 = OpFunctionParameter %uint
-%63 = OpFunctionParameter %uint
-%64 = OpFunctionParameter %uint
-%65 = OpFunctionParameter %uint
-%66 = OpLabel
-%70 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0
-%73 = OpAtomicIAdd %uint %70 %uint_4 %uint_0 %uint_9
-%74 = OpIAdd %uint %73 %uint_9
-%75 = OpArrayLength %uint %69 1
-%76 = OpULessThanEqual %bool %74 %75
-OpSelectionMerge %77 None
-OpBranchConditional %76 %78 %77
-%78 = OpLabel
-%79 = OpIAdd %uint %73 %uint_0
-%80 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %79
-OpStore %80 %uint_9
-%82 = OpIAdd %uint %73 %uint_1
-%83 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %82
-OpStore %83 %uint_23
-%85 = OpIAdd %uint %73 %uint_2
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %85
-OpStore %86 %62
-%88 = OpIAdd %uint %73 %uint_3
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %88
-OpStore %89 %uint_4
-%92 = OpLoad %v4float %gl_FragCoord
-%94 = OpBitcast %v4uint %92
-%95 = OpCompositeExtract %uint %94 0
-%96 = OpIAdd %uint %73 %uint_4
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %96
-OpStore %97 %95
-%98 = OpCompositeExtract %uint %94 1
-%100 = OpIAdd %uint %73 %uint_5
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %100
-OpStore %101 %98
-%103 = OpIAdd %uint %73 %uint_6
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %103
-OpStore %104 %63
-%106 = OpIAdd %uint %73 %uint_7
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %106
-OpStore %107 %64
-%109 = OpIAdd %uint %73 %uint_8
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %109
-OpStore %110 %65
-OpBranch %77
-%77 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true);
-}
-
-TEST_F(InstBindlessTest, SPV14AddToEntryPoint) {
-  const std::string text = R"(
-; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
-; CHECK: OpDecorate [[v1]] DescriptorSet 7
-; CHECK: OpDecorate [[v2]] DescriptorSet 7
-; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer
-; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer
-OpCapability Shader
-OpExtension "SPV_EXT_descriptor_indexing"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var
-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 %struct Block
-OpMemberDecorate %struct 0 Offset 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%v3int = OpTypeVector %int 3
-%float = OpTypeFloat 32
-%v3float = OpTypeVector %float 3
-%v4float = OpTypeVector %float 4
-%struct = OpTypeStruct %v3int
-%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
-%ptr_ssbo_v3int = OpTypePointer StorageBuffer %v3int
-%gid = OpVariable %ptr_ssbo_struct StorageBuffer
-%image = OpTypeImage %float 3D 0 0 0 1 Unknown
-%ptr_uc_image = OpTypePointer UniformConstant %image
-%sampler = OpTypeSampler
-%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
-%image_var = OpVariable %ptr_uc_image UniformConstant
-%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
-%sampled = OpTypeSampledImage %image
-%void_fn = OpTypeFunction %void
-%foo = OpFunction %void None %void_fn
-%entry = OpLabel
-%ld_image = OpLoad %image %image_var
-%ld_sampler = OpLoad %sampler %sampler_var
-%gep = OpAccessChain %ptr_ssbo_v3int %gid %int_0
-%ld_gid = OpLoad %v3int %gep
-%convert = OpConvertUToF %v3float %ld_gid
-%sampled_image = OpSampledImage %sampled %ld_image %ld_sampler
-%sample = OpImageSampleImplicitLod %v4float %sampled_image %convert
-OpReturn
-OpFunctionEnd
-)";
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true);
-}
-
-TEST_F(InstBindlessTest, SPV14AddToEntryPoints) {
-  const std::string text = R"(
-; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
-; CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
-; CHECK: OpDecorate [[v1]] DescriptorSet 7
-; CHECK: OpDecorate [[v2]] DescriptorSet 7
-; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer
-; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer
-OpCapability Shader
-OpExtension "SPV_EXT_descriptor_indexing"
-OpMemoryModel Logical GLSL450
-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 %struct Block
-OpMemberDecorate %struct 0 Offset 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%v3int = OpTypeVector %int 3
-%float = OpTypeFloat 32
-%v3float = OpTypeVector %float 3
-%v4float = OpTypeVector %float 4
-%struct = OpTypeStruct %v3int
-%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
-%ptr_ssbo_v3int = OpTypePointer StorageBuffer %v3int
-%gid = OpVariable %ptr_ssbo_struct StorageBuffer
-%image = OpTypeImage %float 3D 0 0 0 1 Unknown
-%ptr_uc_image = OpTypePointer UniformConstant %image
-%sampler = OpTypeSampler
-%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
-%image_var = OpVariable %ptr_uc_image UniformConstant
-%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
-%sampled = OpTypeSampledImage %image
-%void_fn = OpTypeFunction %void
-%foo = OpFunction %void None %void_fn
-%entry = OpLabel
-%ld_image = OpLoad %image %image_var
-%ld_sampler = OpLoad %sampler %sampler_var
-%gep = OpAccessChain %ptr_ssbo_v3int %gid %int_0
-%ld_gid = OpLoad %v3int %gep
-%convert = OpConvertUToF %v3float %ld_gid
-%sampled_image = OpSampledImage %sampled %ld_image %ld_sampler
-%sample = OpImageSampleImplicitLod %v4float %sampled_image %convert
-OpReturn
-OpFunctionEnd
-)";
-
-  SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) {
-  // #version 450
-  // #extension GL_EXT_nonuniform_qualifier : enable
-  //
-  // layout(location=0) in nonuniformEXT flat int nu_ii;
-  // layout(location=0) out float b;
-  //
-  // layout(binding=3)  uniform uname { float a; }  uniformBuffer[];
-  //
-  // void main()
-  // {
-  //     b = uniformBuffer[nu_ii].a;
-  // }
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %uname "uname"
-OpMemberName %uname 0 "a"
-OpName %uniformBuffer "uniformBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %uname 0 Offset 0
-OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
-OpDecorate %uniformBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %16 NonUniform
-OpDecorate %20 NonUniform
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%uname = OpTypeStruct %float
-%_runtimearr_uname = OpTypeRuntimeArray %uname
-%_ptr_Uniform__runtimearr_uname = OpTypePointer Uniform %_runtimearr_uname
-%uniformBuffer = OpVariable %_ptr_Uniform__runtimearr_uname Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %uname "uname"
-OpMemberName %uname 0 "a"
-OpName %uniformBuffer "uniformBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %uname 0 Offset 0
-OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
-OpDecorate %uniformBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %102 NonUniform
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_31 Block
-OpMemberDecorate %_struct_31 0 Offset 0
-OpDecorate %33 DescriptorSet 7
-OpDecorate %33 Binding 1
-OpDecorate %130 NonUniform
-OpDecorate %_struct_55 Block
-OpMemberDecorate %_struct_55 0 Offset 0
-OpMemberDecorate %_struct_55 1 Offset 4
-OpDecorate %57 DescriptorSet 7
-OpDecorate %57 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniform
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%uname = OpTypeStruct %float
-%_runtimearr_uname = OpTypeRuntimeArray %uname
-%_ptr_Uniform__runtimearr_uname = OpTypePointer Uniform %_runtimearr_uname
-%uniformBuffer = OpVariable %_ptr_Uniform__runtimearr_uname Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_1 = OpConstant %uint 1
-%uint_3 = OpConstant %uint 3
-%26 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_31 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31
-%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%49 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_55 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55
-%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_45 = OpConstant %uint 45
-%101 = OpConstantNull %float
-%105 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
-  const std::string func_before =
-      R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%16 = OpLoad %int %nu_ii
-%19 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %16 %int_0
-%20 = OpLoad %float %19
-OpStore %b %20
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%main = OpFunction %void None %10
-%19 = OpLabel
-%7 = OpLoad %int %nu_ii
-%20 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0
-%40 = OpFunctionCall %uint %25 %uint_1 %uint_3
-%42 = OpULessThan %bool %7 %40
-OpSelectionMerge %43 None
-OpBranchConditional %42 %44 %45
-%44 = OpLabel
-%103 = OpBitcast %uint %7
-%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103
-%123 = OpINotEqual %bool %122 %uint_0
-OpSelectionMerge %124 None
-OpBranchConditional %123 %125 %126
-%125 = OpLabel
-%127 = OpLoad %float %20
-OpBranch %124
-%126 = OpLabel
-%128 = OpBitcast %uint %7
-%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0
-OpBranch %124
-%124 = OpLabel
-%130 = OpPhi %float %127 %125 %101 %126
-OpBranch %43
-%45 = OpLabel
-%47 = OpBitcast %uint %7
-%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40
-OpBranch %43
-%43 = OpLabel
-%102 = OpPhi %float %130 %124 %101 %45
-OpStore %b %102
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string new_funcs =
-      R"(%25 = OpFunction %uint None %26
-%27 = OpFunctionParameter %uint
-%28 = OpFunctionParameter %uint
-%29 = OpLabel
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %28
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37
-%39 = OpLoad %uint %38
-OpReturnValue %39
-OpFunctionEnd
-%48 = OpFunction %void None %49
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpFunctionParameter %uint
-%54 = OpLabel
-%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0
-%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_9
-%62 = OpIAdd %uint %61 %uint_9
-%63 = OpArrayLength %uint %57 1
-%64 = OpULessThanEqual %bool %62 %63
-OpSelectionMerge %65 None
-OpBranchConditional %64 %66 %65
-%66 = OpLabel
-%67 = OpIAdd %uint %61 %uint_0
-%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67
-OpStore %68 %uint_9
-%70 = OpIAdd %uint %61 %uint_1
-%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70
-OpStore %71 %uint_23
-%73 = OpIAdd %uint %61 %uint_2
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73
-OpStore %74 %50
-%75 = OpIAdd %uint %61 %uint_3
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75
-OpStore %76 %uint_4
-%80 = OpLoad %v4float %gl_FragCoord
-%82 = OpBitcast %v4uint %80
-%83 = OpCompositeExtract %uint %82 0
-%84 = OpIAdd %uint %61 %uint_4
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84
-OpStore %85 %83
-%86 = OpCompositeExtract %uint %82 1
-%88 = OpIAdd %uint %61 %uint_5
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88
-OpStore %89 %86
-%91 = OpIAdd %uint %61 %uint_6
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91
-OpStore %92 %51
-%94 = OpIAdd %uint %61 %uint_7
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94
-OpStore %95 %52
-%97 = OpIAdd %uint %61 %uint_8
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97
-OpStore %98 %53
-OpBranch %65
-%65 = OpLabel
-OpReturn
-OpFunctionEnd
-%104 = OpFunction %uint None %105
-%106 = OpFunctionParameter %uint
-%107 = OpFunctionParameter %uint
-%108 = OpFunctionParameter %uint
-%109 = OpFunctionParameter %uint
-%110 = OpLabel
-%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106
-%112 = OpLoad %uint %111
-%113 = OpIAdd %uint %112 %107
-%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113
-%115 = OpLoad %uint %114
-%116 = OpIAdd %uint %115 %108
-%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116
-%118 = OpLoad %uint %117
-%119 = OpIAdd %uint %118 %109
-%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119
-%121 = OpLoad %uint %120
-OpReturnValue %121
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) {
-  // #version 450
-  // #extension GL_EXT_nonuniform_qualifier : enable
-  //
-  // layout(location=0) in nonuniformEXT flat int nu_ii;
-  // layout(location=0) out float b;
-  //
-  // layout(binding=3)  buffer bname { float b; }  storageBuffer[];
-  //
-  // void main()
-  // {
-  //     b = storageBuffer[nu_ii].b;
-  // }
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %bname "bname"
-OpMemberName %bname 0 "a"
-OpName %storageBuffer "storageBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %bname 0 Offset 0
-OpDecorate %bname BufferBlock
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %16 NonUniform
-OpDecorate %20 NonUniform
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%bname = OpTypeStruct %float
-%_runtimearr_bname = OpTypeRuntimeArray %bname
-%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname
-%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %bname "bname"
-OpMemberName %bname 0 "a"
-OpName %storageBuffer "storageBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %bname 0 Offset 0
-OpDecorate %bname BufferBlock
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %102 NonUniform
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_31 Block
-OpMemberDecorate %_struct_31 0 Offset 0
-OpDecorate %33 DescriptorSet 7
-OpDecorate %33 Binding 1
-OpDecorate %130 NonUniform
-OpDecorate %_struct_55 Block
-OpMemberDecorate %_struct_55 0 Offset 0
-OpMemberDecorate %_struct_55 1 Offset 4
-OpDecorate %57 DescriptorSet 7
-OpDecorate %57 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniform
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%bname = OpTypeStruct %float
-%_runtimearr_bname = OpTypeRuntimeArray %bname
-%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname
-%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_1 = OpConstant %uint 1
-%uint_3 = OpConstant %uint 3
-%26 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_31 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31
-%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%49 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_55 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55
-%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_45 = OpConstant %uint 45
-%101 = OpConstantNull %float
-%105 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
-  const std::string func_before =
-      R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%16 = OpLoad %int %nu_ii
-%19 = OpAccessChain %_ptr_Uniform_float %storageBuffer %16 %int_0
-%20 = OpLoad %float %19
-OpStore %b %20
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%main = OpFunction %void None %10
-%19 = OpLabel
-%7 = OpLoad %int %nu_ii
-%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %7 %int_0
-%40 = OpFunctionCall %uint %25 %uint_1 %uint_3
-%42 = OpULessThan %bool %7 %40
-OpSelectionMerge %43 None
-OpBranchConditional %42 %44 %45
-%44 = OpLabel
-%103 = OpBitcast %uint %7
-%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103
-%123 = OpINotEqual %bool %122 %uint_0
-OpSelectionMerge %124 None
-OpBranchConditional %123 %125 %126
-%125 = OpLabel
-%127 = OpLoad %float %20
-OpBranch %124
-%126 = OpLabel
-%128 = OpBitcast %uint %7
-%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0
-OpBranch %124
-%124 = OpLabel
-%130 = OpPhi %float %127 %125 %101 %126
-OpBranch %43
-%45 = OpLabel
-%47 = OpBitcast %uint %7
-%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40
-OpBranch %43
-%43 = OpLabel
-%102 = OpPhi %float %130 %124 %101 %45
-OpStore %b %102
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string new_funcs =
-      R"(%25 = OpFunction %uint None %26
-%27 = OpFunctionParameter %uint
-%28 = OpFunctionParameter %uint
-%29 = OpLabel
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %28
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37
-%39 = OpLoad %uint %38
-OpReturnValue %39
-OpFunctionEnd
-%48 = OpFunction %void None %49
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpFunctionParameter %uint
-%54 = OpLabel
-%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0
-%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_9
-%62 = OpIAdd %uint %61 %uint_9
-%63 = OpArrayLength %uint %57 1
-%64 = OpULessThanEqual %bool %62 %63
-OpSelectionMerge %65 None
-OpBranchConditional %64 %66 %65
-%66 = OpLabel
-%67 = OpIAdd %uint %61 %uint_0
-%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67
-OpStore %68 %uint_9
-%70 = OpIAdd %uint %61 %uint_1
-%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70
-OpStore %71 %uint_23
-%73 = OpIAdd %uint %61 %uint_2
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73
-OpStore %74 %50
-%75 = OpIAdd %uint %61 %uint_3
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75
-OpStore %76 %uint_4
-%80 = OpLoad %v4float %gl_FragCoord
-%82 = OpBitcast %v4uint %80
-%83 = OpCompositeExtract %uint %82 0
-%84 = OpIAdd %uint %61 %uint_4
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84
-OpStore %85 %83
-%86 = OpCompositeExtract %uint %82 1
-%88 = OpIAdd %uint %61 %uint_5
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88
-OpStore %89 %86
-%91 = OpIAdd %uint %61 %uint_6
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91
-OpStore %92 %51
-%94 = OpIAdd %uint %61 %uint_7
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94
-OpStore %95 %52
-%97 = OpIAdd %uint %61 %uint_8
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97
-OpStore %98 %53
-OpBranch %65
-%65 = OpLabel
-OpReturn
-OpFunctionEnd
-%104 = OpFunction %uint None %105
-%106 = OpFunctionParameter %uint
-%107 = OpFunctionParameter %uint
-%108 = OpFunctionParameter %uint
-%109 = OpFunctionParameter %uint
-%110 = OpLabel
-%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106
-%112 = OpLoad %uint %111
-%113 = OpIAdd %uint %112 %107
-%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113
-%115 = OpLoad %uint %114
-%116 = OpIAdd %uint %115 %108
-%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116
-%118 = OpLoad %uint %117
-%119 = OpIAdd %uint %118 %109
-%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119
-%121 = OpLoad %uint %120
-OpReturnValue %121
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) {
-  // Same as Deprecated but declaring as StorageBuffer Block
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability StorageBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %bname "bname"
-OpMemberName %bname 0 "a"
-OpName %storageBuffer "storageBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %bname 0 Offset 0
-OpDecorate %bname Block
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %16 NonUniform
-OpDecorate %20 NonUniform
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%bname = OpTypeStruct %float
-%_runtimearr_bname = OpTypeRuntimeArray %bname
-%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname
-%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability StorageBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %bname "bname"
-OpMemberName %bname 0 "a"
-OpName %storageBuffer "storageBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %bname 0 Offset 0
-OpDecorate %bname Block
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %102 NonUniform
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_31 Block
-OpMemberDecorate %_struct_31 0 Offset 0
-OpDecorate %33 DescriptorSet 7
-OpDecorate %33 Binding 1
-OpDecorate %130 NonUniform
-OpDecorate %_struct_55 Block
-OpMemberDecorate %_struct_55 0 Offset 0
-OpMemberDecorate %_struct_55 1 Offset 4
-OpDecorate %57 DescriptorSet 7
-OpDecorate %57 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniform
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%bname = OpTypeStruct %float
-%_runtimearr_bname = OpTypeRuntimeArray %bname
-%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname
-%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_1 = OpConstant %uint 1
-%uint_3 = OpConstant %uint 3
-%26 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_31 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31
-%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%49 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_55 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55
-%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_45 = OpConstant %uint 45
-%101 = OpConstantNull %float
-%105 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
-  const std::string func_before =
-      R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%16 = OpLoad %int %nu_ii
-%19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0
-%20 = OpLoad %float %19
-OpStore %b %20
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%main = OpFunction %void None %10
-%19 = OpLabel
-%7 = OpLoad %int %nu_ii
-%20 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %7 %int_0
-%40 = OpFunctionCall %uint %25 %uint_1 %uint_3
-%42 = OpULessThan %bool %7 %40
-OpSelectionMerge %43 None
-OpBranchConditional %42 %44 %45
-%44 = OpLabel
-%103 = OpBitcast %uint %7
-%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103
-%123 = OpINotEqual %bool %122 %uint_0
-OpSelectionMerge %124 None
-OpBranchConditional %123 %125 %126
-%125 = OpLabel
-%127 = OpLoad %float %20
-OpBranch %124
-%126 = OpLabel
-%128 = OpBitcast %uint %7
-%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0
-OpBranch %124
-%124 = OpLabel
-%130 = OpPhi %float %127 %125 %101 %126
-OpBranch %43
-%45 = OpLabel
-%47 = OpBitcast %uint %7
-%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40
-OpBranch %43
-%43 = OpLabel
-%102 = OpPhi %float %130 %124 %101 %45
-OpStore %b %102
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string new_funcs =
-      R"(%25 = OpFunction %uint None %26
-%27 = OpFunctionParameter %uint
-%28 = OpFunctionParameter %uint
-%29 = OpLabel
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %28
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37
-%39 = OpLoad %uint %38
-OpReturnValue %39
-OpFunctionEnd
-%48 = OpFunction %void None %49
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpFunctionParameter %uint
-%54 = OpLabel
-%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0
-%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_9
-%62 = OpIAdd %uint %61 %uint_9
-%63 = OpArrayLength %uint %57 1
-%64 = OpULessThanEqual %bool %62 %63
-OpSelectionMerge %65 None
-OpBranchConditional %64 %66 %65
-%66 = OpLabel
-%67 = OpIAdd %uint %61 %uint_0
-%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67
-OpStore %68 %uint_9
-%70 = OpIAdd %uint %61 %uint_1
-%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70
-OpStore %71 %uint_23
-%73 = OpIAdd %uint %61 %uint_2
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73
-OpStore %74 %50
-%75 = OpIAdd %uint %61 %uint_3
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75
-OpStore %76 %uint_4
-%80 = OpLoad %v4float %gl_FragCoord
-%82 = OpBitcast %v4uint %80
-%83 = OpCompositeExtract %uint %82 0
-%84 = OpIAdd %uint %61 %uint_4
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84
-OpStore %85 %83
-%86 = OpCompositeExtract %uint %82 1
-%88 = OpIAdd %uint %61 %uint_5
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88
-OpStore %89 %86
-%91 = OpIAdd %uint %61 %uint_6
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91
-OpStore %92 %51
-%94 = OpIAdd %uint %61 %uint_7
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94
-OpStore %95 %52
-%97 = OpIAdd %uint %61 %uint_8
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97
-OpStore %98 %53
-OpBranch %65
-%65 = OpLabel
-OpReturn
-OpFunctionEnd
-%104 = OpFunction %uint None %105
-%106 = OpFunctionParameter %uint
-%107 = OpFunctionParameter %uint
-%108 = OpFunctionParameter %uint
-%109 = OpFunctionParameter %uint
-%110 = OpLabel
-%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106
-%112 = OpLoad %uint %111
-%113 = OpIAdd %uint %112 %107
-%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113
-%115 = OpLoad %uint %114
-%116 = OpIAdd %uint %115 %108
-%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116
-%118 = OpLoad %uint %117
-%119 = OpIAdd %uint %118 %109
-%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119
-%121 = OpLoad %uint %120
-OpReturnValue %121
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true);
-}
-
-TEST_F(InstBindlessTest, InstInitLoadUBOScalar) {
-  // #version 450
-  // #extension GL_EXT_nonuniform_qualifier : enable
-  //
-  // layout(location=0) out float b;
-  // layout(binding=3)  uniform uname { float a; }  uniformBuffer;
-  //
-  // void main()
-  // {
-  //     b = uniformBuffer.a;
-  // }
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %uname "uname"
-OpMemberName %uname 0 "a"
-OpName %uniformBuffer "uniformBuffer"
-OpDecorate %b Location 0
-OpMemberDecorate %uname 0 Offset 0
-OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
-OpDecorate %uniformBuffer Binding 3
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%uname = OpTypeStruct %float
-%_ptr_Uniform_uname = OpTypePointer Uniform %uname
-%uniformBuffer = OpVariable %_ptr_Uniform_uname Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %uname "uname"
-OpMemberName %uname 0 "a"
-OpName %uniformBuffer "uniformBuffer"
-OpDecorate %b Location 0
-OpMemberDecorate %uname 0 Offset 0
-OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
-OpDecorate %uniformBuffer Binding 3
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_28 Block
-OpMemberDecorate %_struct_28 0 Offset 0
-OpDecorate %30 DescriptorSet 7
-OpDecorate %30 Binding 1
-OpDecorate %_struct_58 Block
-OpMemberDecorate %_struct_58 0 Offset 0
-OpMemberDecorate %_struct_58 1 Offset 4
-OpDecorate %60 DescriptorSet 7
-OpDecorate %60 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%7 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%uname = OpTypeStruct %float
-%_ptr_Uniform_uname = OpTypePointer Uniform %uname
-%uniformBuffer = OpVariable %_ptr_Uniform_uname Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_3 = OpConstant %uint 3
-%21 = OpTypeFunction %uint %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_28 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_28 = OpTypePointer StorageBuffer %_struct_28
-%30 = OpVariable %_ptr_StorageBuffer__struct_28 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%uint_1 = OpConstant %uint 1
-%52 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_58 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_58 = OpTypePointer StorageBuffer %_struct_58
-%60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_32 = OpConstant %uint 32
-%104 = OpConstantNull %float
-)";
-
-  const std::string func_before =
-      R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0
-%16 = OpLoad %float %15
-OpStore %b %16
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%main = OpFunction %void None %7
-%14 = OpLabel
-%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0
-%43 = OpFunctionCall %uint %20 %uint_0 %uint_0 %uint_3 %uint_0
-%45 = OpINotEqual %bool %43 %uint_0
-OpSelectionMerge %47 None
-OpBranchConditional %45 %48 %49
-%48 = OpLabel
-%50 = OpLoad %float %15
-OpBranch %47
-%49 = OpLabel
-%103 = OpFunctionCall %void %51 %uint_32 %uint_1 %uint_0 %uint_0
-OpBranch %47
-%47 = OpLabel
-%105 = OpPhi %float %50 %48 %104 %49
-OpStore %b %105
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string new_funcs =
-      R"(%20 = OpFunction %uint None %21
-%22 = OpFunctionParameter %uint
-%23 = OpFunctionParameter %uint
-%24 = OpFunctionParameter %uint
-%25 = OpFunctionParameter %uint
-%26 = OpLabel
-%32 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %22
-%33 = OpLoad %uint %32
-%34 = OpIAdd %uint %33 %23
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %34
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %24
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %37
-%39 = OpLoad %uint %38
-%40 = OpIAdd %uint %39 %25
-%41 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %40
-%42 = OpLoad %uint %41
-OpReturnValue %42
-OpFunctionEnd
-%51 = OpFunction %void None %52
-%53 = OpFunctionParameter %uint
-%54 = OpFunctionParameter %uint
-%55 = OpFunctionParameter %uint
-%56 = OpFunctionParameter %uint
-%57 = OpLabel
-%61 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_0
-%64 = OpAtomicIAdd %uint %61 %uint_4 %uint_0 %uint_9
-%65 = OpIAdd %uint %64 %uint_9
-%66 = OpArrayLength %uint %60 1
-%67 = OpULessThanEqual %bool %65 %66
-OpSelectionMerge %68 None
-OpBranchConditional %67 %69 %68
-%69 = OpLabel
-%70 = OpIAdd %uint %64 %uint_0
-%71 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %70
-OpStore %71 %uint_9
-%73 = OpIAdd %uint %64 %uint_1
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %73
-OpStore %74 %uint_23
-%76 = OpIAdd %uint %64 %uint_2
-%77 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %76
-OpStore %77 %53
-%78 = OpIAdd %uint %64 %uint_3
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %78
-OpStore %79 %uint_4
-%83 = OpLoad %v4float %gl_FragCoord
-%85 = OpBitcast %v4uint %83
-%86 = OpCompositeExtract %uint %85 0
-%87 = OpIAdd %uint %64 %uint_4
-%88 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %87
-OpStore %88 %86
-%89 = OpCompositeExtract %uint %85 1
-%91 = OpIAdd %uint %64 %uint_5
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %91
-OpStore %92 %89
-%94 = OpIAdd %uint %64 %uint_6
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %94
-OpStore %95 %54
-%97 = OpIAdd %uint %64 %uint_7
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %97
-OpStore %98 %55
-%100 = OpIAdd %uint %64 %uint_8
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %100
-OpStore %101 %56
-OpBranch %68
-%68 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) {
-  // #version 450
-  // #extension GL_EXT_nonuniform_qualifier : enable
-  //
-  // layout(location=0) in nonuniformEXT flat int nu_ii;
-  // layout(location=1) in float b;
-  //
-  // layout(binding=4)  buffer bname { float b; }  storageBuffer[];
-  //
-  // void main()
-  // {
-  //     storageBuffer[nu_ii].b = b;
-  // }
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability StorageBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %nu_ii %b
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %bname "bname"
-OpMemberName %bname 0 "b"
-OpName %storageBuffer "storageBuffer"
-OpName %nu_ii "nu_ii"
-OpName %b "b"
-OpMemberDecorate %bname 0 Offset 0
-OpDecorate %bname BufferBlock
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 4
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %14 NonUniform
-OpDecorate %b Location 1
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%bname = OpTypeStruct %float
-%_runtimearr_bname = OpTypeRuntimeArray %bname
-%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname
-%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Input_float = OpTypePointer Input %float
-%b = OpVariable %_ptr_Input_float Input
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability StorageBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %bname "bname"
-OpMemberName %bname 0 "b"
-OpName %storageBuffer "storageBuffer"
-OpName %nu_ii "nu_ii"
-OpName %b "b"
-OpMemberDecorate %bname 0 Offset 0
-OpDecorate %bname BufferBlock
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 4
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %b Location 1
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_31 Block
-OpMemberDecorate %_struct_31 0 Offset 0
-OpDecorate %33 DescriptorSet 7
-OpDecorate %33 Binding 1
-OpDecorate %_struct_54 Block
-OpMemberDecorate %_struct_54 0 Offset 0
-OpMemberDecorate %_struct_54 1 Offset 4
-OpDecorate %56 DescriptorSet 7
-OpDecorate %56 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%bname = OpTypeStruct %float
-%_runtimearr_bname = OpTypeRuntimeArray %bname
-%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname
-%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Input_float = OpTypePointer Input %float
-%b = OpVariable %_ptr_Input_float Input
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_1 = OpConstant %uint 1
-%uint_4 = OpConstant %uint 4
-%26 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_31 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31
-%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%48 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_54 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_54 = OpTypePointer StorageBuffer %_struct_54
-%56 = OpVariable %_ptr_StorageBuffer__struct_54 StorageBuffer
-%uint_9 = OpConstant %uint 9
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_45 = OpConstant %uint 45
-%102 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
-  const std::string func_before =
-      R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%14 = OpLoad %int %nu_ii
-%18 = OpLoad %float %b
-%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %14 %int_0
-OpStore %20 %18
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%main = OpFunction %void None %9
-%18 = OpLabel
-%7 = OpLoad %int %nu_ii
-%19 = OpLoad %float %b
-%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %7 %int_0
-%40 = OpFunctionCall %uint %25 %uint_1 %uint_4
-%42 = OpULessThan %bool %7 %40
-OpSelectionMerge %43 None
-OpBranchConditional %42 %44 %45
-%44 = OpLabel
-%100 = OpBitcast %uint %7
-%119 = OpFunctionCall %uint %101 %uint_0 %uint_0 %uint_4 %100
-%120 = OpINotEqual %bool %119 %uint_0
-OpSelectionMerge %121 None
-OpBranchConditional %120 %122 %123
-%122 = OpLabel
-OpStore %20 %19
-OpBranch %121
-%123 = OpLabel
-%124 = OpBitcast %uint %7
-%125 = OpFunctionCall %void %47 %uint_45 %uint_1 %124 %uint_0
-OpBranch %121
-%121 = OpLabel
-OpBranch %43
-%45 = OpLabel
-%46 = OpBitcast %uint %7
-%99 = OpFunctionCall %void %47 %uint_45 %uint_0 %46 %40
-OpBranch %43
-%43 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string new_funcs =
-      R"(%25 = OpFunction %uint None %26
-%27 = OpFunctionParameter %uint
-%28 = OpFunctionParameter %uint
-%29 = OpLabel
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %28
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37
-%39 = OpLoad %uint %38
-OpReturnValue %39
-OpFunctionEnd
-%47 = OpFunction %void None %48
-%49 = OpFunctionParameter %uint
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpLabel
-%57 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_0
-%59 = OpAtomicIAdd %uint %57 %uint_4 %uint_0 %uint_9
-%60 = OpIAdd %uint %59 %uint_9
-%61 = OpArrayLength %uint %56 1
-%62 = OpULessThanEqual %bool %60 %61
-OpSelectionMerge %63 None
-OpBranchConditional %62 %64 %63
-%64 = OpLabel
-%65 = OpIAdd %uint %59 %uint_0
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %65
-OpStore %66 %uint_9
-%68 = OpIAdd %uint %59 %uint_1
-%69 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %68
-OpStore %69 %uint_23
-%71 = OpIAdd %uint %59 %uint_2
-%72 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %71
-OpStore %72 %49
-%74 = OpIAdd %uint %59 %uint_3
-%75 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %74
-OpStore %75 %uint_4
-%79 = OpLoad %v4float %gl_FragCoord
-%81 = OpBitcast %v4uint %79
-%82 = OpCompositeExtract %uint %81 0
-%83 = OpIAdd %uint %59 %uint_4
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %83
-OpStore %84 %82
-%85 = OpCompositeExtract %uint %81 1
-%87 = OpIAdd %uint %59 %uint_5
-%88 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %87
-OpStore %88 %85
-%90 = OpIAdd %uint %59 %uint_6
-%91 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %90
-OpStore %91 %50
-%93 = OpIAdd %uint %59 %uint_7
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %93
-OpStore %94 %51
-%96 = OpIAdd %uint %59 %uint_8
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %96
-OpStore %97 %52
-OpBranch %63
-%63 = OpLabel
-OpReturn
-OpFunctionEnd
-%101 = OpFunction %uint None %102
-%103 = OpFunctionParameter %uint
-%104 = OpFunctionParameter %uint
-%105 = OpFunctionParameter %uint
-%106 = OpFunctionParameter %uint
-%107 = OpLabel
-%108 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %103
-%109 = OpLoad %uint %108
-%110 = OpIAdd %uint %109 %104
-%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %110
-%112 = OpLoad %uint %111
-%113 = OpIAdd %uint %112 %105
-%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113
-%115 = OpLoad %uint %114
-%116 = OpIAdd %uint %115 %106
-%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116
-%118 = OpLoad %uint %117
-OpReturnValue %118
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true);
-}
-
-TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) {
-  // #version 450
-  // #extension GL_EXT_nonuniform_qualifier : enable
-  //
-  // layout(location=0) in nonuniformEXT flat int nu_ii;
-  // layout(location=0) out float b;
-  //
-  // layout(binding=3)  uniform uname { float a; }  uniformBuffer[128];
-  //
-  // void main()
-  // {
-  //     b = uniformBuffer[nu_ii].a;
-  // }
-
-  const std::string defs_before =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %uname "uname"
-OpMemberName %uname 0 "a"
-OpName %uniformBuffer "uniformBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %uname 0 Offset 0
-OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
-OpDecorate %uniformBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %18 NonUniform
-OpDecorate %22 NonUniform
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%uname = OpTypeStruct %float
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_uname_uint_128 = OpTypeArray %uname %uint_128
-%_ptr_Uniform__arr_uname_uint_128 = OpTypePointer Uniform %_arr_uname_uint_128
-%uniformBuffer = OpVariable %_ptr_Uniform__arr_uname_uint_128 Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-)";
-
-  const std::string defs_after =
-      R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %uname "uname"
-OpMemberName %uname 0 "a"
-OpName %uniformBuffer "uniformBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %uname 0 Offset 0
-OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
-OpDecorate %uniformBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %89 NonUniform
-OpDecorate %120 NonUniform
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpMemberDecorate %_struct_39 1 Offset 4
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %_struct_98 Block
-OpMemberDecorate %_struct_98 0 Offset 0
-OpDecorate %100 DescriptorSet 7
-OpDecorate %100 Binding 1
-OpDecorate %117 NonUniform
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%uname = OpTypeStruct %float
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_uname_uint_128 = OpTypeArray %uname %uint_128
-%_ptr_Uniform__arr_uname_uint_128 = OpTypePointer Uniform %_arr_uname_uint_128
-%uniformBuffer = OpVariable %_ptr_Uniform__arr_uname_uint_128 Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%32 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_39 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_9 = OpConstant %uint 9
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_46 = OpConstant %uint 46
-%88 = OpConstantNull %float
-%92 = OpTypeFunction %uint %uint %uint %uint %uint
-%_struct_98 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_98 = OpTypePointer StorageBuffer %_struct_98
-%100 = OpVariable %_ptr_StorageBuffer__struct_98 StorageBuffer
-)";
-
-  const std::string func_before =
-      R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%18 = OpLoad %int %nu_ii
-%21 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %18 %int_0
-%22 = OpLoad %float %21
-OpStore %b %22
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string func_after =
-      R"(%main = OpFunction %void None %10
-%21 = OpLabel
-%7 = OpLoad %int %nu_ii
-%22 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0
-%25 = OpULessThan %bool %7 %uint_128
-OpSelectionMerge %26 None
-OpBranchConditional %25 %27 %28
-%27 = OpLabel
-%90 = OpBitcast %uint %7
-%112 = OpFunctionCall %uint %91 %uint_0 %uint_0 %uint_3 %90
-%113 = OpINotEqual %bool %112 %uint_0
-OpSelectionMerge %114 None
-OpBranchConditional %113 %115 %116
-%115 = OpLabel
-%117 = OpLoad %float %22
-OpBranch %114
-%116 = OpLabel
-%118 = OpBitcast %uint %7
-%119 = OpFunctionCall %void %31 %uint_46 %uint_1 %118 %uint_0
-OpBranch %114
-%114 = OpLabel
-%120 = OpPhi %float %117 %115 %88 %116
-OpBranch %26
-%28 = OpLabel
-%30 = OpBitcast %uint %7
-%87 = OpFunctionCall %void %31 %uint_46 %uint_0 %30 %uint_128
-OpBranch %26
-%26 = OpLabel
-%89 = OpPhi %float %120 %114 %88 %28
-OpStore %b %89
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string new_funcs =
-      R"(%31 = OpFunction %void None %32
-%33 = OpFunctionParameter %uint
-%34 = OpFunctionParameter %uint
-%35 = OpFunctionParameter %uint
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0
-%46 = OpAtomicIAdd %uint %43 %uint_4 %uint_0 %uint_9
-%47 = OpIAdd %uint %46 %uint_9
-%48 = OpArrayLength %uint %41 1
-%49 = OpULessThanEqual %bool %47 %48
-OpSelectionMerge %50 None
-OpBranchConditional %49 %51 %50
-%51 = OpLabel
-%52 = OpIAdd %uint %46 %uint_0
-%54 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %52
-OpStore %54 %uint_9
-%56 = OpIAdd %uint %46 %uint_1
-%57 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %56
-OpStore %57 %uint_23
-%59 = OpIAdd %uint %46 %uint_2
-%60 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %59
-OpStore %60 %33
-%62 = OpIAdd %uint %46 %uint_3
-%63 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %62
-OpStore %63 %uint_4
-%67 = OpLoad %v4float %gl_FragCoord
-%69 = OpBitcast %v4uint %67
-%70 = OpCompositeExtract %uint %69 0
-%71 = OpIAdd %uint %46 %uint_4
-%72 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %71
-OpStore %72 %70
-%73 = OpCompositeExtract %uint %69 1
-%75 = OpIAdd %uint %46 %uint_5
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %75
-OpStore %76 %73
-%78 = OpIAdd %uint %46 %uint_6
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %78
-OpStore %79 %34
-%81 = OpIAdd %uint %46 %uint_7
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %81
-OpStore %82 %35
-%84 = OpIAdd %uint %46 %uint_8
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %84
-OpStore %85 %36
-OpBranch %50
-%50 = OpLabel
-OpReturn
-OpFunctionEnd
-%91 = OpFunction %uint None %92
-%93 = OpFunctionParameter %uint
-%94 = OpFunctionParameter %uint
-%95 = OpFunctionParameter %uint
-%96 = OpFunctionParameter %uint
-%97 = OpLabel
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %93
-%102 = OpLoad %uint %101
-%103 = OpIAdd %uint %102 %94
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %103
-%105 = OpLoad %uint %104
-%106 = OpIAdd %uint %105 %95
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %106
-%108 = OpLoad %uint %107
-%109 = OpIAdd %uint %108 %96
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %109
-%111 = OpLoad %uint %110
-OpReturnValue %111
-OpFunctionEnd
-)";
-
-  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass>(
-      defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true);
-}
-
-TEST_F(InstBindlessTest, SimpleV2) {
+TEST_F(InstBindlessTest, Simple) {
   // Texture2D g_tColor[128];
   //
   // layout(push_constant) cbuffer PerViewConstantBuffer_t
@@ -4452,16 +407,15 @@
 )";
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool,
-                        uint32_t>(
+  SinglePassRunAndCheck<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool>(
       entry_before + names_annots + consts_types_vars + func_pt1 +
           func_pt2_before,
       entry_after + names_annots + new_annots + consts_types_vars +
           new_consts_types_vars + func_pt1 + func_pt2_after + output_func,
-      true, true, 7u, 23u, false, false, 2u);
+      true, true, 7u, 23u, false, false);
 }
 
-TEST_F(InstBindlessTest, InstrumentMultipleInstructionsV2) {
+TEST_F(InstBindlessTest, InstrumentMultipleInstructions) {
   // Texture2D g_tColor[128];
   //
   // layout(push_constant) cbuffer PerViewConstantBuffer_t
@@ -4753,10 +707,10 @@
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 2u);
+      true, 7u, 23u, false, false);
 }
 
-TEST_F(InstBindlessTest, InstrumentOpImageV2) {
+TEST_F(InstBindlessTest, InstrumentOpImage) {
   // This test verifies that the pass will correctly instrument shader
   // using OpImage. This test was created by editing the SPIR-V
   // from the Simple test.
@@ -4981,10 +935,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 2u);
+      true, 7u, 23u, false, false);
 }
 
-TEST_F(InstBindlessTest, InstrumentSampledImageV2) {
+TEST_F(InstBindlessTest, InstrumentSampledImage) {
   // This test verifies that the pass will correctly instrument shader
   // using sampled image. This test was created by editing the SPIR-V
   // from the Simple test.
@@ -5204,10 +1158,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 2u);
+      true, 7u, 23u, false, false);
 }
 
-TEST_F(InstBindlessTest, InstrumentImageWriteV2) {
+TEST_F(InstBindlessTest, InstrumentImageWrite) {
   // This test verifies that the pass will correctly instrument shader
   // doing bindless image write. This test was created by editing the SPIR-V
   // from the Simple test.
@@ -5429,10 +1383,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 2u);
+      true, 7u, 23u, false, false);
 }
 
-TEST_F(InstBindlessTest, InstrumentVertexSimpleV2) {
+TEST_F(InstBindlessTest, InstrumentVertexSimple) {
   // This test verifies that the pass will correctly instrument shader
   // doing bindless image write. This test was created by editing the SPIR-V
   // from the Simple test.
@@ -5703,10 +1657,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 2u);
+      true, 7u, 23u, false, false);
 }
 
-TEST_F(InstBindlessTest, InstrumentTeseSimpleV2) {
+TEST_F(InstBindlessTest, InstrumentTeseSimple) {
   // This test verifies that the pass will correctly instrument tessellation
   // evaluation shader doing bindless buffer load.
   //
@@ -5979,10 +1933,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + output_func, true,
-      true, 7u, 23u, false, false, 2u);
+      true, 7u, 23u, false, false);
 }
 
-TEST_F(InstBindlessTest, MultipleDebugFunctionsV2) {
+TEST_F(InstBindlessTest, MultipleDebugFunctions) {
   // Same source as Simple, but compiled -g and not optimized, especially not
   // inlined. The OpSource has had the source extracted for the sake of brevity.
 
@@ -6303,10 +2257,10 @@
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func1_before + func2_before,
       defs_after + func1_after + func2_after + output_func, true, true, 7u, 23u,
-      false, false, 2u);
+      false, false);
 }
 
-TEST_F(InstBindlessTest, RuntimeArrayV2) {
+TEST_F(InstBindlessTest, RuntimeArray) {
   // This test verifies that the pass will correctly instrument shader
   // with runtime descriptor array. This test was created by editing the
   // SPIR-V from the Simple test.
@@ -6599,10 +2553,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
-TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptorV2) {
+TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) {
   // This test verifies that the pass will correctly instrument vanilla
   // texture sample on a scalar descriptor with an initialization check if the
   // input_init_enable argument is set to true. This can happen when the
@@ -6835,10 +2789,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
-TEST_F(InstBindlessTest, SPV14AddToEntryPointV2) {
+TEST_F(InstBindlessTest, SPV14AddToEntryPoint) {
   const std::string text = R"(
 ; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
 ; CHECK: OpDecorate [[v1]] DescriptorSet 7
@@ -6891,11 +2845,10 @@
 )";
 
   SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true,
-                                               2u);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true);
 }
 
-TEST_F(InstBindlessTest, SPV14AddToEntryPointsV2) {
+TEST_F(InstBindlessTest, SPV14AddToEntryPoints) {
   const std::string text = R"(
 ; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
 ; CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]]
@@ -6950,11 +2903,10 @@
 )";
 
   SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
-  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true,
-                                               2u);
+  SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true);
 }
 
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArrayV2) {
+TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) {
   // #version 450
   // #extension GL_EXT_nonuniform_qualifier : enable
   //
@@ -7232,10 +3184,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecatedV2) {
+TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) {
   // #version 450
   // #extension GL_EXT_nonuniform_qualifier : enable
   //
@@ -7513,10 +3465,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
-TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayV2) {
+TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) {
   // Same as Deprecated but declaring as StorageBuffer Block
 
   const std::string defs_before =
@@ -7783,10 +3735,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
-TEST_F(InstBindlessTest, InstInitLoadUBOScalarV2) {
+TEST_F(InstBindlessTest, InstInitLoadUBOScalar) {
   // #version 450
   // #extension GL_EXT_nonuniform_qualifier : enable
   //
@@ -8008,10 +3960,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
-TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArrayV2) {
+TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) {
   // #version 450
   // #extension GL_EXT_nonuniform_qualifier : enable
   //
@@ -8282,10 +4234,10 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
-TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArrayV2) {
+TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) {
   // #version 450
   // #extension GL_EXT_nonuniform_qualifier : enable
   //
@@ -8551,7 +4503,7 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
 TEST_F(InstBindlessTest,
@@ -8873,7 +4825,7 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
 TEST_F(InstBindlessTest,
@@ -9197,7 +5149,7 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
 TEST_F(InstBindlessTest,
@@ -9521,7 +5473,7 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
 TEST_F(InstBindlessTest,
@@ -9845,7 +5797,7 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
 TEST_F(InstBindlessTest,
@@ -10169,7 +6121,7 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
 TEST_F(InstBindlessTest,
@@ -10493,7 +6445,7 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
 TEST_F(InstBindlessTest,
@@ -10817,7 +6769,7 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
 TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) {
@@ -11226,7 +7178,7 @@
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBindlessCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, true, true, 2u);
+      true, 7u, 23u, true, true);
 }
 
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp
index f8448a9..41ead67 100644
--- a/test/opt/inst_buff_addr_check_test.cpp
+++ b/test/opt/inst_buff_addr_check_test.cpp
@@ -310,7 +310,7 @@
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBuffAddrCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, 2u);
+      true, 7u, 23u);
 }
 
 TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
@@ -612,7 +612,7 @@
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InstBuffAddrCheckPass>(
       defs_before + func_before, defs_after + func_after + new_funcs, true,
-      true, 7u, 23u, 2u);
+      true, 7u, 23u);
 }
 
 }  // namespace
diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp
index f10d118..7afbb4c 100644
--- a/test/opt/local_ssa_elim_test.cpp
+++ b/test/opt/local_ssa_elim_test.cpp
@@ -193,7 +193,24 @@
 )";
 
   const std::string before =
-      R"(%main = OpFunction %void None %9
+      R"(
+; CHECK: = OpFunction
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK: [[outer_header:%\w+]] = OpLabel
+; CHECK-NEXT: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]]
+; CHECK-NEXT: [[i:%\w+]] = OpPhi %int %int_0 [[entry]] [[i_next:%\w+]] [[outer_be]]
+; CHECK-NEXT: OpSLessThan {{%\w+}} [[i]]
+; CHECK: [[inner_pre_header:%\w+]] = OpLabel
+; CHECK: [[inner_header:%\w+]] = OpLabel
+; CHECK-NEXT: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]]
+; CHECK-NEXT: [[j:%\w+]] = OpPhi %int %int_0 [[inner_pre_header]] [[j_next:%\w+]] [[inner_be]]
+; CHECK: [[inner_be]] = OpLabel
+; CHECK: [[f_next]] = OpFAdd %float [[inner_f]]
+; CHECK: [[j_next]] = OpIAdd %int [[j]] %int_1
+; CHECK: [[outer_be]] = OpLabel
+; CHECK: [[i_next]] = OpIAdd
+; CHECK: OpStore %fo [[outer_f]]
+%main = OpFunction %void None %9
 %24 = OpLabel
 %f = OpVariable %_ptr_Function_float Function
 %i = OpVariable %_ptr_Function_int Function
@@ -212,8 +229,8 @@
 %31 = OpLabel
 %32 = OpLoad %int %j
 %33 = OpSLessThan %bool %32 %int_4
-OpLoopMerge %29 %34 None
-OpBranchConditional %33 %34 %29
+OpLoopMerge %50 %34 None
+OpBranchConditional %33 %34 %50
 %34 = OpLabel
 %35 = OpLoad %float %f
 %36 = OpLoad %int %i
@@ -226,6 +243,8 @@
 %42 = OpIAdd %int %41 %int_1
 OpStore %j %42
 OpBranch %31
+%50 = OpLabel
+OpBranch %29
 %29 = OpLabel
 %43 = OpLoad %int %i
 %44 = OpIAdd %int %43 %int_1
@@ -238,50 +257,7 @@
 OpFunctionEnd
 )";
 
-  const std::string after =
-      R"(%main = OpFunction %void None %9
-%24 = OpLabel
-%f = OpVariable %_ptr_Function_float Function
-%i = OpVariable %_ptr_Function_int Function
-%j = OpVariable %_ptr_Function_int Function
-OpStore %f %float_0
-OpStore %i %int_0
-OpBranch %25
-%25 = OpLabel
-%47 = OpPhi %float %float_0 %24 %50 %29
-%46 = OpPhi %int %int_0 %24 %44 %29
-%27 = OpSLessThan %bool %46 %int_4
-OpLoopMerge %28 %29 None
-OpBranchConditional %27 %30 %28
-%30 = OpLabel
-OpStore %j %int_0
-OpBranch %31
-%31 = OpLabel
-%50 = OpPhi %float %47 %30 %40 %34
-%48 = OpPhi %int %int_0 %30 %42 %34
-%33 = OpSLessThan %bool %48 %int_4
-OpLoopMerge %29 %34 None
-OpBranchConditional %33 %34 %29
-%34 = OpLabel
-%38 = OpAccessChain %_ptr_Input_float %BC %46 %48
-%39 = OpLoad %float %38
-%40 = OpFAdd %float %50 %39
-OpStore %f %40
-%42 = OpIAdd %int %48 %int_1
-OpStore %j %42
-OpBranch %31
-%29 = OpLabel
-%44 = OpIAdd %int %46 %int_1
-OpStore %i %44
-OpBranch %25
-%28 = OpLabel
-OpStore %fo %47
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
-                                        true);
+  SinglePassRunAndMatch<SSARewritePass>(predefs + before, true);
 }
 
 TEST_F(LocalSSAElimTest, ForLoopWithContinue) {
diff --git a/test/opt/loop_optimizations/fusion_legal.cpp b/test/opt/loop_optimizations/fusion_legal.cpp
index 41d796f..56b0b76 100644
--- a/test/opt/loop_optimizations/fusion_legal.cpp
+++ b/test/opt/loop_optimizations/fusion_legal.cpp
@@ -3177,7 +3177,7 @@
          %21 = OpLabel
          %29 = OpSMod %6 %96 %28
          %30 = OpIEqual %17 %29 %9
-               OpSelectionMerge %23 None
+               OpSelectionMerge %sel_merge None
                OpBranchConditional %30 %31 %48
          %31 = OpLabel
          %44 = OpAccessChain %7 %41 %91 %96
@@ -3187,8 +3187,10 @@
                OpStore %47 %46
                OpBranch %32
          %48 = OpLabel
-               OpBranch %23
+               OpBranch %sel_merge
          %32 = OpLabel
+               OpBranch %sel_merge
+  %sel_merge = OpLabel
                OpBranch %23
          %23 = OpLabel
          %52 = OpIAdd %6 %96 %51
diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp
index 5c0707d..945aa78 100644
--- a/test/opt/optimizer_test.cpp
+++ b/test/opt/optimizer_test.cpp
@@ -234,7 +234,6 @@
                                               "eliminate-dead-code-aggressive",
                                               "eliminate-dead-const",
                                               "flatten-decorations",
-                                              "strip-debug",
                                               "strip-atomic-counter-memory",
                                               "generate-webgpu-initializers",
                                               "legalize-vector-shuffle",
@@ -330,35 +329,6 @@
          "OpFunctionEnd\n",
          // pass
          "flatten-decorations"},
-        // Strip Debug
-        {// input
-         "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModel\n"
-         "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical Vulkan\n"
-         "OpEntryPoint Vertex %func \"shader\"\n"
-         "OpName %main \"main\"\n"
-         "OpName %void_fn \"void_fn\"\n"
-         "%void = OpTypeVoid\n"
-         "%void_f = OpTypeFunction %void\n"
-         "%func = OpFunction %void None %void_f\n"
-         "%label = OpLabel\n"
-         "OpReturn\n"
-         "OpFunctionEnd\n",
-         // expected
-         "OpCapability Shader\n"
-         "OpCapability VulkanMemoryModel\n"
-         "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
-         "OpMemoryModel Logical Vulkan\n"
-         "OpEntryPoint Vertex %1 \"shader\"\n"
-         "%void = OpTypeVoid\n"
-         "%3 = OpTypeFunction %void\n"
-         "%1 = OpFunction %void None %3\n"
-         "%4 = OpLabel\n"
-         "OpReturn\n"
-         "OpFunctionEnd\n",
-         // pass
-         "strip-debug"},
         // Eliminate Dead Constants
         {// input
          "OpCapability Shader\n"
diff --git a/test/opt/pass_fixture.h b/test/opt/pass_fixture.h
index 53fb206..64c089d 100644
--- a/test/opt/pass_fixture.h
+++ b/test/opt/pass_fixture.h
@@ -60,8 +60,7 @@
   // from pass Process() function.
   std::tuple<std::vector<uint32_t>, Pass::Status> OptimizeToBinary(
       Pass* pass, const std::string& original, bool skip_nop) {
-    context_ =
-        std::move(BuildModule(env_, consumer_, original, assemble_options_));
+    context_ = BuildModule(env_, consumer_, original, assemble_options_);
     EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
                                   << original << std::endl;
     if (!context()) {
@@ -197,8 +196,7 @@
   // messages.
   template <typename PassT, typename... Args>
   void SinglePassRunAndFail(const std::string& original, Args&&... args) {
-    context_ =
-        std::move(BuildModule(env_, consumer_, original, assemble_options_));
+    context_ = BuildModule(env_, consumer_, original, assemble_options_);
     EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
                                   << original << std::endl;
     std::ostringstream errs;
@@ -235,8 +233,7 @@
   void RunAndCheck(const std::string& original, const std::string& expected) {
     assert(manager_->NumPasses());
 
-    context_ =
-        std::move(BuildModule(env_, nullptr, original, assemble_options_));
+    context_ = BuildModule(env_, nullptr, original, assemble_options_);
     ASSERT_NE(nullptr, context());
 
     context()->set_preserve_bindings(OptimizerOptions()->preserve_bindings_);
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp
index a305680..4118169 100644
--- a/test/opt/pass_merge_return_test.cpp
+++ b/test/opt/pass_merge_return_test.cpp
@@ -1390,7 +1390,6 @@
                OpLoopMerge %36 %40 None
                OpBranch %35
          %35 = OpLabel
-               OpSelectionMerge %40 None
                OpBranchConditional %77 %39 %40
          %39 = OpLabel
                OpReturnValue %16
@@ -1402,7 +1401,6 @@
                OpLoopMerge %45 %49 None
                OpBranch %44
          %44 = OpLabel
-               OpSelectionMerge %49 None
                OpBranchConditional %77 %48 %49
          %48 = OpLabel
                OpReturnValue %16
@@ -1415,7 +1413,6 @@
                OpLoopMerge %64 %68 None
                OpBranch %63
          %63 = OpLabel
-               OpSelectionMerge %68 None
                OpBranchConditional %77 %67 %68
          %67 = OpLabel
                OpReturnValue %16
@@ -1813,12 +1810,14 @@
                OpLoopMerge %11 %12 None
                OpBranch %13
          %13 = OpLabel
-               OpLoopMerge %12 %14 None
-               OpBranchConditional %8 %15 %12
+               OpLoopMerge %18 %14 None
+               OpBranchConditional %8 %15 %18
          %15 = OpLabel
                OpReturn
          %14 = OpLabel
                OpBranch %13
+         %18 = OpLabel
+               OpBranch %12
          %12 = OpLabel
          %16 = OpUndef %float
                OpBranchConditional %8 %10 %11
diff --git a/test/opt/register_liveness.cpp b/test/opt/register_liveness.cpp
index cb973d2..7cb210f 100644
--- a/test/opt/register_liveness.cpp
+++ b/test/opt/register_liveness.cpp
@@ -1277,6 +1277,46 @@
   }
 }
 
+// Test that register liveness does not fail when there is an unreachable block.
+// We are not testing if the liveness is computed correctly because the specific
+// results do not matter for unreachable blocks.
+TEST_F(PassClassTest, RegisterLivenessWithUnreachableBlock) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginLowerLeft
+               OpSource GLSL 330
+               OpSourceExtension "GL_ARB_shading_language_420pack"
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+          %2 = OpFunction %void None %4
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpLoopMerge %7 %8 None
+               OpBranch %9
+          %9 = OpLabel
+               OpBranch %7
+          %8 = OpLabel
+               OpBranch %6
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  Function* f = &*module->begin();
+  LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis();
+  liveness_analysis->Get(f);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/strip_debug_info_test.cpp b/test/opt/strip_debug_info_test.cpp
index 2f2ff46..088bba9 100644
--- a/test/opt/strip_debug_info_test.cpp
+++ b/test/opt/strip_debug_info_test.cpp
@@ -152,6 +152,53 @@
                                             /* do_validation */ true);
 }
 
+TEST_F(StripDebugStringTest, OpStringRemovedWithNonSemantic) {
+  std::vector<const char*> input{
+      // clang-format off
+                     "OpCapability Shader",
+                     "OpExtension \"SPV_KHR_non_semantic_info\"",
+                "%1 = OpExtInstImport \"NonSemantic.Testing.Set\"",
+                     "OpMemoryModel Logical GLSL450",
+                     "OpEntryPoint Vertex %2 \"main\"",
+                // this string is not referenced, should be removed fully
+                "%3 = OpString \"minimal.vert\"",
+                     "OpName %3 \"bob\"",
+                // this string is referenced and cannot be removed,
+                // but the name should be
+                "%4 = OpString \"secondary.inc\"",
+                     "OpName %4 \"sue\"",
+             "%void = OpTypeVoid",
+                "%6 = OpTypeFunction %void",
+                "%2 = OpFunction %void None %6",
+                "%7 = OpLabel",
+                "%8 = OpExtInst %void %1 5 %4",
+                     "OpReturn",
+                     "OpFunctionEnd",
+      // clang-format on
+  };
+  std::vector<const char*> output{
+      // clang-format off
+                     "OpCapability Shader",
+                     "OpExtension \"SPV_KHR_non_semantic_info\"",
+                "%1 = OpExtInstImport \"NonSemantic.Testing.Set\"",
+                     "OpMemoryModel Logical GLSL450",
+                     "OpEntryPoint Vertex %2 \"main\"",
+                "%4 = OpString \"secondary.inc\"",
+             "%void = OpTypeVoid",
+                "%6 = OpTypeFunction %void",
+                "%2 = OpFunction %void None %6",
+                "%7 = OpLabel",
+                "%8 = OpExtInst %void %1 5 %4",
+                     "OpReturn",
+                     "OpFunctionEnd",
+      // clang-format on
+  };
+  SinglePassRunAndCheck<StripDebugInfoPass>(JoinAllInsts(input),
+                                            JoinAllInsts(output),
+                                            /* skip_nop = */ false,
+                                            /* do_validation */ true);
+}
+
 using StripDebugInfoTest = PassTest<::testing::TestWithParam<const char*>>;
 
 TEST_P(StripDebugInfoTest, Kind) {
diff --git a/test/opt/strip_reflect_info_test.cpp b/test/opt/strip_reflect_info_test.cpp
index a9cfd47..5db34b7 100644
--- a/test/opt/strip_reflect_info_test.cpp
+++ b/test/opt/strip_reflect_info_test.cpp
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include <string>
+#include "gmock/gmock.h"
+
+#include "spirv-tools/optimizer.hpp"
 
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
@@ -23,6 +26,50 @@
 
 using StripLineReflectInfoTest = PassTest<::testing::Test>;
 
+// This test acts as an end-to-end code example on how to strip
+// reflection info from a SPIR-V module.  Use this code pattern
+// when you have compiled HLSL code with Glslang or DXC using
+// option -fhlsl_functionality1 to insert reflection information,
+// but then want to filter out the extra instructions before sending
+// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1.
+TEST_F(StripLineReflectInfoTest, StripReflectEnd2EndExample) {
+  // This is a non-sensical example, but exercises the instructions.
+  std::string before = R"(OpCapability Shader
+OpCapability Linkage
+OpExtension "SPV_GOOGLE_decorate_string"
+OpExtension "SPV_GOOGLE_hlsl_functionality1"
+OpMemoryModel Logical Simple
+OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar"
+OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness"
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+)";
+  SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  std::vector<uint32_t> binary_in;
+  tools.Assemble(before, &binary_in);
+
+  // Instantiate the optimizer, and run the strip-reflection-info
+  // pass over the |binary_in| module, and place the modified module
+  // into |binary_out|.
+  spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1);
+  optimizer.RegisterPass(spvtools::CreateStripReflectInfoPass());
+  std::vector<uint32_t> binary_out;
+  optimizer.Run(binary_in.data(), binary_in.size(), &binary_out);
+
+  // Check results
+  std::string disassembly;
+  tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
+  std::string after = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical Simple
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+)";
+  EXPECT_THAT(disassembly, testing::Eq(after));
+}
+
+// This test is functionally the same as the end-to-end test above,
+// but uses the test SinglePassRunAndCheck test fixture instead.
 TEST_F(StripLineReflectInfoTest, StripHlslSemantic) {
   // This is a non-sensical example, but exercises the instructions.
   std::string before = R"(OpCapability Shader
diff --git a/test/opt/value_table_test.cpp b/test/opt/value_table_test.cpp
index 0b7530c..a0942cc 100644
--- a/test/opt/value_table_test.cpp
+++ b/test/opt/value_table_test.cpp
@@ -653,6 +653,37 @@
   EXPECT_NE(vtable.GetValueNumber(phi1), vtable.GetValueNumber(phi2));
 }
 
+// Test to make sure that OpPhi instructions with no in operands are handled
+// correctly.
+TEST_F(ValueTableTest, EmptyPhiTest) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource GLSL 430
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+          %2 = OpFunction %void None %4
+          %7 = OpLabel
+               OpSelectionMerge %8 None
+               OpBranchConditional %true %9 %8
+          %9 = OpLabel
+               OpKill
+          %8 = OpLabel
+         %10 = OpPhi %bool
+               OpReturn
+               OpFunctionEnd
+  )";
+  auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+  ValueNumberTable vtable(context.get());
+  Instruction* inst = context->get_def_use_mgr()->GetDef(10);
+  vtable.GetValueNumber(inst);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/reduce/reducer_test.cpp b/test/reduce/reducer_test.cpp
index a650d3b..59f2803 100644
--- a/test/reduce/reducer_test.cpp
+++ b/test/reduce/reducer_test.cpp
@@ -125,19 +125,21 @@
          %29 = OpAccessChain %28 %27 %9
          %30 = OpLoad %24 %29
          %32 = OpFOrdGreaterThan %22 %30 %31
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %32 %33 %46
          %33 = OpLabel
          %40 = OpFAdd %24 %71 %30
          %45 = OpISub %6 %73 %21
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
          %50 = OpFMul %24 %71 %30
          %54 = OpSDiv %6 %73 %21
-               OpBranch %34
-         %34 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
          %77 = OpPhi %6 %45 %33 %54 %46
          %76 = OpPhi %24 %40 %33 %50 %46
+               OpBranch %34
+         %34 = OpLabel
          %57 = OpIAdd %6 %70 %56
                OpBranch %10
          %12 = OpLabel
@@ -193,11 +195,13 @@
                OpLoopMerge %12 %34 None
                OpBranchConditional %100 %11 %12
          %11 = OpLabel
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %100 %33 %46
          %33 = OpLabel
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
                OpBranch %34
          %34 = OpLabel
                OpBranch %10
@@ -345,7 +349,6 @@
                OpLoopMerge %33 %38 None
                OpBranch %32
          %32 = OpLabel
-               OpSelectionMerge %38 None
                OpBranchConditional %30 %37 %38
          %37 = OpLabel
                OpSelectionMerge %42 None
diff --git a/test/reduce/validation_during_reduction_test.cpp b/test/reduce/validation_during_reduction_test.cpp
index 4051bac..2981c2e 100644
--- a/test/reduce/validation_during_reduction_test.cpp
+++ b/test/reduce/validation_during_reduction_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "source/reduce/reducer.h"
-
 #include "source/reduce/reduction_opportunity.h"
 #include "source/reduce/remove_instruction_reduction_opportunity.h"
 #include "test/reduce/reduce_test_util.h"
@@ -23,8 +22,8 @@
 namespace {
 
 using opt::Function;
-using opt::IRContext;
 using opt::Instruction;
+using opt::IRContext;
 
 // A dumb reduction opportunity finder that finds opportunities to remove global
 // values regardless of whether they are referenced. This is very likely to make
@@ -181,19 +180,21 @@
          %29 = OpAccessChain %28 %27 %9
          %30 = OpLoad %24 %29
          %32 = OpFOrdGreaterThan %22 %30 %31
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %32 %33 %46
          %33 = OpLabel
          %40 = OpFAdd %24 %71 %30
          %45 = OpISub %6 %73 %21
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
          %50 = OpFMul %24 %71 %30
          %54 = OpSDiv %6 %73 %21
-               OpBranch %34
-         %34 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
          %77 = OpPhi %6 %45 %33 %54 %46
          %76 = OpPhi %24 %40 %33 %50 %46
+               OpBranch %34
+         %34 = OpLabel
          %57 = OpIAdd %6 %70 %56
                OpBranch %10
          %12 = OpLabel
@@ -303,19 +304,21 @@
          %29 = OpAccessChain %28 %27 %9
          %30 = OpLoad %24 %29
          %32 = OpFOrdGreaterThan %22 %30 %31
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %32 %33 %46
          %33 = OpLabel
          %40 = OpFAdd %24 %71 %30
          %45 = OpISub %6 %73 %21
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
          %50 = OpFMul %24 %71 %30
          %54 = OpSDiv %6 %73 %21
-               OpBranch %34
-         %34 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
          %77 = OpPhi %6 %45 %33 %54 %46
          %76 = OpPhi %24 %40 %33 %50 %46
+               OpBranch %34
+         %34 = OpLabel
          %57 = OpIAdd %6 %70 %56
                OpBranch %10
          %12 = OpLabel
@@ -392,19 +395,21 @@
          %29 = OpAccessChain %28 %27 %9
          %30 = OpLoad %24 %29
          %32 = OpFOrdGreaterThan %22 %30 %31
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %32 %33 %46
          %33 = OpLabel
          %40 = OpFAdd %24 %71 %30
          %45 = OpISub %6 %73 %21
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
          %50 = OpFMul %24 %71 %30
          %54 = OpSDiv %6 %73 %21
-               OpBranch %34
-         %34 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
          %77 = OpPhi %6 %45 %33 %54 %46
          %76 = OpPhi %24 %40 %33 %50 %46
+               OpBranch %34
+         %34 = OpLabel
          %57 = OpIAdd %6 %70 %56
                OpBranch %10
          %12 = OpLabel
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt
index cee95ca..99f9780 100644
--- a/test/tools/CMakeLists.txt
+++ b/test/tools/CMakeLists.txt
@@ -12,7 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-spirv_add_nosetests(expect)
-spirv_add_nosetests(spirv_test_framework)
-
+add_test(NAME spirv-tools_expect_unittests
+         COMMAND ${PYTHON_EXECUTABLE} -m unittest expect_unittest.py
+         WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+add_test(NAME spirv-tools_spirv_test_framework_unittests
+         COMMAND ${PYTHON_EXECUTABLE} -m unittest spirv_test_framework_unittest.py
+         WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
 add_subdirectory(opt)
diff --git a/test/tools/expect.py b/test/tools/expect.py
index 52999ce..0b51adc 100755
--- a/test/tools/expect.py
+++ b/test/tools/expect.py
@@ -510,7 +510,7 @@
     if not status.stderr:
       return False, 'Expected error message, but no output on stderr'
     if self.expected_error_substr not in convert_to_unix_line_endings(
-        status.stderr.decode('utf8')):
+        status.stderr):
       return False, ('Incorrect stderr output:\n{act}\n'
                      'Expected substring not found in stderr:\n{exp}'.format(
                          act=status.stderr, exp=self.expected_error_substr))
@@ -530,7 +530,7 @@
                      ' command execution')
     if not status.stderr:
       return False, 'Expected warning message, but no output on stderr'
-    if self.expected_warning != convert_to_unix_line_endings(status.stderr.decode('utf8')):
+    if self.expected_warning != convert_to_unix_line_endings(status.stderr):
       return False, ('Incorrect stderr output:\n{act}\n'
                      'Expected:\n{exp}'.format(
                          act=status.stderr, exp=self.expected_warning))
@@ -590,16 +590,16 @@
       if not status.stdout:
         return False, 'Expected something on stdout'
     elif type(self.expected_stdout) == str:
-      if self.expected_stdout != convert_to_unix_line_endings(status.stdout.decode('utf8')):
+      if self.expected_stdout != convert_to_unix_line_endings(status.stdout):
         return False, ('Incorrect stdout output:\n{ac}\n'
                        'Expected:\n{ex}'.format(
                            ac=status.stdout, ex=self.expected_stdout))
     else:
-      converted = convert_to_unix_line_endings(status.stdout.decode('utf8'))
+      converted = convert_to_unix_line_endings(status.stdout)
       if not self.expected_stdout.search(converted):
         return False, ('Incorrect stdout output:\n{ac}\n'
                        'Expected to match regex:\n{ex}'.format(
-                           ac=status.stdout.decode('utf8'), ex=self.expected_stdout.pattern))
+                           ac=status.stdout, ex=self.expected_stdout.pattern))
     return True, ''
 
 
@@ -624,13 +624,13 @@
       if not status.stderr:
         return False, 'Expected something on stderr'
     elif type(self.expected_stderr) == str:
-      if self.expected_stderr != convert_to_unix_line_endings(status.stderr.decode('utf8')):
+      if self.expected_stderr != convert_to_unix_line_endings(status.stderr):
         return False, ('Incorrect stderr output:\n{ac}\n'
                        'Expected:\n{ex}'.format(
                            ac=status.stderr, ex=self.expected_stderr))
     else:
       if not self.expected_stderr.search(
-          convert_to_unix_line_endings(status.stderr.decode('utf8'))):
+          convert_to_unix_line_endings(status.stderr)):
         return False, ('Incorrect stderr output:\n{ac}\n'
                        'Expected to match regex:\n{ex}'.format(
                            ac=status.stderr, ex=self.expected_stderr.pattern))
@@ -695,7 +695,7 @@
     # Collect all the output lines containing a pass name.
     pass_names = []
     pass_name_re = re.compile(r'.*IR before pass (?P<pass_name>[\S]+)')
-    for line in status.stderr.decode('utf8').splitlines():
+    for line in status.stderr.splitlines():
       match = pass_name_re.match(line)
       if match:
         pass_names.append(match.group('pass_name'))
diff --git a/test/tools/expect_nosetest.py b/test/tools/expect_nosetest.py
deleted file mode 100755
index b591a2d..0000000
--- a/test/tools/expect_nosetest.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (c) 2018 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Tests for the expect module."""
-
-import expect
-from spirv_test_framework import TestStatus
-from nose.tools import assert_equal, assert_true, assert_false
-import re
-
-
-def nosetest_get_object_name():
-  """Tests get_object_filename()."""
-  source_and_object_names = [('a.vert', 'a.vert.spv'), ('b.frag', 'b.frag.spv'),
-                             ('c.tesc', 'c.tesc.spv'), ('d.tese', 'd.tese.spv'),
-                             ('e.geom', 'e.geom.spv'), ('f.comp', 'f.comp.spv'),
-                             ('file', 'file.spv'), ('file.', 'file.spv'),
-                             ('file.uk',
-                              'file.spv'), ('file.vert.',
-                                            'file.vert.spv'), ('file.vert.bla',
-                                                               'file.vert.spv')]
-  actual_object_names = [
-      expect.get_object_filename(f[0]) for f in source_and_object_names
-  ]
-  expected_object_names = [f[1] for f in source_and_object_names]
-
-  assert_equal(actual_object_names, expected_object_names)
-
-
-class TestStdoutMatchADotC(expect.StdoutMatch):
-  expected_stdout = re.compile('a.c')
-
-
-def nosetest_stdout_match_regex_has_match():
-  test = TestStdoutMatchADotC()
-  status = TestStatus(
-      test_manager=None,
-      returncode=0,
-      stdout='0abc1',
-      stderr=None,
-      directory=None,
-      inputs=None,
-      input_filenames=None)
-  assert_true(test.check_stdout_match(status)[0])
-
-
-def nosetest_stdout_match_regex_no_match():
-  test = TestStdoutMatchADotC()
-  status = TestStatus(
-      test_manager=None,
-      returncode=0,
-      stdout='ab',
-      stderr=None,
-      directory=None,
-      inputs=None,
-      input_filenames=None)
-  assert_false(test.check_stdout_match(status)[0])
-
-
-def nosetest_stdout_match_regex_empty_stdout():
-  test = TestStdoutMatchADotC()
-  status = TestStatus(
-      test_manager=None,
-      returncode=0,
-      stdout='',
-      stderr=None,
-      directory=None,
-      inputs=None,
-      input_filenames=None)
-  assert_false(test.check_stdout_match(status)[0])
diff --git a/test/tools/expect_unittest.py b/test/tools/expect_unittest.py
new file mode 100644
index 0000000..a28de1b
--- /dev/null
+++ b/test/tools/expect_unittest.py
@@ -0,0 +1,82 @@
+# Copyright (c) 2019 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests for the expect module."""
+
+import expect
+from spirv_test_framework import TestStatus
+import re
+import unittest
+
+
+class TestStdoutMatchADotC(expect.StdoutMatch):
+    expected_stdout = re.compile('a.c')
+
+
+class TestExpect(unittest.TestCase):
+    def test_get_object_name(self):
+        """Tests get_object_filename()."""
+        source_and_object_names = [('a.vert', 'a.vert.spv'),
+                                   ('b.frag', 'b.frag.spv'),
+                                   ('c.tesc', 'c.tesc.spv'),
+                                   ('d.tese', 'd.tese.spv'),
+                                   ('e.geom', 'e.geom.spv'),
+                                   ('f.comp', 'f.comp.spv'),
+                                   ('file', 'file.spv'), ('file.', 'file.spv'),
+                                   ('file.uk',
+                                    'file.spv'), ('file.vert.',
+                                                  'file.vert.spv'),
+                                   ('file.vert.bla',
+                                    'file.vert.spv')]
+        actual_object_names = [
+            expect.get_object_filename(f[0]) for f in source_and_object_names
+        ]
+        expected_object_names = [f[1] for f in source_and_object_names]
+
+        self.assertEqual(actual_object_names, expected_object_names)
+
+    def test_stdout_match_regex_has_match(self):
+        test = TestStdoutMatchADotC()
+        status = TestStatus(
+            test_manager=None,
+            returncode=0,
+            stdout=b'0abc1',
+            stderr=None,
+            directory=None,
+            inputs=None,
+            input_filenames=None)
+        self.assertTrue(test.check_stdout_match(status)[0])
+
+    def test_stdout_match_regex_no_match(self):
+        test = TestStdoutMatchADotC()
+        status = TestStatus(
+            test_manager=None,
+            returncode=0,
+            stdout=b'ab',
+            stderr=None,
+            directory=None,
+            inputs=None,
+            input_filenames=None)
+        self.assertFalse(test.check_stdout_match(status)[0])
+
+    def test_stdout_match_regex_empty_stdout(self):
+        test = TestStdoutMatchADotC()
+        status = TestStatus(
+            test_manager=None,
+            returncode=0,
+            stdout=b'',
+            stderr=None,
+            directory=None,
+            inputs=None,
+            input_filenames=None)
+        self.assertFalse(test.check_stdout_match(status)[0])
diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py
index b34a168..2f6c0a7 100644
--- a/test/tools/opt/flags.py
+++ b/test/tools/opt/flags.py
@@ -187,28 +187,35 @@
       'eliminate-dead-branches',
       'merge-return',
       'inline-entry-points-exhaustive',
-      'eliminate-dead-code-aggressive',
+      'eliminate-dead-functions',
       'private-to-local',
-      'scalar-replacement=100',
-      'convert-local-access-chains',
-      'eliminate-local-single-block',
-      'eliminate-local-single-store',
-      'eliminate-dead-code-aggressive',
-      'simplify-instructions',
-      'eliminate-dead-inserts',
+      'scalar-replacement=0',
       'ssa-rewrite',
-      'eliminate-dead-code-aggressive',
       'ccp',
+      'loop-unroll',
+      'eliminate-dead-branches',
+      'simplify-instructions',
+      'scalar-replacement=0',
+      'eliminate-local-single-store',
+      'if-conversion',
+      'simplify-instructions',
       'eliminate-dead-code-aggressive',
       'eliminate-dead-branches',
-      'if-conversion',
-      'eliminate-dead-code-aggressive',
       'merge-blocks',
-      'simplify-instructions',
-      'eliminate-dead-inserts',
-      'redundancy-elimination',
-      'cfg-cleanup',
+      'convert-local-access-chains',
+      'eliminate-local-single-block',
       'eliminate-dead-code-aggressive',
+      'copy-propagate-arrays',
+      'vector-dce',
+      'eliminate-dead-inserts',
+      'eliminate-dead-members',
+      'eliminate-local-single-store',
+      'merge-blocks',
+      'ssa-rewrite',
+      'redundancy-elimination',
+      'simplify-instructions',
+      'eliminate-dead-code-aggressive',
+      'cfg-cleanup',
   ]
   shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
   output = placeholder.TempFileName('output.spv')
diff --git a/test/tools/spirv_test_framework.py b/test/tools/spirv_test_framework.py
index d8d64f3..42f83c6 100755
--- a/test/tools/spirv_test_framework.py
+++ b/test/tools/spirv_test_framework.py
@@ -70,7 +70,7 @@
 
 
 def get_all_superclasses(cls):
-  """Returns all superclasses of a given class.
+  """Returns all superclasses of a given class. Omits root 'object' superclass.
 
     Returns:
       A list of superclasses of the given class. The order guarantees that
@@ -83,11 +83,12 @@
   classes = []
   for superclass in cls.__bases__:
     for c in get_all_superclasses(superclass):
-      if c not in classes:
+      if c is not object and c not in classes:
         classes.append(c)
   for superclass in cls.__bases__:
-    if superclass not in classes:
+    if superclass is not object and superclass not in classes:
       classes.append(superclass)
+
   return classes
 
 
@@ -142,8 +143,28 @@
                inputs, input_filenames):
     self.test_manager = test_manager
     self.returncode = returncode
-    self.stdout = stdout
-    self.stderr = stderr
+    # Some of our MacOS bots still run Python 2, so need to be backwards
+    # compatible here.
+    if type(stdout) is not str:     
+      if sys.version_info[0] is 2:
+       self.stdout = stdout.decode('utf-8')
+      elif sys.version_info[0] is 3:
+        self.stdout = str(stdout, encoding='utf-8') if stdout is not None else stdout
+      else:
+        raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info))
+    else:
+      self.stdout = stdout
+    
+    if type(stderr) is not str:     
+      if sys.version_info[0] is 2:
+       self.stderr = stderr.decode('utf-8')
+      elif sys.version_info[0] is 3:
+        self.stderr = str(stderr, encoding='utf-8') if stderr is not None else stderr
+      else:
+        raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info))
+    else:
+      self.stderr = stderr
+
     # temporary directory where the test runs
     self.directory = directory
     # List of inputs, as PlaceHolder objects.
diff --git a/test/tools/spirv_test_framework_nosetest.py b/test/tools/spirv_test_framework_nosetest.py
deleted file mode 100755
index c0fbed5..0000000
--- a/test/tools/spirv_test_framework_nosetest.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Copyright (c) 2018 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from spirv_test_framework import get_all_test_methods, get_all_superclasses
-from nose.tools import assert_equal, with_setup
-
-
-# Classes to be used in testing get_all_{superclasses|test_methods}()
-class Root:
-
-  def check_root(self):
-    pass
-
-
-class A(Root):
-
-  def check_a(self):
-    pass
-
-
-class B(Root):
-
-  def check_b(self):
-    pass
-
-
-class C(Root):
-
-  def check_c(self):
-    pass
-
-
-class D(Root):
-
-  def check_d(self):
-    pass
-
-
-class E(Root):
-
-  def check_e(self):
-    pass
-
-
-class H(B, C, D):
-
-  def check_h(self):
-    pass
-
-
-class I(E):
-
-  def check_i(self):
-    pass
-
-
-class O(H, I):
-
-  def check_o(self):
-    pass
-
-
-class U(A, O):
-
-  def check_u(self):
-    pass
-
-
-class X(U, A):
-
-  def check_x(self):
-    pass
-
-
-class R1:
-
-  def check_r1(self):
-    pass
-
-
-class R2:
-
-  def check_r2(self):
-    pass
-
-
-class Multi(R1, R2):
-
-  def check_multi(self):
-    pass
-
-
-def nosetest_get_all_superclasses():
-  """Tests get_all_superclasses()."""
-
-  assert_equal(get_all_superclasses(A), [Root])
-  assert_equal(get_all_superclasses(B), [Root])
-  assert_equal(get_all_superclasses(C), [Root])
-  assert_equal(get_all_superclasses(D), [Root])
-  assert_equal(get_all_superclasses(E), [Root])
-
-  assert_equal(get_all_superclasses(H), [Root, B, C, D])
-  assert_equal(get_all_superclasses(I), [Root, E])
-
-  assert_equal(get_all_superclasses(O), [Root, B, C, D, E, H, I])
-
-  assert_equal(get_all_superclasses(U), [Root, B, C, D, E, H, I, A, O])
-  assert_equal(get_all_superclasses(X), [Root, B, C, D, E, H, I, A, O, U])
-
-  assert_equal(get_all_superclasses(Multi), [R1, R2])
-
-
-def nosetest_get_all_methods():
-  """Tests get_all_test_methods()."""
-  assert_equal(get_all_test_methods(A), ['check_root', 'check_a'])
-  assert_equal(get_all_test_methods(B), ['check_root', 'check_b'])
-  assert_equal(get_all_test_methods(C), ['check_root', 'check_c'])
-  assert_equal(get_all_test_methods(D), ['check_root', 'check_d'])
-  assert_equal(get_all_test_methods(E), ['check_root', 'check_e'])
-
-  assert_equal(
-      get_all_test_methods(H),
-      ['check_root', 'check_b', 'check_c', 'check_d', 'check_h'])
-  assert_equal(get_all_test_methods(I), ['check_root', 'check_e', 'check_i'])
-
-  assert_equal(
-      get_all_test_methods(O), [
-          'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
-          'check_i', 'check_o'
-      ])
-
-  assert_equal(
-      get_all_test_methods(U), [
-          'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
-          'check_i', 'check_a', 'check_o', 'check_u'
-      ])
-  assert_equal(
-      get_all_test_methods(X), [
-          'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
-          'check_i', 'check_a', 'check_o', 'check_u', 'check_x'
-      ])
-
-  assert_equal(
-      get_all_test_methods(Multi), ['check_r1', 'check_r2', 'check_multi'])
diff --git a/test/tools/spirv_test_framework_unittest.py b/test/tools/spirv_test_framework_unittest.py
new file mode 100644
index 0000000..e64e86c
--- /dev/null
+++ b/test/tools/spirv_test_framework_unittest.py
@@ -0,0 +1,158 @@
+# Copyright (c) 2019 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests for the spirv test framework module."""
+
+from spirv_test_framework import get_all_test_methods, get_all_superclasses
+import unittest
+
+# Classes to be used in testing get_all_{superclasses|test_methods}()
+
+
+class Root:
+
+    def check_root(self):
+        pass
+
+
+class A(Root):
+
+    def check_a(self):
+        pass
+
+
+class B(Root):
+
+    def check_b(self):
+        pass
+
+
+class C(Root):
+
+    def check_c(self):
+        pass
+
+
+class D(Root):
+
+    def check_d(self):
+        pass
+
+
+class E(Root):
+
+    def check_e(self):
+        pass
+
+
+class H(B, C, D):
+
+    def check_h(self):
+        pass
+
+
+class I(E):
+
+    def check_i(self):
+        pass
+
+
+class O(H, I):
+
+    def check_o(self):
+        pass
+
+
+class U(A, O):
+
+    def check_u(self):
+        pass
+
+
+class X(U, A):
+
+    def check_x(self):
+        pass
+
+
+class R1:
+
+    def check_r1(self):
+        pass
+
+
+class R2:
+
+    def check_r2(self):
+        pass
+
+
+class Multi(R1, R2):
+
+    def check_multi(self):
+        pass
+
+
+class TestSpirvTestFramework(unittest.TestCase):
+    def test_get_all_superclasses(self):
+        self.assertEqual(get_all_superclasses(A), [Root])
+        self.assertEqual(get_all_superclasses(B), [Root])
+        self.assertEqual(get_all_superclasses(C), [Root])
+        self.assertEqual(get_all_superclasses(D), [Root])
+        self.assertEqual(get_all_superclasses(E), [Root])
+
+        self.assertEqual(get_all_superclasses(H), [Root, B, C, D])
+        self.assertEqual(get_all_superclasses(I), [Root, E])
+
+        self.assertEqual(get_all_superclasses(O), [Root, B, C, D, E, H, I])
+
+        self.assertEqual(get_all_superclasses(
+            U), [Root, B, C, D, E, H, I, A, O])
+        self.assertEqual(get_all_superclasses(
+            X), [Root, B, C, D, E, H, I, A, O, U])
+
+        self.assertEqual(get_all_superclasses(Multi), [R1, R2])
+
+    def test_get_all_methods(self):
+        self.assertEqual(get_all_test_methods(A), ['check_root', 'check_a'])
+        self.assertEqual(get_all_test_methods(B), ['check_root', 'check_b'])
+        self.assertEqual(get_all_test_methods(C), ['check_root', 'check_c'])
+        self.assertEqual(get_all_test_methods(D), ['check_root', 'check_d'])
+        self.assertEqual(get_all_test_methods(E), ['check_root', 'check_e'])
+
+        self.assertEqual(
+            get_all_test_methods(H),
+            ['check_root', 'check_b', 'check_c', 'check_d', 'check_h'])
+        self.assertEqual(get_all_test_methods(
+            I), ['check_root', 'check_e', 'check_i'])
+
+        self.assertEqual(
+            get_all_test_methods(O), [
+                'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
+                'check_i', 'check_o'
+            ])
+
+        self.assertEqual(
+            get_all_test_methods(U), [
+                'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
+                'check_i', 'check_a', 'check_o', 'check_u'
+            ])
+
+        self.assertEqual(
+            get_all_test_methods(X), [
+                'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h',
+                'check_i', 'check_a', 'check_o', 'check_u', 'check_x'
+            ])
+
+        self.assertEqual(
+            get_all_test_methods(Multi), ['check_r1', 'check_r2', 'check_multi'])
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index d4bfe1d..138e711 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -70,6 +70,7 @@
        val_memory_test.cpp
        val_misc_test.cpp
        val_modes_test.cpp
+       val_non_semantic_test.cpp
        val_non_uniform_test.cpp
        val_opencl_test.cpp
        val_primitives_test.cpp
diff --git a/test/val/val_adjacency_test.cpp b/test/val/val_adjacency_test.cpp
index e61c03d..0b09de0 100644
--- a/test/val/val_adjacency_test.cpp
+++ b/test/val/val_adjacency_test.cpp
@@ -315,6 +315,243 @@
                         "non-OpPhi instructions"));
 }
 
+TEST_F(ValidateAdjacency, NonSemanticBeforeOpPhiBad) {
+  const std::string body = R"(
+OpSelectionMerge %end_label None
+OpBranchConditional %true %true_label %false_label
+%true_label = OpLabel
+OpBranch %end_label
+%false_label = OpLabel
+OpBranch %end_label
+%end_label = OpLabel
+%dummy = OpExtInst %void %extinst 123 %int_1
+%result = OpPhi %bool %true %true_label %false %false_label
+)";
+
+  const std::string extra = R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpPhi must appear within a non-entry block before all "
+                        "non-OpPhi instructions"));
+}
+
+TEST_F(ValidateAdjacency, NonSemanticBetweenOpPhiBad) {
+  const std::string body = R"(
+OpSelectionMerge %end_label None
+OpBranchConditional %true %true_label %false_label
+%true_label = OpLabel
+OpBranch %end_label
+%false_label = OpLabel
+OpBranch %end_label
+%end_label = OpLabel
+%result1 = OpPhi %bool %true %true_label %false %false_label
+%dummy = OpExtInst %void %extinst 123 %int_1
+%result2 = OpPhi %bool %true %true_label %false %false_label
+)";
+
+  const std::string extra = R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpPhi must appear within a non-entry block before all "
+                        "non-OpPhi instructions"));
+}
+
+TEST_F(ValidateAdjacency, NonSemanticAfterOpPhiGood) {
+  const std::string body = R"(
+OpSelectionMerge %end_label None
+OpBranchConditional %true %true_label %false_label
+%true_label = OpLabel
+OpBranch %end_label
+%false_label = OpLabel
+OpBranch %end_label
+%end_label = OpLabel
+OpLine %string 0 0
+%result = OpPhi %bool %true %true_label %false %false_label
+%dummy = OpExtInst %void %extinst 123 %int_1
+)";
+
+  const std::string extra = R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateAdjacency, NonSemanticBeforeOpFunctionParameterBad) {
+  const std::string body = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+
+%string = OpString ""
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%zero = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%func = OpTypeFunction %void
+%func_int = OpTypePointer Function %int
+%paramfunc_type = OpTypeFunction %void %int %int
+
+%paramfunc = OpFunction %void None %paramfunc_type
+%dummy = OpExtInst %void %extinst 123 %int_1
+%a = OpFunctionParameter %int
+%b = OpFunctionParameter %int
+%paramfunc_entry = OpLabel
+OpReturn
+OpFunctionEnd
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Non-semantic OpExtInst within function definition "
+                        "must appear in a block"));
+}
+
+TEST_F(ValidateAdjacency, NonSemanticBetweenOpFunctionParameterBad) {
+  const std::string body = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+
+%string = OpString ""
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%zero = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%func = OpTypeFunction %void
+%func_int = OpTypePointer Function %int
+%paramfunc_type = OpTypeFunction %void %int %int
+
+%paramfunc = OpFunction %void None %paramfunc_type
+%a = OpFunctionParameter %int
+%dummy = OpExtInst %void %extinst 123 %int_1
+%b = OpFunctionParameter %int
+%paramfunc_entry = OpLabel
+OpReturn
+OpFunctionEnd
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Non-semantic OpExtInst within function definition "
+                        "must appear in a block"));
+}
+
+TEST_F(ValidateAdjacency, NonSemanticAfterOpFunctionParameterGood) {
+  const std::string body = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+
+%string = OpString ""
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%zero = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%func = OpTypeFunction %void
+%func_int = OpTypePointer Function %int
+%paramfunc_type = OpTypeFunction %void %int %int
+
+%paramfunc = OpFunction %void None %paramfunc_type
+%a = OpFunctionParameter %int
+%b = OpFunctionParameter %int
+%paramfunc_entry = OpLabel
+%dummy = OpExtInst %void %extinst 123 %int_1
+OpReturn
+OpFunctionEnd
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateAdjacency, NonSemanticBetweenFunctionsGood) {
+  const std::string body = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+
+%string = OpString ""
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%zero = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%func = OpTypeFunction %void
+%func_int = OpTypePointer Function %int
+%paramfunc_type = OpTypeFunction %void %int %int
+
+%paramfunc = OpFunction %void None %paramfunc_type
+%a = OpFunctionParameter %int
+%b = OpFunctionParameter %int
+%paramfunc_entry = OpLabel
+OpReturn
+OpFunctionEnd
+
+%dummy = OpExtInst %void %extinst 123 %int_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_F(ValidateAdjacency, OpVariableInFunctionGood) {
   const std::string body = R"(
 OpLine %string 1 1
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index f06f36c..8d8de37 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -23,7 +23,6 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-
 #include "source/diagnostic.h"
 #include "source/spirv_target_env.h"
 #include "source/val/validate.h"
@@ -1355,7 +1354,7 @@
   }
   if (cap == SpvCapabilityShader) {
     branch.AppendBody("OpLoopMerge %merge %target None\n");
-    body.AppendBody("OpSelectionMerge %target None\n");
+    body.AppendBody("OpSelectionMerge %f None\n");
   }
 
   if (!spvIsWebGPUEnv(env))
@@ -1650,25 +1649,27 @@
   Block entry("entry");
   Block loop1("loop1", SpvOpBranchConditional);
   Block loop2("loop2", SpvOpBranchConditional);
-  Block loop2_merge("loop2_merge", SpvOpBranchConditional);
+  Block loop2_merge("loop2_merge");
+  Block loop1_cont("loop1_cont", SpvOpBranchConditional);
   Block be_block("be_block");
   Block exit("exit", SpvOpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) {
-    loop1.SetBody("OpLoopMerge %exit %loop2_merge None\n");
+    loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n");
     loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
   }
 
-  std::string str = GetDefaultHeader(GetParam()) +
-                    nameOps("loop1", "loop2", "be_block", "loop2_merge") +
-                    types_consts() +
-                    "%func    = OpFunction %voidt None %funct\n";
+  std::string str =
+      GetDefaultHeader(GetParam()) +
+      nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") +
+      types_consts() + "%func    = OpFunction %voidt None %funct\n";
 
   str += entry >> loop1;
   str += loop1 >> std::vector<Block>({loop2, exit});
   str += loop2 >> std::vector<Block>({loop2, loop2_merge});
-  str += loop2_merge >> std::vector<Block>({be_block, exit});
+  str += loop2_merge >> loop1_cont;
+  str += loop1_cont >> std::vector<Block>({be_block, exit});
   str += be_block >> loop1;
   str += exit;
   str += "OpFunctionEnd";
@@ -1678,7 +1679,7 @@
     ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex("The continue construct with the continue target "
-                             ".\\[%loop2_merge\\] is not post dominated by the "
+                             ".\\[%loop1_cont\\] is not post dominated by the "
                              "back-edge block .\\[%be_block\\]\n"
                              "  %be_block = OpLabel\n"));
   } else {
@@ -2037,13 +2038,10 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
 }
 
-TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) {
-  // This example is valid.  It shows that the validator can't just add
-  // an edge from the loop head to the continue target.  If that edge
-  // is added, then the "if_merge" block is both the continue target
-  // for the loop and also the merge block for the nested selection, but
-  // then it wouldn't be dominated by "if_head", the header block for the
-  // nested selection.
+TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) {
+  // The continue construct cannot be the merge target of a nested selection
+  // because the loop construct must contain "if_merge" because it contains
+  // "if_head".
   bool is_shader = GetParam() == SpvCapabilityShader;
   Block entry("entry");
   Block loop("loop");
@@ -2072,7 +2070,16 @@
   str += "OpFunctionEnd";
 
   CompileSuccessfully(str);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
+  if (is_shader) {
+    EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+    EXPECT_THAT(
+        getDiagnosticString(),
+        HasSubstr("Header block 3[%if_head] is contained in the loop construct "
+                  "headed "
+                  "by 2[%loop], but its merge block 5[%if_merge] is not"));
+  } else {
+    EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
+  }
 }
 
 TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) {
@@ -3681,17 +3688,21 @@
 OpLoopMerge %8 %9 None
 OpBranch %10
 %10 = OpLabel
-OpLoopMerge %9 %11 None
+OpLoopMerge %11 %12 None
+OpBranch %13
+%13 = OpLabel
+OpSelectionMerge %14 None
+OpBranchConditional %3 %14 %15
+%15 = OpLabel
+OpBranch %8
+%14 = OpLabel
 OpBranch %12
 %12 = OpLabel
-OpSelectionMerge %11 None
-OpBranchConditional %3 %11 %13
-%13 = OpLabel
-OpBranch %8
+OpBranchConditional %3 %10 %11
 %11 = OpLabel
-OpBranchConditional %3 %9 %10
+OpBranch %9
 %9 = OpLabel
-OpBranchConditional %3 %8 %7
+OpBranchConditional %3 %7 %8
 %8 = OpLabel
 OpReturn
 OpFunctionEnd
@@ -3700,7 +3711,7 @@
   CompileSuccessfully(text);
   EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("block <ID> 13[%13] exits the loop headed by <ID> "
+              HasSubstr("block <ID> 15[%15] exits the loop headed by <ID> "
                         "10[%10], but not via a structured exit"));
 }
 
@@ -3726,9 +3737,11 @@
 OpSelectionMerge %31 None
 OpBranchConditional %undef %30 %31
 %30 = OpLabel
-OpSelectionMerge %37 None
-OpBranchConditional %undef %36 %37
+OpSelectionMerge %38 None
+OpBranchConditional %undef %36 %38
 %36 = OpLabel
+OpBranch %38
+%38 = OpLabel
 OpBranch %37
 %37 = OpLabel
 OpBranch %10
@@ -4100,6 +4113,80 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
 }
 
+TEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpName %loop "loop"
+OpName %continue "continue"
+OpName %body "body"
+%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
+OpBranch %body
+%body = OpLabel
+OpSelectionMerge %continue None
+OpBranchConditional %undef %exit %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Header block 3[%body] is contained in the loop construct headed by "
+          "1[%loop], but its merge block 2[%continue] is not"));
+}
+
+TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpName %loop "loop"
+OpName %continue "continue"
+OpName %inner "inner"
+%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 %exit %inner
+%inner = OpLabel
+OpLoopMerge %continue %inner None
+OpBranchConditional %undef %inner %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Header block 3[%inner] is contained in the loop construct headed by "
+          "1[%loop], but its merge block 2[%continue] is not"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp
index 327aef1..019d91a 100644
--- a/test/val/val_id_test.cpp
+++ b/test/val/val_id_test.cpp
@@ -749,20 +749,40 @@
 // Signed or unsigned.
 enum Signed { kSigned, kUnsigned };
 
-// Creates an assembly snippet declaring OpTypeArray with the given length.
-std::string MakeArrayLength(const std::string& len, Signed isSigned,
-                            int width) {
+// Creates an assembly module declaring OpTypeArray with the given length.
+std::string MakeArrayLength(const std::string& len, Signed isSigned, int width,
+                            int max_int_width = 64,
+                            bool use_vulkan_memory_model = false) {
   std::ostringstream ss;
   ss << R"(
     OpCapability Shader
-    OpCapability Linkage
-    OpCapability Int16
-    OpCapability Int64
   )";
-  ss << "OpMemoryModel Logical GLSL450\n";
+  if (use_vulkan_memory_model) {
+    ss << " OpCapability VulkanMemoryModel\n";
+  }
+  if (width == 16) {
+    ss << " OpCapability Int16\n";
+  }
+  if (max_int_width > 32) {
+    ss << "\n  OpCapability Int64\n";
+  }
+  if (use_vulkan_memory_model) {
+    ss << " OpExtension \"SPV_KHR_vulkan_memory_model\"\n";
+    ss << "OpMemoryModel Logical Vulkan\n";
+  } else {
+    ss << "OpMemoryModel Logical GLSL450\n";
+  }
+  ss << "OpEntryPoint GLCompute %main \"main\"\n";
+  ss << "OpExecutionMode %main LocalSize 1 1 1\n";
   ss << " %t = OpTypeInt " << width << (isSigned == kSigned ? " 1" : " 0");
   ss << " %l = OpConstant %t " << len;
   ss << " %a = OpTypeArray %t %l";
+  ss << " %void = OpTypeVoid \n"
+        " %voidfn = OpTypeFunction %void \n"
+        " %main = OpFunction %void None %voidfn \n"
+        " %entry = OpLabel\n"
+        " OpReturn\n"
+        " OpFunctionEnd\n";
   return ss.str();
 }
 
@@ -772,7 +792,8 @@
     : public spvtest::TextToBinaryTestBase<::testing::TestWithParam<int>> {
  protected:
   OpTypeArrayLengthTest()
-      : position_(spv_position_t{0, 0, 0}),
+      : env_(SPV_ENV_UNIVERSAL_1_0),
+        position_(spv_position_t{0, 0, 0}),
         diagnostic_(spvDiagnosticCreate(&position_, "")) {}
 
   ~OpTypeArrayLengthTest() { spvDiagnosticDestroy(diagnostic_); }
@@ -783,7 +804,7 @@
     spvDiagnosticDestroy(diagnostic_);
     diagnostic_ = nullptr;
     const auto status =
-        spvValidate(ScopedContext().context, &cbinary, &diagnostic_);
+        spvValidate(ScopedContext(env_).context, &cbinary, &diagnostic_);
     if (status != SPV_SUCCESS) {
       spvDiagnosticPrint(diagnostic_);
       EXPECT_THAT(std::string(diagnostic_->error),
@@ -792,12 +813,15 @@
     return status;
   }
 
+ protected:
+  spv_target_env env_;
+
  private:
   spv_position_t position_;  // For creating diagnostic_.
   spv_diagnostic diagnostic_;
 };
 
-TEST_P(OpTypeArrayLengthTest, LengthPositive) {
+TEST_P(OpTypeArrayLengthTest, LengthPositiveSmall) {
   const int width = GetParam();
   EXPECT_EQ(SPV_SUCCESS,
             Val(CompileSuccessfully(MakeArrayLength("1", kSigned, width))));
@@ -814,20 +838,19 @@
   const std::string fpad(width / 4 - 1, 'F');
   EXPECT_EQ(
       SPV_SUCCESS,
-      Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width))));
-  EXPECT_EQ(SPV_SUCCESS, Val(CompileSuccessfully(
-                             MakeArrayLength("0xF" + fpad, kUnsigned, width))));
+      Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width))))
+      << MakeArrayLength("0x7" + fpad, kSigned, width);
 }
 
 TEST_P(OpTypeArrayLengthTest, LengthZero) {
   const int width = GetParam();
   EXPECT_EQ(SPV_ERROR_INVALID_ID,
             Val(CompileSuccessfully(MakeArrayLength("0", kSigned, width)),
-                "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                 "least 1."));
   EXPECT_EQ(SPV_ERROR_INVALID_ID,
             Val(CompileSuccessfully(MakeArrayLength("0", kUnsigned, width)),
-                "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                 "least 1."));
 }
 
@@ -835,23 +858,88 @@
   const int width = GetParam();
   EXPECT_EQ(SPV_ERROR_INVALID_ID,
             Val(CompileSuccessfully(MakeArrayLength("-1", kSigned, width)),
-                "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                 "least 1."));
   EXPECT_EQ(SPV_ERROR_INVALID_ID,
             Val(CompileSuccessfully(MakeArrayLength("-2", kSigned, width)),
-                "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                 "least 1."));
   EXPECT_EQ(SPV_ERROR_INVALID_ID,
             Val(CompileSuccessfully(MakeArrayLength("-123", kSigned, width)),
-                "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                 "least 1."));
   const std::string neg_max = "0x8" + std::string(width / 4 - 1, '0');
   EXPECT_EQ(SPV_ERROR_INVALID_ID,
             Val(CompileSuccessfully(MakeArrayLength(neg_max, kSigned, width)),
-                "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at "
+                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                 "least 1."));
 }
 
+// Returns the string form of an integer of the form 0x80....0 of the
+// given bit width.
+std::string big_num_ending_0(int bit_width) {
+  return "0x8" + std::string(bit_width / 4 - 1, '0');
+}
+
+// Returns the string form of an integer of the form 0x80..001 of the
+// given bit width.
+std::string big_num_ending_1(int bit_width) {
+  return "0x8" + std::string(bit_width / 4 - 2, '0') + "1";
+}
+
+TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InVulkan) {
+  env_ = SPV_ENV_VULKAN_1_0;
+  const int width = GetParam();
+  for (int max_int_width : {32, 64}) {
+    if (width > max_int_width) {
+      // Not valid to even make the OpConstant in this case.
+      continue;
+    }
+    const auto module = CompileSuccessfully(MakeArrayLength(
+        big_num_ending_0(width), kUnsigned, width, max_int_width));
+    EXPECT_EQ(SPV_SUCCESS, Val(module));
+  }
+}
+
+TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) {
+  env_ = SPV_ENV_VULKAN_1_0;
+  const int width = GetParam();
+  for (int max_int_width : {32, 64}) {
+    if (width > max_int_width) {
+      // Not valid to even make the OpConstant in this case.
+      continue;
+    }
+    const auto module = CompileSuccessfully(MakeArrayLength(
+        big_num_ending_1(width), kUnsigned, width, max_int_width));
+    EXPECT_EQ(SPV_SUCCESS, Val(module));
+  }
+}
+
+TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InWebGPU) {
+  env_ = SPV_ENV_WEBGPU_0;
+  const int width = GetParam();
+  // WebGPU only has 32 bit integers.
+  if (width != 32) return;
+  const int max_int_width = 32;
+  const auto module = CompileSuccessfully(MakeArrayLength(
+      big_num_ending_0(width), kUnsigned, width, max_int_width, true));
+  EXPECT_EQ(SPV_SUCCESS, Val(module));
+}
+
+TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InWebGPU) {
+  env_ = SPV_ENV_WEBGPU_0;
+  const int width = GetParam();
+  // WebGPU only has 32 bit integers.
+  if (width != 32) return;
+  const int max_int_width = 32;
+  const auto module = CompileSuccessfully(MakeArrayLength(
+      big_num_ending_1(width), kUnsigned, width, max_int_width, true));
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            Val(module,
+                "OpTypeArray Length <id> '3\\[%.*\\]' size exceeds max value "
+                "2147483648 permitted by WebGPU: got 2147483649"));
+}
+
 // The only valid widths for integers are 8, 16, 32, and 64.
 // Since the Int8 capability requires the Kernel capability, and the Kernel
 // capability prohibits usage of signed integers, we can skip 8-bit integers
diff --git a/test/val/val_non_semantic_test.cpp b/test/val/val_non_semantic_test.cpp
new file mode 100644
index 0000000..b80bb1a
--- /dev/null
+++ b/test/val/val_non_semantic_test.cpp
@@ -0,0 +1,195 @@
+// Copyright (c) 2019 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Validation tests for non-semantic instructions
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_code_generator.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+struct TestResult {
+  TestResult(spv_result_t in_validation_result = SPV_SUCCESS,
+             const char* in_error_str = nullptr,
+             const char* in_error_str2 = nullptr)
+      : validation_result(in_validation_result),
+        error_str(in_error_str),
+        error_str2(in_error_str2) {}
+  spv_result_t validation_result;
+  const char* error_str;
+  const char* error_str2;
+};
+
+using ::testing::Combine;
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateNonSemanticGenerated = spvtest::ValidateBase<
+    std::tuple<bool, bool, const char*, const char*, TestResult>>;
+using ValidateNonSemanticString = spvtest::ValidateBase<bool>;
+
+CodeGenerator GetNonSemanticCodeGenerator(const bool declare_ext,
+                                          const bool declare_extinst,
+                                          const char* const global_extinsts,
+                                          const char* const function_extinsts) {
+  CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
+
+  if (declare_ext) {
+    generator.extensions_ += "OpExtension \"SPV_KHR_non_semantic_info\"\n";
+  }
+  if (declare_extinst) {
+    generator.extensions_ +=
+        "%extinst = OpExtInstImport \"NonSemantic.Testing.Set\"\n";
+  }
+
+  generator.after_types_ = global_extinsts;
+
+  generator.before_types_ = "%decorate_group = OpDecorationGroup";
+
+  EntryPoint entry_point;
+  entry_point.name = "main";
+  entry_point.execution_model = "Vertex";
+
+  entry_point.body = R"(
+)";
+  entry_point.body += function_extinsts;
+  generator.entry_points_.push_back(std::move(entry_point));
+
+  return generator;
+}
+
+TEST_P(ValidateNonSemanticGenerated, InTest) {
+  const bool declare_ext = std::get<0>(GetParam());
+  const bool declare_extinst = std::get<1>(GetParam());
+  const char* const global_extinsts = std::get<2>(GetParam());
+  const char* const function_extinsts = std::get<3>(GetParam());
+  const TestResult& test_result = std::get<4>(GetParam());
+
+  CodeGenerator generator = GetNonSemanticCodeGenerator(
+      declare_ext, declare_extinst, global_extinsts, function_extinsts);
+
+  CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(test_result.validation_result,
+            ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  if (test_result.error_str) {
+    EXPECT_THAT(getDiagnosticString(),
+                testing::ContainsRegex(test_result.error_str));
+  }
+  if (test_result.error_str2) {
+    EXPECT_THAT(getDiagnosticString(),
+                testing::ContainsRegex(test_result.error_str2));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated,
+                         Combine(Values(true), Values(false), Values(""),
+                                 Values(""), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    MissingOpExtension, ValidateNonSemanticGenerated,
+    Combine(Values(false), Values(true), Values(""), Values(""),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "NonSemantic extended instruction sets cannot be declared "
+                "without SPV_KHR_non_semantic_info."))));
+
+INSTANTIATE_TEST_SUITE_P(NoExtInst, ValidateNonSemanticGenerated,
+                         Combine(Values(true), Values(true), Values(""),
+                                 Values(""), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    SimpleGlobalExtInst, ValidateNonSemanticGenerated,
+    Combine(Values(true), Values(true),
+            Values("%result = OpExtInst %void %extinst 123 %i32"), Values(""),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    ComplexGlobalExtInst, ValidateNonSemanticGenerated,
+    Combine(Values(true), Values(true),
+            Values("%result = OpExtInst %void %extinst  123 %i32 %u32_2 "
+                   "%f32vec4_1234 %u32_0"),
+            Values(""), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    SimpleFunctionLevelExtInst, ValidateNonSemanticGenerated,
+    Combine(Values(true), Values(true), Values(""),
+            Values("%result = OpExtInst %void %extinst 123 %i32"),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    FunctionTypeReference, ValidateNonSemanticGenerated,
+    Combine(Values(true), Values(true),
+            Values("%result = OpExtInst %void %extinst 123 %func"), Values(""),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    EntryPointReference, ValidateNonSemanticGenerated,
+    Combine(Values(true), Values(true), Values(""),
+            Values("%result = OpExtInst %void %extinst 123 %main"),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    DecorationGroupReference, ValidateNonSemanticGenerated,
+    Combine(Values(true), Values(true), Values(""),
+            Values("%result = OpExtInst %void %extinst 123 %decorate_group"),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    UnknownIDReference, ValidateNonSemanticGenerated,
+    Combine(Values(true), Values(true),
+            Values("%result = OpExtInst %void %extinst 123 %undefined_id"),
+            Values(""),
+            Values(TestResult(SPV_ERROR_INVALID_ID,
+                              "ID .* has not been defined"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    NonSemanticUseInSemantic, ValidateNonSemanticGenerated,
+    Combine(Values(true), Values(true),
+            Values("%result = OpExtInst %f32 %extinst 123 %i32\n"
+                   "%invalid = OpConstantComposite %f32vec2 %f32_0 %result"),
+            Values(""),
+            Values(TestResult(SPV_ERROR_INVALID_ID,
+                              "in semantic instruction cannot be a "
+                              "non-semantic instruction"))));
+
+TEST_F(ValidateNonSemanticString, InvalidSectionOpExtInst) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+%test = OpExtInst %void %extinst 4 %void
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+
+  // there's no specific error for using an OpExtInst too early, it requires a
+  // type so by definition any use of a type in it will be an undefined ID
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("ID 2[%2] has not been defined"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_webgpu_test.cpp b/test/val/val_webgpu_test.cpp
index 8f62555..e81fc7c 100644
--- a/test/val/val_webgpu_test.cpp
+++ b/test/val/val_webgpu_test.cpp
@@ -54,117 +54,170 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed"));
 }
 
-TEST_F(ValidateWebGPU, OpNameIsDisallowed) {
+TEST_F(ValidateWebGPU, OpNameIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpName %1 "foo"
-%1 = OpTypeFloat 32
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpName %1 "foo"
+       %1 = OpTypeFloat 32
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpName %foo \"foo\"\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-TEST_F(ValidateWebGPU, OpMemberNameIsDisallowed) {
+TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpMemberName %2 0 "foo"
-%1 = OpTypeFloat 32
-%2 = OpTypeStruct %1
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpMemberName %2 0 "foo"
+       %1 = OpTypeFloat 32
+       %2 = OpTypeStruct %1
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
+
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpMemberName %_struct_1 0 "
-                        "\"foo\"\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-TEST_F(ValidateWebGPU, OpSourceIsDisallowed) {
+TEST_F(ValidateWebGPU, OpSourceIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpSource GLSL 450
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpSource GLSL 450
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpSource GLSL 450\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-// OpSourceContinued does not have a test case, because it requires being
-// preceded by OpSource, which will cause a validation error.
-
-TEST_F(ValidateWebGPU, OpSourceExtensionIsDisallowed) {
+TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpSourceExtension "bar"
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpSource GLSL 450
+            OpSourceContinued "I am a happy shader! Yay! ;"
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpSourceExtension "
-                        "\"bar\"\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-TEST_F(ValidateWebGPU, OpStringIsDisallowed) {
+TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-%1 = OpString "foo"
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpSourceExtension "bar"
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  %1 = OpString \"foo\"\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-// OpLine does not have a test case, because it requires being preceded by
-// OpString, which will cause a validation error.
-
-TEST_F(ValidateWebGPU, OpNoLineDisallowed) {
+TEST_F(ValidateWebGPU, OpStringIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpNoLine
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+       %1 = OpString "foo"
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
 
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpNoLine\n"));
+TEST_F(ValidateWebGPU, OpLineIsAllowed) {
+  std::string spirv = R"(
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+       %1 = OpString "minimal.vert"
+            OpLine %1 1 1
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
+
+TEST_F(ValidateWebGPU, OpNoLineIsAllowed) {
+  std::string spirv = R"(
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpNoLine
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
 TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
@@ -183,7 +236,6 @@
 )";
 
   CompileSuccessfully(spirv);
-
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp
index 5d582f5..cb98914 100644
--- a/tools/fuzz/fuzz.cpp
+++ b/tools/fuzz/fuzz.cpp
@@ -17,6 +17,7 @@
 #include <cstring>
 #include <fstream>
 #include <functional>
+#include <random>
 #include <sstream>
 #include <string>
 
@@ -106,6 +107,10 @@
                facts to make the guard non-obviously false.  This option is a
                helper for massaging crash-inducing tests into a runnable
                format; it does not perform any fuzzing.
+  --fuzzer-pass-validation
+               Run the validator after applying each fuzzer pass during
+               fuzzing.  Aborts fuzzing early if an invalid binary is created.
+               Useful for debugging spirv-fuzz.
   --replay
                File from which to read a sequence of transformations to replay
                (instead of fuzzing)
@@ -179,6 +184,9 @@
       } else if (0 == strncmp(cur_arg, "--force-render-red",
                               sizeof("--force-render-red") - 1)) {
         force_render_red = true;
+      } else if (0 == strncmp(cur_arg, "--fuzzer-pass-validation",
+                              sizeof("--fuzzer-pass-validation") - 1)) {
+        fuzzer_options->enable_fuzzer_pass_validation();
       } else if (0 == strncmp(cur_arg, "--replay=", sizeof("--replay=") - 1)) {
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
         *replay_transformations_file = std::string(split_flag.second);
@@ -397,16 +405,21 @@
 }
 
 bool Fuzz(const spv_target_env& target_env,
-          const spvtools::FuzzerOptions& fuzzer_options,
+          spv_const_fuzzer_options fuzzer_options,
           const std::vector<uint32_t>& binary_in,
           const spvtools::fuzz::protobufs::FactSequence& initial_facts,
           std::vector<uint32_t>* binary_out,
           spvtools::fuzz::protobufs::TransformationSequence*
               transformations_applied) {
-  spvtools::fuzz::Fuzzer fuzzer(target_env);
+  spvtools::fuzz::Fuzzer fuzzer(
+      target_env,
+      fuzzer_options->has_random_seed
+          ? fuzzer_options->random_seed
+          : static_cast<uint32_t>(std::random_device()()),
+      fuzzer_options->fuzzer_pass_validation_enabled);
   fuzzer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
-  auto fuzz_result_status = fuzzer.Run(binary_in, initial_facts, fuzzer_options,
-                                       binary_out, transformations_applied);
+  auto fuzz_result_status =
+      fuzzer.Run(binary_in, initial_facts, binary_out, transformations_applied);
   if (fuzz_result_status !=
       spvtools::fuzz::Fuzzer::FuzzerResultStatus::kComplete) {
     spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error running fuzzer");
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 4364e3e..0ff937a 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -467,7 +467,8 @@
   printf(R"(
   --strip-reflect
                Remove all reflection information.  For now, this covers
-               reflection information defined by SPV_GOOGLE_hlsl_functionality1.)");
+               reflection information defined by SPV_GOOGLE_hlsl_functionality1
+               and SPV_KHR_non_semantic_info)");
   printf(R"(
   --target-env=<env>
                Set the target environment. Without this flag the target
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index cfeef80..969371d 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -34,7 +34,7 @@
            'Samsung Inc']
 CURRENT_YEAR='2019'
 
-YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2018|2019)'
+YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019)'
 COPYRIGHT_RE = re.compile(
         'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
 
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py
index ed24bd0..06fcf94 100755
--- a/utils/generate_grammar_tables.py
+++ b/utils/generate_grammar_tables.py
@@ -30,6 +30,7 @@
 SPV_AMD_gpu_shader_half_float
 SPV_AMD_gpu_shader_int16
 SPV_AMD_shader_trinary_minmax
+SPV_KHR_non_semantic_info
 """
 
 
@@ -359,14 +360,22 @@
     return '{}\n\n{}\n\n{}'.format(caps_arrays, exts_arrays, '\n'.join(insts))
 
 
-def generate_extended_instruction_table(inst_table, set_name):
+def generate_extended_instruction_table(json_grammar, set_name, operand_kind_prefix=""):
     """Returns the info table containing all SPIR-V extended instructions,
     sorted by opcode, and prefixed by capability arrays.
 
     Arguments:
       - inst_table: a list containing all SPIR-V instructions.
       - set_name: the name of the extended instruction set.
+      - operand_kind_prefix: the prefix, if any, to add to the front
+        of operand kind names.
     """
+    if operand_kind_prefix:
+        prefix_operand_kind_names(operand_kind_prefix, json_grammar)
+
+    inst_table = json_grammar["instructions"]
+    set_name = set_name.replace(".", "_")
+
     inst_table = sorted(inst_table, key=lambda k: k['opcode'])
     caps = [inst.get('capabilities', []) for inst in inst_table]
     caps_arrays = generate_capability_arrays(caps)
@@ -451,6 +460,7 @@
 
 def generate_enum_operand_kind(enum, synthetic_exts_list):
     """Returns the C definition for the given operand kind.
+    It's a static const named array of spv_operand_desc_t.
 
     Also appends to |synthetic_exts_list| a list of extension lists
     used.
@@ -680,6 +690,26 @@
     return operand_kinds
 
 
+def prefix_operand_kind_names(prefix, json_dict):
+    """Modifies json_dict, by prefixing all the operand kind names
+    with the given prefix.  Also modifies their uses in the instructions
+    to match.
+    """
+
+    old_to_new = {}
+    for operand_kind in json_dict["operand_kinds"]:
+        old_name = operand_kind["kind"]
+        new_name = prefix + old_name
+        operand_kind["kind"] = new_name
+        old_to_new[old_name] = new_name
+
+    for instruction in json_dict["instructions"]:
+        for operand in instruction.get("operands", []):
+            replacement = old_to_new.get(operand["kind"])
+            if replacement is not None:
+                operand["kind"] = replacement
+
+
 def main():
     import argparse
     parser = argparse.ArgumentParser(description='Generate SPIR-V info tables')
@@ -692,6 +722,10 @@
                         type=str, required=False, default=None,
                         help='input JSON grammar file for DebugInfo extended '
                         'instruction set')
+    parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='<path>',
+                        type=str, required=False, default=None,
+                        help='input JSON grammar file for OpenCL.DebugInfo.100 '
+                        'extended instruction set')
     parser.add_argument('--extinst-glsl-grammar', metavar='<path>',
                         type=str, required=False, default=None,
                         help='input JSON grammar file for GLSL extended '
@@ -726,6 +760,9 @@
     parser.add_argument('--vendor-insts-output', metavar='<path>',
                         type=str, required=False, default=None,
                         help='output file for vendor extended instruction set')
+    parser.add_argument('--vendor-operand-kind-prefix', metavar='<string>',
+                        type=str, required=False, default=None,
+                        help='prefix for operand kinds (to disambiguate operand type enums)')
     args = parser.parse_args()
 
     if (args.core_insts_output is None) != \
@@ -733,9 +770,12 @@
         print('error: --core-insts-output and --operand-kinds-output '
               'should be specified together.')
         exit(1)
-    if args.operand_kinds_output and not (args.spirv_core_grammar and args.extinst_debuginfo_grammar):
+    if args.operand_kinds_output and not (args.spirv_core_grammar and
+         args.extinst_debuginfo_grammar and
+         args.extinst_cldebuginfo100_grammar):
         print('error: --operand-kinds-output requires --spirv-core-grammar '
-              'and --exinst-debuginfo-grammar')
+              'and --exinst-debuginfo-grammar '
+              'and --exinst-cldebuginfo100-grammar')
         exit(1)
     if (args.glsl_insts_output is None) != \
             (args.extinst_glsl_grammar is None):
@@ -766,14 +806,19 @@
             core_grammar = json.loads(json_file.read())
             with open(args.extinst_debuginfo_grammar) as debuginfo_json_file:
                 debuginfo_grammar = json.loads(debuginfo_json_file.read())
-                instructions = []
-                instructions.extend(core_grammar['instructions'])
-                instructions.extend(debuginfo_grammar['instructions'])
-                operand_kinds = []
-                operand_kinds.extend(core_grammar['operand_kinds'])
-                operand_kinds.extend(debuginfo_grammar['operand_kinds'])
-                extensions = get_extension_list(instructions, operand_kinds)
-                operand_kinds = precondition_operand_kinds(operand_kinds)
+                with open(args.extinst_cldebuginfo100_grammar) as cldebuginfo100_json_file:
+                    cldebuginfo100_grammar = json.loads(cldebuginfo100_json_file.read())
+                    prefix_operand_kind_names("CLDEBUG100_", cldebuginfo100_grammar)
+                    instructions = []
+                    instructions.extend(core_grammar['instructions'])
+                    instructions.extend(debuginfo_grammar['instructions'])
+                    instructions.extend(cldebuginfo100_grammar['instructions'])
+                    operand_kinds = []
+                    operand_kinds.extend(core_grammar['operand_kinds'])
+                    operand_kinds.extend(debuginfo_grammar['operand_kinds'])
+                    operand_kinds.extend(cldebuginfo100_grammar['operand_kinds'])
+                    extensions = get_extension_list(instructions, operand_kinds)
+                    operand_kinds = precondition_operand_kinds(operand_kinds)
         if args.core_insts_output is not None:
             make_path_to_file(args.core_insts_output)
             make_path_to_file(args.operand_kinds_output)
@@ -798,7 +843,7 @@
             make_path_to_file(args.glsl_insts_output)
             with open(args.glsl_insts_output, 'w') as f:
                 f.write(generate_extended_instruction_table(
-                    grammar['instructions'], 'glsl'))
+                    grammar, 'glsl'))
 
     if args.extinst_opencl_grammar is not None:
         with open(args.extinst_opencl_grammar) as json_file:
@@ -806,7 +851,7 @@
             make_path_to_file(args.opencl_insts_output)
             with open(args.opencl_insts_output, 'w') as f:
                 f.write(generate_extended_instruction_table(
-                    grammar['instructions'], 'opencl'))
+                    grammar, 'opencl'))
 
     if args.extinst_vendor_grammar is not None:
         with open(args.extinst_vendor_grammar) as json_file:
@@ -817,7 +862,7 @@
             name = name[start:-len('.grammar.json')].replace('-', '_')
             with open(args.vendor_insts_output, 'w') as f:
                 f.write(generate_extended_instruction_table(
-                    grammar['instructions'], name))
+                    grammar, name, args.vendor_operand_kind_prefix))
 
 
 if __name__ == '__main__':
diff --git a/utils/vscode/README.md b/utils/vscode/README.md
index afbb246..bc02211 100644
--- a/utils/vscode/README.md
+++ b/utils/vscode/README.md
@@ -1,7 +1,17 @@
 # Visual Studio Code extension for SPIR-V disassembly files
 
-This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V disassembly files (.spirv)
+This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V assembly files (`.spvasm`)
+
+## Dependencies
+
+In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies:
+* [`npm`](https://www.npmjs.com/)
+* [`golang`](https://golang.org/)
 
 ## Installing (macOS / Linux)
 
-Simply run `install.sh`
+Run `install.sh`
+
+## Installing (Windows)
+
+Run `install.bat`
diff --git a/utils/vscode/install.bat b/utils/vscode/install.bat
new file mode 100644
index 0000000..21a52ec
--- /dev/null
+++ b/utils/vscode/install.bat
@@ -0,0 +1,30 @@
+@REM Copyright (c) 2019 Google Inc.
+@REM
+@REM Licensed under the Apache License, Version 2.0 (the "License");
+@REM you may not use this file except in compliance with the License.
+@REM You may obtain a copy of the License at
+@REM
+@REM     http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing, software
+@REM distributed under the License is distributed on an "AS IS" BASIS,
+@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@REM See the License for the specific language governing permissions and
+@REM limitations under the License.
+
+@set EXT_PATH=%userprofile%\.vscode\extensions\google.spirvls-0.0.1
+@set ROOT_PATH=%~dp0
+
+go run %ROOT_PATH%\src\tools\gen-grammar.go --cache %ROOT_PATH%\cache --template %ROOT_PATH%\spirv.json.tmpl --out %ROOT_PATH%\spirv.json
+go run %ROOT_PATH%\src\tools\gen-grammar.go --cache %ROOT_PATH%\cache --template %ROOT_PATH%\src\schema\schema.go.tmpl --out %ROOT_PATH%\src\schema\schema.go
+
+if not exist %EXT_PATH% mkdir -p %EXT_PATH%
+copy %ROOT_PATH%\extension.js %EXT_PATH%
+copy %ROOT_PATH%\package.json %EXT_PATH%
+copy %ROOT_PATH%\spirv.json %EXT_PATH%
+
+go build -o %EXT_PATH%\langsvr %ROOT_PATH%\src\langsvr.go
+
+@pushd %EXT_PATH%
+call npm install
+@popd
diff --git a/utils/vscode/src/tools/gen-grammar.go b/utils/vscode/src/tools/gen-grammar.go
index f9980b9..42cbbe9 100644
--- a/utils/vscode/src/tools/gen-grammar.go
+++ b/utils/vscode/src/tools/gen-grammar.go
@@ -77,9 +77,12 @@
 					}
 					return rel
 				}
+				escape := func(str string) string {
+					return strings.ReplaceAll(str, `\`, `/`)
+				}
 				args := []string{
-					"--template=" + relPath(*templatePath),
-					"--out=" + relPath(*outputPath),
+					"--template=" + escape(relPath(*templatePath)),
+					"--out=" + escape(relPath(*outputPath)),
 				}
 				return "gen-grammar.go " + strings.Join(args, " ")
 			},