Update SPIR-V Tools to fd773eb50

Includes fix #3253, which fixed the following SwANGLE test:
KHR-GLES31.core.compute_shader.resources-max

Changes:
    fd773eb50 Start SPIRV-Tools v2020.3
    fd8e13051 Finalize SPIRV-Tools v2020.2
    3ef8fe9a5 Update CHANGES
    022da4d0e Fix identification of Vulkan images and buffers (#3253)
    1346dd5de Disallow phis of images, samplers and sampled images (#3246)
    1c8bda372 Add data structure for DebugScope, DebugDeclare in spirv-opt (#3183)
    e6f372c5c Whitelist SPV_KHR_ray_tracing (#3241)
    7f341ffee Make file formatting comply with POSIX standards (#3242)
    60104cd97 Add opt::Operand::AsCString and AsString (#3240)
    da52d0875 Add RayQueryProvisionalKHR to opt types (#3239)
    18d3896a1 Whitelist SPV_EXT_demote_to_helper_invocation for opt passes (#3236)
    5a97e3a39 Add support for KHR_ray_{query,tracing} extensions (#3235)
    25ede1ced Roll external/spirv-headers/ 30ef660ce..a17e17e36 (1 commit) (#3230)
    7a8f79762 Update dependencies (#3228)
    1fe9bcc10 Instrument: Debug Printf support (#3215)
    6428ad05e spirv-fuzz: Support OpPhi when adding dead break and continue (#3225)
    1af1df3b2 spirv-fuzz: Fix vector width issue in 'add equation instructions' pass (#3223)
    dd3d91691 Allow sampledimage types as operand of OpCopyObject (#3222)
    7c3de218f spirv-fuzz: Remove duplicated functionality (#3220)
    659470446 spirv-fuzz: Allow OpPhi operand to be replaced with a composite synonym (#3221)
    4c027048d spirv-fuzz: Add toggle access chain instruction transformation (#3211)
    533af4981 spirv-fuzz: Add fuzzer pass to permute function parameters (#3212)
    da4cd2148 spirv-fuzz: Use better function name (#3207)
    66a682b6a spirv-fuzz: Add swap commutable operands transformation (#3205)
    044ecc0b2 spirv-fuzz: Fuzzer pass to add equation instructions (#3202)
    a6d3a2dd4 Refactor FuzzerPass::ApplyTransformation code duplication. (#3206)
    e1688b60c Avoid use of Python distutils.dir_util (#3203)
    661e79eec Adding WebGPU specific Workgroup scope rule (#3204)
    70f888131 Add validation rules for OpenCL.DebugInfo.100 extension (#3133)
    fb6e3e48d Combine extinst-name and extinst-output-base into one arg. (#3200)

Commands:
    ./third_party/update-spirvtools.sh

Bug: b/123642959
Bug: b/148460089
Change-Id: I8fa5d3609de1c6ae786c84b93cba7ac015b4f56e
diff --git a/src/Pipeline/SpirvShaderInstructions.inl b/src/Pipeline/SpirvShaderInstructions.inl
index 8ec4a44..4e60dd6 100644
--- a/src/Pipeline/SpirvShaderInstructions.inl
+++ b/src/Pipeline/SpirvShaderInstructions.inl
@@ -374,6 +374,13 @@
 DECORATE_OP(T, OpSubgroupAnyKHR)
 DECORATE_OP(T, OpSubgroupAllEqualKHR)
 DECORATE_OP(T, OpSubgroupReadInvocationKHR)
+DECORATE_OP(T, OpTypeRayQueryProvisionalKHR)
+DECORATE_OP(T, OpRayQueryInitializeKHR)
+DECORATE_OP(T, OpRayQueryTerminateKHR)
+DECORATE_OP(T, OpRayQueryGenerateIntersectionKHR)
+DECORATE_OP(T, OpRayQueryConfirmIntersectionKHR)
+DECORATE_OP(T, OpRayQueryProceedKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionTypeKHR)
 DECORATE_OP(T, OpGroupIAddNonUniformAMD)
 DECORATE_OP(T, OpGroupFAddNonUniformAMD)
 DECORATE_OP(T, OpGroupFMinNonUniformAMD)
@@ -547,3 +554,20 @@
 DECORATE_OP(T, OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL)
 DECORATE_OP(T, OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL)
 DECORATE_OP(T, OpSubgroupAvcSicGetInterRawSadsINTEL)
+DECORATE_OP(T, OpRayQueryGetRayTMinKHR)
+DECORATE_OP(T, OpRayQueryGetRayFlagsKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionTKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionInstanceCustomIndexKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionInstanceIdKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionGeometryIndexKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionPrimitiveIndexKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionBarycentricsKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionFrontFaceKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionCandidateAABBOpaqueKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionObjectRayDirectionKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionObjectRayOriginKHR)
+DECORATE_OP(T, OpRayQueryGetWorldRayDirectionKHR)
+DECORATE_OP(T, OpRayQueryGetWorldRayOriginKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionObjectToWorldKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionWorldToObjectKHR)
diff --git a/third_party/SPIRV-Tools/.gitignore b/third_party/SPIRV-Tools/.gitignore
index 196c63c..b2af56e 100644
--- a/third_party/SPIRV-Tools/.gitignore
+++ b/third_party/SPIRV-Tools/.gitignore
@@ -29,3 +29,6 @@
 # C-Lion
 /.idea/
 /cmake-build-*/
+
+# VSCode
+/.vscode/*
diff --git a/third_party/SPIRV-Tools/BUILD.gn b/third_party/SPIRV-Tools/BUILD.gn
index b7cde34..d3107fd 100644
--- a/third_party/SPIRV-Tools/BUILD.gn
+++ b/third_party/SPIRV-Tools/BUILD.gn
@@ -186,21 +186,19 @@
     script = "utils/generate_language_headers.py"
 
     name = invoker.name
-    extinst_output_base = "${target_gen_dir}/${name}"
+    extinst_output_path = "${target_gen_dir}/${name}.h"
 
     args = [
-      "--extinst-name",
-      "${name}",
       "--extinst-grammar",
       rebase_path(invoker.grammar_file, root_build_dir),
-      "--extinst-output-base",
-      rebase_path(extinst_output_base, root_build_dir),
+      "--extinst-output-path",
+      rebase_path(extinst_output_path, root_build_dir),
     ]
     inputs = [
       invoker.grammar_file,
     ]
     outputs = [
-      "${extinst_output_base}.h",
+      "${extinst_output_path}",
     ]
   }
 }
@@ -592,6 +590,8 @@
     "source/opt/inst_bindless_check_pass.h",
     "source/opt/inst_buff_addr_check_pass.cpp",
     "source/opt/inst_buff_addr_check_pass.h",
+    "source/opt/inst_debug_printf_pass.cpp",
+    "source/opt/inst_debug_printf_pass.h",
     "source/opt/instruction.cpp",
     "source/opt/instruction.h",
     "source/opt/instruction_list.cpp",
diff --git a/third_party/SPIRV-Tools/CHANGES b/third_party/SPIRV-Tools/CHANGES
index 48c93a4..fe6641e 100644
--- a/third_party/SPIRV-Tools/CHANGES
+++ b/third_party/SPIRV-Tools/CHANGES
@@ -1,7 +1,34 @@
 Revision history for SPIRV-Tools
 
-v2020.2-dev 2020-02-03
- - Start v2020.2-dev
+v2020.3-dev 2020-03-26
+ - Start v2020.3-dev
+
+v2020.2 2020-03-26
+ - General:
+   - Support extended instructions in the vscode language server
+   - Make spvOpcodeString part of the public API (#3174)
+   - Added guide to writing a spirv-fuzz fuzzer pass (#3190)
+   - Add support for KHR_ray_{query,tracing} extensions (#3235)
+ - Optimizer
+   - Debug Printf support (#3215)
+   - Add data structure for DebugScope, DebugDeclare in spirv-opt (#3183)
+   - Fix identification of Vulkan images and buffers (#3253)
+ - Validator
+   - Add support for SPV_AMD_shader_image_load_store_lod (#3186)
+   - Add validation rules for OpenCL.DebugInfo.100 extension (#3133)
+   - Adding WebGPU specific Workgroup scope rule (#3204)
+   - Disallow phis of images, samplers and sampled images (#3246)
+ - Reduce
+ - Fuzz
+   - Fuzzer passes to add local and global variables (#3175)
+   - Add fuzzer passes to add loads/stores (#3176)
+   - Fuzzer pass to add function calls (#3178)
+   - Fuzzer pass that adds access chains (#3182)
+   - Fuzzer pass to add equation instructions (#3202)
+   - Add swap commutable operands transformation (#3205)
+   - Add fuzzer pass to permute function parameters (#3212)
+   - Allow OpPhi operand to be replaced with a composite synonym (#3221)
+ - Linker
 
 v2020.1 2020-02-03
  - General:
diff --git a/third_party/SPIRV-Tools/DEPS b/third_party/SPIRV-Tools/DEPS
index 23c4111..c3e78a2 100644
--- a/third_party/SPIRV-Tools/DEPS
+++ b/third_party/SPIRV-Tools/DEPS
@@ -6,7 +6,7 @@
   'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d',
   'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
   're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb',
-  'spirv_headers_revision': 'dc77030acc9c6fe7ca21fff54c5a9d7b532d7da6',
+  'spirv_headers_revision': 'f8bf11a0253a32375c32cad92c841237b96696c0',
 }
 
 deps = {
diff --git a/third_party/SPIRV-Tools/build_defs.bzl b/third_party/SPIRV-Tools/build_defs.bzl
index 5d913a1..15b70c7 100644
--- a/third_party/SPIRV-Tools/build_defs.bzl
+++ b/third_party/SPIRV-Tools/build_defs.bzl
@@ -167,16 +167,16 @@
 def generate_extinst_lang_headers(name, grammar = None):
     if not grammar:
         fail("Must specify grammar", "grammar")
-    fmtargs = [name]
+    outs = [name + ".h"]
+    fmtargs = outs
     native.genrule(
         name = "gen_extinst_lang_headers_" + name,
         srcs = [grammar],
-        outs = [name + ".h"],
+        outs = outs,
         cmd = (
             "$(location :generate_language_headers) " +
-            "--extinst-name={0} " +
             "--extinst-grammar=$< " +
-            "--extinst-output-base=$(@D)/{0}"
+            "--extinst-output-path=$(location {0})"
         ).format(*fmtargs),
         tools = [":generate_language_headers"],
         visibility = ["//visibility:private"],
diff --git a/third_party/SPIRV-Tools/examples/cpp-interface/CMakeLists.txt b/third_party/SPIRV-Tools/examples/cpp-interface/CMakeLists.txt
index d050b07..7887ee7 100644
--- a/third_party/SPIRV-Tools/examples/cpp-interface/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/examples/cpp-interface/CMakeLists.txt
@@ -16,4 +16,4 @@
   TARGET spirv-tools-cpp-example
   SRCS main.cpp
   LIBS SPIRV-Tools-opt
-)
\ No newline at end of file
+)
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
index 2dcb333..d3180e4 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
@@ -208,6 +208,9 @@
 // The binding for the input buffer read by InstBuffAddrCheckPass.
 static const int kDebugInputBindingBuffAddr = 2;
 
+// This is the output buffer written by InstDebugPrintfPass.
+static const int kDebugOutputPrintfStream = 3;
+
 // Bindless Validation Input Buffer Format
 //
 // An input buffer for bindless validation consists of a single array of
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
index 21a9608..03c7d1b 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
+++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -165,6 +167,12 @@
   SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS,              // SPIR-V Sec 3.29
   SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO,         // SPIR-V Sec 3.30
   SPV_OPERAND_TYPE_CAPABILITY,                    // SPIR-V Sec 3.31
+  SPV_OPERAND_TYPE_RAY_FLAGS,                     // SPIR-V Sec 3.RF
+  SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION,        // SPIR-V Sec 3.RQIntersection
+  SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE,  // SPIR-V Sec
+                                                           // 3.RQCommitted
+  SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE,  // SPIR-V Sec
+                                                           // 3.RQCandidate
 
   // Set 5:  Operands that are a single word bitmask.
   // Sometimes a set bit indicates the instruction requires still more operands.
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
index c31ccef..b904923 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
@@ -791,6 +791,18 @@
                                                  uint32_t shader_id,
                                                  uint32_t version = 2);
 
+// Create a pass to instrument OpDebugPrintf instructions.
+// This pass replaces all OpDebugPrintf instructions with instructions to write
+// a record containing the string id and the all specified values into a special
+// printf output buffer (if space allows). This pass is designed to support
+// the printf validation in the Vulkan validation layers.
+//
+// The instrumentation will write buffers in debug descriptor set |desc_set|.
+// It will write |shader_id| in each output record to identify the shader
+// module which generated the record.
+Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
+                                               uint32_t shader_id);
+
 // Create a pass to upgrade to the VulkanKHR memory model.
 // This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
 // Additionally, it modifies memory, image, atomic and barrier operations to
diff --git a/third_party/SPIRV-Tools/source/CMakeLists.txt b/third_party/SPIRV-Tools/source/CMakeLists.txt
index 4e7e10c..708ca84 100644
--- a/third_party/SPIRV-Tools/source/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/CMakeLists.txt
@@ -126,13 +126,11 @@
 endmacro(spvtools_vendor_tables)
 
 macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE)
-  set(OUTBASE ${spirv-tools_BINARY_DIR}/${NAME})
-  set(OUT_H ${OUTBASE}.h)
+  set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h)
   add_custom_command(OUTPUT ${OUT_H}
     COMMAND ${PYTHON_EXECUTABLE} ${LANG_HEADER_PROCESSING_SCRIPT}
-      --extinst-name=${NAME}
       --extinst-grammar=${GRAMMAR_FILE}
-      --extinst-output-base=${OUTBASE}
+      --extinst-output-path=${OUT_H}
     DEPENDS ${LANG_HEADER_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
     COMMENT "Generate language specific header for ${NAME}.")
   add_custom_target(spirv-tools-header-${NAME} DEPENDS ${OUT_H})
diff --git a/third_party/SPIRV-Tools/source/binary.cpp b/third_party/SPIRV-Tools/source/binary.cpp
index 0463061..f16bf52 100644
--- a/third_party/SPIRV-Tools/source/binary.cpp
+++ b/third_party/SPIRV-Tools/source/binary.cpp
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -633,6 +635,10 @@
     case SPV_OPERAND_TYPE_GROUP_OPERATION:
     case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
     case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
+    case SPV_OPERAND_TYPE_RAY_FLAGS:
+    case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
+    case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
+    case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
     case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
diff --git a/third_party/SPIRV-Tools/source/disassemble.cpp b/third_party/SPIRV-Tools/source/disassemble.cpp
index 4b3972b..af30ce0 100644
--- a/third_party/SPIRV-Tools/source/disassemble.cpp
+++ b/third_party/SPIRV-Tools/source/disassemble.cpp
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -277,6 +279,10 @@
     case SPV_OPERAND_TYPE_GROUP_OPERATION:
     case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
     case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
+    case SPV_OPERAND_TYPE_RAY_FLAGS:
+    case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
+    case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
+    case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
     case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
diff --git a/third_party/SPIRV-Tools/source/enum_set.h b/third_party/SPIRV-Tools/source/enum_set.h
index 2e7046d..d4d31e3 100644
--- a/third_party/SPIRV-Tools/source/enum_set.h
+++ b/third_party/SPIRV-Tools/source/enum_set.h
@@ -93,6 +93,10 @@
   // enum value is already in the set.
   void Add(EnumType c) { AddWord(ToWord(c)); }
 
+  // Removes the given enum value from the set.  This has no effect if the
+  // enum value is not in the set.
+  void Remove(EnumType c) { RemoveWord(ToWord(c)); }
+
   // Returns true if this enum value is in the set.
   bool Contains(EnumType c) const { return ContainsWord(ToWord(c)); }
 
@@ -141,6 +145,17 @@
     }
   }
 
+  // Removes the given enum value (as a 32-bit word) from the set.  This has no
+  // effect if the enum value is not in the set.
+  void RemoveWord(uint32_t word) {
+    if (auto new_bits = AsMask(word)) {
+      mask_ &= ~new_bits;
+    } else {
+      auto itr = Overflow().find(word);
+      if (itr != Overflow().end()) Overflow().erase(itr);
+    }
+  }
+
   // Returns true if the enum represented as a 32-bit word is in the set.
   bool ContainsWord(uint32_t word) const {
     // We shouldn't call Overflow() since this is a const method.
diff --git a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
index 4d5feea..3a9d604 100644
--- a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
@@ -42,6 +42,7 @@
         fuzzer_pass_add_dead_blocks.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_dead_continues.h
+        fuzzer_pass_add_equation_instructions.h
         fuzzer_pass_add_function_calls.h
         fuzzer_pass_add_global_variables.h
         fuzzer_pass_add_loads.h
@@ -61,7 +62,10 @@
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_outline_functions.h
         fuzzer_pass_permute_blocks.h
+        fuzzer_pass_permute_function_parameters.h
         fuzzer_pass_split_blocks.h
+        fuzzer_pass_swap_commutable_operands.h
+        fuzzer_pass_toggle_access_chain_instruction.h
         fuzzer_util.h
         id_use_descriptor.h
         instruction_descriptor.h
@@ -96,11 +100,13 @@
         transformation_composite_construct.h
         transformation_composite_extract.h
         transformation_copy_object.h
+        transformation_equation_instruction.h
         transformation_function_call.h
         transformation_load.h
         transformation_merge_blocks.h
         transformation_move_block_down.h
         transformation_outline_function.h
+        transformation_permute_function_parameters.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
         transformation_replace_id_with_synonym.h
@@ -110,6 +116,8 @@
         transformation_set_selection_control.h
         transformation_split_block.h
         transformation_store.h
+        transformation_swap_commutable_operands.h
+        transformation_toggle_access_chain_instruction.h
         transformation_vector_shuffle.h
         uniform_buffer_element_descriptor.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
@@ -126,6 +134,7 @@
         fuzzer_pass_add_dead_blocks.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_dead_continues.cpp
+        fuzzer_pass_add_equation_instructions.cpp
         fuzzer_pass_add_function_calls.cpp
         fuzzer_pass_add_global_variables.cpp
         fuzzer_pass_add_loads.cpp
@@ -145,7 +154,10 @@
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_outline_functions.cpp
         fuzzer_pass_permute_blocks.cpp
+        fuzzer_pass_permute_function_parameters.cpp
         fuzzer_pass_split_blocks.cpp
+        fuzzer_pass_swap_commutable_operands.cpp
+        fuzzer_pass_toggle_access_chain_instruction.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
         instruction_descriptor.cpp
@@ -179,11 +191,13 @@
         transformation_composite_construct.cpp
         transformation_composite_extract.cpp
         transformation_copy_object.cpp
+        transformation_equation_instruction.cpp
         transformation_function_call.cpp
         transformation_load.cpp
         transformation_merge_blocks.cpp
         transformation_move_block_down.cpp
         transformation_outline_function.cpp
+        transformation_permute_function_parameters.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
         transformation_replace_id_with_synonym.cpp
@@ -193,6 +207,8 @@
         transformation_set_selection_control.cpp
         transformation_split_block.cpp
         transformation_store.cpp
+        transformation_swap_commutable_operands.cpp
+        transformation_toggle_access_chain_instruction.cpp
         transformation_vector_shuffle.cpp
         uniform_buffer_element_descriptor.cpp
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
diff --git a/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h b/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h
index 046536f..7bb8b66 100644
--- a/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h
+++ b/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h
@@ -75,18 +75,8 @@
     // Register each value if necessary.
     for (auto value : {value1, value2}) {
       if (!Exists(value)) {
-        // Register the value in the equivalence relation.  This relies on
-        // T having a copy constructor.
-        auto unique_pointer_to_value = MakeUnique<T>(value);
-        auto pointer_to_value = unique_pointer_to_value.get();
-        owned_values_.push_back(std::move(unique_pointer_to_value));
-        value_set_.insert(pointer_to_value);
-
-        // Initially say that the value is its own parent and that it has no
-        // children.
-        assert(pointer_to_value && "Representatives should never be null.");
-        parent_[pointer_to_value] = pointer_to_value;
-        children_[pointer_to_value] = std::vector<const T*>();
+        // Register the value in the equivalence relation.
+        Register(value);
       }
     }
 
@@ -112,6 +102,27 @@
     }
   }
 
+  // Requires that |value| is not known to the equivalence relation. Registers
+  // it in its own equivalence class and returns a pointer to the equivalence
+  // class representative.
+  const T* Register(T& value) {
+    assert(!Exists(value));
+
+    // This relies on T having a copy constructor.
+    auto unique_pointer_to_value = MakeUnique<T>(value);
+    auto pointer_to_value = unique_pointer_to_value.get();
+    owned_values_.push_back(std::move(unique_pointer_to_value));
+    value_set_.insert(pointer_to_value);
+
+    // Initially say that the value is its own parent and that it has no
+    // children.
+    assert(pointer_to_value && "Representatives should never be null.");
+    parent_[pointer_to_value] = pointer_to_value;
+    children_[pointer_to_value] = std::vector<const T*>();
+
+    return pointer_to_value;
+  }
+
   // Returns exactly one representative per equivalence class.
   std::vector<const T*> GetEquivalenceClassRepresentatives() const {
     std::vector<const T*> result;
@@ -168,7 +179,6 @@
     return value_set_.find(&value) != value_set_.end();
   }
 
- private:
   // Returns the representative of the equivalence class of |value|, which must
   // already be known to the equivalence relation.  This is the 'Find' operation
   // in a classic union-find data structure.
@@ -207,6 +217,7 @@
     return result;
   }
 
+ private:
   // Maps every value to a parent.  The representative of an equivalence class
   // is its own parent.  A value's representative can be found by walking its
   // chain of ancestors.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
index 486e8f5..31d3b94 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
@@ -28,24 +28,13 @@
 
 namespace {
 
-std::string ToString(const protobufs::Fact& fact) {
-  assert(fact.fact_case() == protobufs::Fact::kConstantUniformFact &&
-         "Right now this is the only fact we know how to stringify.");
+std::string ToString(const protobufs::FactConstantUniform& fact) {
   std::stringstream stream;
-  stream << "("
-         << fact.constant_uniform_fact()
-                .uniform_buffer_element_descriptor()
-                .descriptor_set()
-         << ", "
-         << fact.constant_uniform_fact()
-                .uniform_buffer_element_descriptor()
-                .binding()
-         << ")[";
+  stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set()
+         << ", " << fact.uniform_buffer_element_descriptor().binding() << ")[";
 
   bool first = true;
-  for (auto index : fact.constant_uniform_fact()
-                        .uniform_buffer_element_descriptor()
-                        .index()) {
+  for (auto index : fact.uniform_buffer_element_descriptor().index()) {
     if (first) {
       first = false;
     } else {
@@ -57,7 +46,7 @@
   stream << "] == [";
 
   first = true;
-  for (auto constant_word : fact.constant_uniform_fact().constant_word()) {
+  for (auto constant_word : fact.constant_word()) {
     if (first) {
       first = false;
     } else {
@@ -70,6 +59,36 @@
   return stream.str();
 }
 
+std::string ToString(const protobufs::FactDataSynonym& fact) {
+  std::stringstream stream;
+  stream << fact.data1() << " = " << fact.data2();
+  return stream.str();
+}
+
+std::string ToString(const protobufs::FactIdEquation& fact) {
+  std::stringstream stream;
+  stream << fact.lhs_id();
+  stream << " " << static_cast<SpvOp>(fact.opcode());
+  for (auto rhs_id : fact.rhs_id()) {
+    stream << " " << rhs_id;
+  }
+  return stream.str();
+}
+
+std::string ToString(const protobufs::Fact& fact) {
+  switch (fact.fact_case()) {
+    case protobufs::Fact::kConstantUniformFact:
+      return ToString(fact.constant_uniform_fact());
+    case protobufs::Fact::kDataSynonymFact:
+      return ToString(fact.data_synonym_fact());
+    case protobufs::Fact::kIdEquationFact:
+      return ToString(fact.id_equation_fact());
+    default:
+      assert(false && "Stringification not supported for this fact.");
+      return "";
+  }
+}
+
 }  // namespace
 
 //=======================
@@ -331,16 +350,71 @@
 //==============================
 
 //==============================
-// Data synonym facts
+// Data synonym and id equation facts
+
+// This helper struct represents the right hand side of an equation as an
+// operator applied to a number of data descriptor operands.
+struct Operation {
+  SpvOp opcode;
+  std::vector<const protobufs::DataDescriptor*> operands;
+};
+
+// Hashing for operations, to allow deterministic unordered sets.
+struct OperationHash {
+  size_t operator()(const Operation& operation) const {
+    std::u32string hash;
+    hash.push_back(operation.opcode);
+    for (auto operand : operation.operands) {
+      hash.push_back(static_cast<uint32_t>(DataDescriptorHash()(operand)));
+    }
+    return std::hash<std::u32string>()(hash);
+  }
+};
+
+// Equality for operations, to allow deterministic unordered sets.
+struct OperationEquals {
+  bool operator()(const Operation& first, const Operation& second) const {
+    // Equal operations require...
+    //
+    // Equal opcodes.
+    if (first.opcode != second.opcode) {
+      return false;
+    }
+    // Matching operand counds.
+    if (first.operands.size() != second.operands.size()) {
+      return false;
+    }
+    // Equal operands.
+    for (uint32_t i = 0; i < first.operands.size(); i++) {
+      if (!DataDescriptorEquals()(first.operands[i], second.operands[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+// A helper, for debugging, to represent an operation as a string.
+std::string ToString(const Operation& operation) {
+  std::stringstream stream;
+  stream << operation.opcode;
+  for (auto operand : operation.operands) {
+    stream << " " << *operand;
+  }
+  return stream.str();
+}
 
 // The purpose of this class is to group the fields and data used to represent
-// facts about data synonyms.
-class FactManager::DataSynonymFacts {
+// facts about data synonyms and id equations.
+class FactManager::DataSynonymAndIdEquationFacts {
  public:
   // See method in FactManager which delegates to this method.
   void AddFact(const protobufs::FactDataSynonym& fact, opt::IRContext* context);
 
   // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactIdEquation& fact, opt::IRContext* context);
+
+  // See method in FactManager which delegates to this method.
   std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
       const protobufs::DataDescriptor& data_descriptor,
       opt::IRContext* context) const;
@@ -355,11 +429,12 @@
                     opt::IRContext* context) const;
 
  private:
-  // Adds |fact| to the set of managed facts, and recurses into sub-components
-  // of the data descriptors referenced in |fact|, if they are composites, to
+  // Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses
+  // into sub-components of the data descriptors, if they are composites, to
   // record that their components are pairwise-synonymous.
-  void AddFactRecursive(const protobufs::FactDataSynonym& fact,
-                        opt::IRContext* context);
+  void AddDataSynonymFactRecursive(const protobufs::DataDescriptor& dd1,
+                                   const protobufs::DataDescriptor& dd2,
+                                   opt::IRContext* context);
 
   // Inspects all known facts and adds corollary facts; e.g. if we know that
   // a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record
@@ -371,11 +446,21 @@
   void ComputeClosureOfFacts(opt::IRContext* context) const;
 
   // Returns true if and only if |dd1| and |dd2| are valid data descriptors
-  // whose associated data have the same type.
+  // whose associated data have the same type (modulo integer signedness).
   bool DataDescriptorsAreWellFormedAndComparable(
       opt::IRContext* context, const protobufs::DataDescriptor& dd1,
       const protobufs::DataDescriptor& dd2) const;
 
+  // Requires that |lhs_dd| and every element of |rhs_dds| is present in the
+  // |synonymous_| equivalence relation and is its own representative.  Records
+  // the fact that the equation "|lhs_dd| |opcode| |rhs_dds|" holds, and adds
+  // any corollaries, in the form of data synonym or equation facts, that
+  // follow from this and other known facts.
+  void AddEquationFactRecursive(
+      const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
+      const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
+      opt::IRContext* context);
+
   // The data descriptors that are known to be synonymous with one another are
   // captured by this equivalence relation.
   //
@@ -396,31 +481,243 @@
   // whether a new fact has been added since the last time such a computation
   // was performed.
   //
-  // It is mutable so faciliate having const methods, that provide answers to
+  // It is mutable to facilitate having const methods, that provide answers to
   // questions about data synonym facts, triggering closure computation on
   // demand.
-  mutable bool closure_computation_required = false;
+  mutable bool closure_computation_required_ = false;
+
+  // Represents a set of equations on data descriptors as a map indexed by
+  // left-hand-side, mapping a left-hand-side to a set of operations, each of
+  // which (together with the left-hand-side) defines an equation.
+  //
+  // All data descriptors occurring in equations are required to be present in
+  // the |synonymous_| equivalence relation, and to be their own representatives
+  // in that relation.
+  std::unordered_map<
+      const protobufs::DataDescriptor*,
+      std::unordered_set<Operation, OperationHash, OperationEquals>>
+      id_equations_;
 };
 
-void FactManager::DataSynonymFacts::AddFact(
+void FactManager::DataSynonymAndIdEquationFacts::AddFact(
     const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
   // Add the fact, including all facts relating sub-components of the data
   // descriptors that are involved.
-  AddFactRecursive(fact, context);
+  AddDataSynonymFactRecursive(fact.data1(), fact.data2(), context);
 }
 
-void FactManager::DataSynonymFacts::AddFactRecursive(
-    const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
-  assert(DataDescriptorsAreWellFormedAndComparable(context, fact.data1(),
-                                                   fact.data2()));
+void FactManager::DataSynonymAndIdEquationFacts::AddFact(
+    const protobufs::FactIdEquation& fact, opt::IRContext* context) {
+  protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
+
+  // Register the LHS in the equivalence relation if needed, and get a pointer
+  // to its representative.
+  if (!synonymous_.Exists(lhs_dd)) {
+    synonymous_.Register(lhs_dd);
+  }
+  const protobufs::DataDescriptor* lhs_dd_ptr = synonymous_.Find(&lhs_dd);
+
+  // Get equivalence class representatives for all ids used on the RHS of the
+  // equation.
+  std::vector<const protobufs::DataDescriptor*> rhs_dd_ptrs;
+  for (auto rhs_id : fact.rhs_id()) {
+    // Register a data descriptor based on this id in the equivalence relation
+    // if needed, and then record the equivalence class representative.
+    protobufs::DataDescriptor rhs_dd = MakeDataDescriptor(rhs_id, {});
+    if (!synonymous_.Exists(rhs_dd)) {
+      synonymous_.Register(rhs_dd);
+    }
+    rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd));
+  }
+  // We now have the equation in a form where it refers exclusively to
+  // equivalence class representatives.  Add it to our set of facts and work
+  // out any follow-on facts.
+  AddEquationFactRecursive(*lhs_dd_ptr, static_cast<SpvOp>(fact.opcode()),
+                           rhs_dd_ptrs, context);
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
+    const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
+    const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
+    opt::IRContext* context) {
+  // Precondition: all data descriptors referenced in this equation must be
+  // equivalence class representatives - i.e. the equation must be in canonical
+  // form.
+  assert(synonymous_.Exists(lhs_dd));
+  assert(synonymous_.Find(&lhs_dd) == &lhs_dd);
+  for (auto rhs_dd : rhs_dds) {
+    (void)(rhs_dd);  // Keep compilers happy in release mode.
+    assert(synonymous_.Exists(*rhs_dd));
+    assert(synonymous_.Find(rhs_dd) == rhs_dd);
+  }
+
+  if (id_equations_.count(&lhs_dd) == 0) {
+    // We have not seen an equation with this LHS before, so associate the LHS
+    // with an initially empty set.
+    id_equations_.insert(
+        {&lhs_dd,
+         std::unordered_set<Operation, OperationHash, OperationEquals>()});
+  }
+
+  {
+    auto existing_equations = id_equations_.find(&lhs_dd);
+    assert(existing_equations != id_equations_.end() &&
+           "A set of operations should be present, even if empty.");
+
+    Operation new_operation = {opcode, rhs_dds};
+    if (existing_equations->second.count(new_operation)) {
+      // This equation is known, so there is nothing further to be done.
+      return;
+    }
+    // Add the equation to the set of known equations.
+    existing_equations->second.insert(new_operation);
+  }
+
+  // Now try to work out corollaries implied by the new equation and existing
+  // facts.
+  switch (opcode) {
+    case SpvOpIAdd: {
+      // Equation form: "a = b + c"
+      {
+        auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
+        if (existing_first_operand_equations != id_equations_.end()) {
+          for (auto equation : existing_first_operand_equations->second) {
+            if (equation.opcode == SpvOpISub) {
+              // Equation form: "a = (d - e) + c"
+              if (equation.operands[1] == rhs_dds[1]) {
+                // Equation form: "a = (d - c) + c"
+                // We can thus infer "a = d"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
+                                            context);
+              }
+              if (equation.operands[0] == rhs_dds[1]) {
+                // Equation form: "a = (c - e) + c"
+                // We can thus infer "a = -e"
+                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                         {equation.operands[1]}, context);
+              }
+            }
+          }
+        }
+      }
+      {
+        auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
+        if (existing_second_operand_equations != id_equations_.end()) {
+          for (auto equation : existing_second_operand_equations->second) {
+            if (equation.opcode == SpvOpISub) {
+              // Equation form: "a = b + (d - e)"
+              if (equation.operands[1] == rhs_dds[0]) {
+                // Equation form: "a = b + (d - b)"
+                // We can thus infer "a = d"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
+                                            context);
+              }
+            }
+          }
+        }
+      }
+      break;
+    }
+    case SpvOpISub: {
+      // Equation form: "a = b - c"
+      {
+        auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
+        if (existing_first_operand_equations != id_equations_.end()) {
+          for (auto equation : existing_first_operand_equations->second) {
+            if (equation.opcode == SpvOpIAdd) {
+              // Equation form: "a = (d + e) - c"
+              if (equation.operands[0] == rhs_dds[1]) {
+                // Equation form: "a = (c + e) - c"
+                // We can thus infer "a = e"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
+                                            context);
+              }
+              if (equation.operands[1] == rhs_dds[1]) {
+                // Equation form: "a = (d + c) - c"
+                // We can thus infer "a = d"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
+                                            context);
+              }
+            }
+
+            if (equation.opcode == SpvOpISub) {
+              // Equation form: "a = (d - e) - c"
+              if (equation.operands[0] == rhs_dds[1]) {
+                // Equation form: "a = (c - e) - c"
+                // We can thus infer "a = -e"
+                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                         {equation.operands[1]}, context);
+              }
+            }
+          }
+        }
+      }
+
+      {
+        auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
+        if (existing_second_operand_equations != id_equations_.end()) {
+          for (auto equation : existing_second_operand_equations->second) {
+            if (equation.opcode == SpvOpIAdd) {
+              // Equation form: "a = b - (d + e)"
+              if (equation.operands[0] == rhs_dds[0]) {
+                // Equation form: "a = b - (b + e)"
+                // We can thus infer "a = -e"
+                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                         {equation.operands[1]}, context);
+              }
+              if (equation.operands[1] == rhs_dds[0]) {
+                // Equation form: "a = b - (d + b)"
+                // We can thus infer "a = -d"
+                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                         {equation.operands[0]}, context);
+              }
+            }
+            if (equation.opcode == SpvOpISub) {
+              // Equation form: "a = b - (d - e)"
+              if (equation.operands[0] == rhs_dds[0]) {
+                // Equation form: "a = b - (b - e)"
+                // We can thus infer "a = e"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
+                                            context);
+              }
+            }
+          }
+        }
+      }
+      break;
+    }
+    case SpvOpLogicalNot:
+    case SpvOpSNegate: {
+      // Equation form: "a = !b" or "a = -b"
+      auto existing_equations = id_equations_.find(rhs_dds[0]);
+      if (existing_equations != id_equations_.end()) {
+        for (auto equation : existing_equations->second) {
+          if (equation.opcode == opcode) {
+            // Equation form: "a = !!b" or "a = -(-b)"
+            // We can thus infer "a = b"
+            AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
+          }
+        }
+      }
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
+    const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2,
+    opt::IRContext* context) {
+  assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2));
 
   // Record that the data descriptors provided in the fact are equivalent.
-  synonymous_.MakeEquivalent(fact.data1(), fact.data2());
+  synonymous_.MakeEquivalent(dd1, dd2);
   // As we have updated the equivalence relation, we might be able to deduce
   // more facts by performing a closure computation, so we record that such a
   // computation is required; it will be performed next time a method answering
   // a data synonym fact-related question is invoked.
-  closure_computation_required = true;
+  closure_computation_required_ = true;
 
   // We now check whether this is a synonym about composite objects.  If it is,
   // we can recursively add synonym facts about their associated sub-components.
@@ -428,9 +725,8 @@
   // Get the type of the object referred to by the first data descriptor in the
   // synonym fact.
   uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices(
-      context,
-      context->get_def_use_mgr()->GetDef(fact.data1().object())->type_id(),
-      fact.data1().index());
+      context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
+      dd1.index());
   auto type = context->get_type_mgr()->GetType(type_id);
   auto type_instruction = context->get_def_use_mgr()->GetDef(type_id);
   assert(type != nullptr &&
@@ -460,21 +756,19 @@
   //   obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
   for (uint32_t i = 0; i < num_composite_elements; i++) {
     std::vector<uint32_t> extended_indices1 =
-        fuzzerutil::RepeatedFieldToVector(fact.data1().index());
+        fuzzerutil::RepeatedFieldToVector(dd1.index());
     extended_indices1.push_back(i);
     std::vector<uint32_t> extended_indices2 =
-        fuzzerutil::RepeatedFieldToVector(fact.data2().index());
+        fuzzerutil::RepeatedFieldToVector(dd2.index());
     extended_indices2.push_back(i);
-    protobufs::FactDataSynonym extended_data_synonym_fact;
-    *extended_data_synonym_fact.mutable_data1() =
-        MakeDataDescriptor(fact.data1().object(), std::move(extended_indices1));
-    *extended_data_synonym_fact.mutable_data2() =
-        MakeDataDescriptor(fact.data2().object(), std::move(extended_indices2));
-    AddFactRecursive(extended_data_synonym_fact, context);
+    AddDataSynonymFactRecursive(
+        MakeDataDescriptor(dd1.object(), std::move(extended_indices1)),
+        MakeDataDescriptor(dd2.object(), std::move(extended_indices2)),
+        context);
   }
 }
 
-void FactManager::DataSynonymFacts::ComputeClosureOfFacts(
+void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
     opt::IRContext* context) const {
   // Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct
   // data descriptors that describe objects of the same composite type, and that
@@ -551,10 +845,10 @@
 
   // We keep looking for new facts until we perform a complete pass over the
   // equivalence relation without finding any new facts.
-  while (closure_computation_required) {
+  while (closure_computation_required_) {
     // We have not found any new facts yet during this pass; we set this to
     // 'true' if we do find a new fact.
-    closure_computation_required = false;
+    closure_computation_required_ = false;
 
     // Consider each class in the equivalence relation.
     for (auto representative :
@@ -738,7 +1032,7 @@
             synonymous_.MakeEquivalent(dd1_prefix, dd2_prefix);
             // As we have added a new synonym fact, we might benefit from doing
             // another pass over the equivalence relation.
-            closure_computation_required = true;
+            closure_computation_required_ = true;
             // Now that we know this pair of data descriptors are synonymous,
             // there is no point recording how close they are to being
             // synonymous.
@@ -750,20 +1044,56 @@
   }
 }
 
-bool FactManager::DataSynonymFacts::DataDescriptorsAreWellFormedAndComparable(
-    opt::IRContext* context, const protobufs::DataDescriptor& dd1,
-    const protobufs::DataDescriptor& dd2) const {
-  auto end_type_1 = fuzzerutil::WalkCompositeTypeIndices(
+bool FactManager::DataSynonymAndIdEquationFacts::
+    DataDescriptorsAreWellFormedAndComparable(
+        opt::IRContext* context, const protobufs::DataDescriptor& dd1,
+        const protobufs::DataDescriptor& dd2) const {
+  auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
       context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
       dd1.index());
-  auto end_type_2 = fuzzerutil::WalkCompositeTypeIndices(
+  auto end_type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
       context, context->get_def_use_mgr()->GetDef(dd2.object())->type_id(),
       dd2.index());
-  return end_type_1 && end_type_1 == end_type_2;
+  // The end types of the data descriptors must exist.
+  if (end_type_id_1 == 0 || end_type_id_2 == 0) {
+    return false;
+  }
+  // If the end types are the same, the data descriptors are comparable.
+  if (end_type_id_1 == end_type_id_2) {
+    return true;
+  }
+  // Otherwise they are only comparable if they are integer scalars or integer
+  // vectors that differ only in signedness.
+
+  // Get both types.
+  const opt::analysis::Type* type_1 =
+      context->get_type_mgr()->GetType(end_type_id_1);
+  const opt::analysis::Type* type_2 =
+      context->get_type_mgr()->GetType(end_type_id_2);
+
+  // If the first type is a vector, check that the second type is a vector of
+  // the same width, and drill down to the vector element types.
+  if (type_1->AsVector()) {
+    if (!type_2->AsVector()) {
+      return false;
+    }
+    if (type_1->AsVector()->element_count() !=
+        type_2->AsVector()->element_count()) {
+      return false;
+    }
+    type_1 = type_1->AsVector()->element_type();
+    type_2 = type_2->AsVector()->element_type();
+  }
+  // Check that type_1 and type_2 are both integer types of the same bit-width
+  // (but with potentially different signedness).
+  auto integer_type_1 = type_1->AsInteger();
+  auto integer_type_2 = type_2->AsInteger();
+  return integer_type_1 && integer_type_2 &&
+         integer_type_1->width() == integer_type_2->width();
 }
 
 std::vector<const protobufs::DataDescriptor*>
-FactManager::DataSynonymFacts::GetSynonymsForDataDescriptor(
+FactManager::DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor(
     const protobufs::DataDescriptor& data_descriptor,
     opt::IRContext* context) const {
   ComputeClosureOfFacts(context);
@@ -774,7 +1104,7 @@
 }
 
 std::vector<uint32_t>
-FactManager::DataSynonymFacts ::GetIdsForWhichSynonymsAreKnown(
+FactManager::DataSynonymAndIdEquationFacts ::GetIdsForWhichSynonymsAreKnown(
     opt::IRContext* context) const {
   ComputeClosureOfFacts(context);
   std::vector<uint32_t> result;
@@ -786,12 +1116,12 @@
   return result;
 }
 
-bool FactManager::DataSynonymFacts::IsSynonymous(
+bool FactManager::DataSynonymAndIdEquationFacts::IsSynonymous(
     const protobufs::DataDescriptor& data_descriptor1,
     const protobufs::DataDescriptor& data_descriptor2,
     opt::IRContext* context) const {
-  const_cast<FactManager::DataSynonymFacts*>(this)->ComputeClosureOfFacts(
-      context);
+  const_cast<FactManager::DataSynonymAndIdEquationFacts*>(this)
+      ->ComputeClosureOfFacts(context);
   return synonymous_.Exists(data_descriptor1) &&
          synonymous_.Exists(data_descriptor2) &&
          synonymous_.IsEquivalent(data_descriptor1, data_descriptor2);
@@ -891,7 +1221,8 @@
 
 FactManager::FactManager()
     : uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
-      data_synonym_facts_(MakeUnique<DataSynonymFacts>()),
+      data_synonym_and_id_equation_facts_(
+          MakeUnique<DataSynonymAndIdEquationFacts>()),
       dead_block_facts_(MakeUnique<DeadBlockFacts>()),
       livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
       irrelevant_pointee_value_facts_(
@@ -918,7 +1249,8 @@
       return uniform_constant_facts_->AddFact(fact.constant_uniform_fact(),
                                               context);
     case protobufs::Fact::kDataSynonymFact:
-      data_synonym_facts_->AddFact(fact.data_synonym_fact(), context);
+      data_synonym_and_id_equation_facts_->AddFact(fact.data_synonym_fact(),
+                                                   context);
       return true;
     case protobufs::Fact::kBlockIsDeadFact:
       dead_block_facts_->AddFact(fact.block_is_dead_fact());
@@ -938,7 +1270,7 @@
   protobufs::FactDataSynonym fact;
   *fact.mutable_data1() = data1;
   *fact.mutable_data2() = data2;
-  data_synonym_facts_->AddFact(fact, context);
+  data_synonym_and_id_equation_facts_->AddFact(fact, context);
 }
 
 std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
@@ -973,15 +1305,16 @@
 
 std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown(
     opt::IRContext* context) const {
-  return data_synonym_facts_->GetIdsForWhichSynonymsAreKnown(context);
+  return data_synonym_and_id_equation_facts_->GetIdsForWhichSynonymsAreKnown(
+      context);
 }
 
 std::vector<const protobufs::DataDescriptor*>
 FactManager::GetSynonymsForDataDescriptor(
     const protobufs::DataDescriptor& data_descriptor,
     opt::IRContext* context) const {
-  return data_synonym_facts_->GetSynonymsForDataDescriptor(data_descriptor,
-                                                           context);
+  return data_synonym_and_id_equation_facts_->GetSynonymsForDataDescriptor(
+      data_descriptor, context);
 }
 
 std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
@@ -993,8 +1326,8 @@
     const protobufs::DataDescriptor& data_descriptor1,
     const protobufs::DataDescriptor& data_descriptor2,
     opt::IRContext* context) const {
-  return data_synonym_facts_->IsSynonymous(data_descriptor1, data_descriptor2,
-                                           context);
+  return data_synonym_and_id_equation_facts_->IsSynonymous(
+      data_descriptor1, data_descriptor2, context);
 }
 
 bool FactManager::BlockIsDead(uint32_t block_id) const {
@@ -1027,5 +1360,17 @@
   irrelevant_pointee_value_facts_->AddFact(fact);
 }
 
+void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
+                                    const std::vector<uint32_t>& rhs_id,
+                                    opt::IRContext* context) {
+  protobufs::FactIdEquation fact;
+  fact.set_lhs_id(lhs_id);
+  fact.set_opcode(opcode);
+  for (auto an_rhs_id : rhs_id) {
+    fact.add_rhs_id(an_rhs_id);
+  }
+  data_synonym_and_id_equation_facts_->AddFact(fact, context);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
index 55cbfa0..f80d677 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
@@ -68,6 +68,14 @@
   // is irrelevant: it does not affect the observable behaviour of the module.
   void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id);
 
+  // Records the fact that |lhs_id| is defined by the equation:
+  //
+  //   |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]|
+  //
+  void AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
+                         const std::vector<uint32_t>& rhs_id,
+                         opt::IRContext* context);
+
   // The fact manager is responsible for managing a few distinct categories of
   // facts. In principle there could be different fact managers for each kind
   // of fact, but in practice providing one 'go to' place for facts is
@@ -179,9 +187,10 @@
   std::unique_ptr<ConstantUniformFacts>
       uniform_constant_facts_;  // Unique pointer to internal data.
 
-  class DataSynonymFacts;  // Opaque class for management of data synonym facts.
-  std::unique_ptr<DataSynonymFacts>
-      data_synonym_facts_;  // Unique pointer to internal data.
+  class DataSynonymAndIdEquationFacts;  // Opaque class for management of data
+                                        // synonym and id equation facts.
+  std::unique_ptr<DataSynonymAndIdEquationFacts>
+      data_synonym_and_id_equation_facts_;  // Unique pointer to internal data.
 
   class DeadBlockFacts;  // Opaque class for management of dead block facts.
   std::unique_ptr<DeadBlockFacts>
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
index 6c2821c..119bd3c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
@@ -26,6 +26,7 @@
 #include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
+#include "source/fuzz/fuzzer_pass_add_equation_instructions.h"
 #include "source/fuzz/fuzzer_pass_add_function_calls.h"
 #include "source/fuzz/fuzzer_pass_add_global_variables.h"
 #include "source/fuzz/fuzzer_pass_add_loads.h"
@@ -44,7 +45,10 @@
 #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_permute_function_parameters.h"
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
+#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
+#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/pseudo_random_generator.h"
 #include "source/opt/build_module.h"
@@ -200,6 +204,9 @@
     MaybeAddPass<FuzzerPassAddDeadContinues>(&passes, ir_context.get(),
                                              &fact_manager, &fuzzer_context,
                                              transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddEquationInstructions>(
+        &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddFunctionCalls>(&passes, ir_context.get(),
                                              &fact_manager, &fuzzer_context,
                                              transformation_sequence_out);
@@ -239,6 +246,9 @@
     MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
                                           &fact_manager, &fuzzer_context,
                                           transformation_sequence_out);
+    MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
+        &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassSplitBlocks>(&passes, ir_context.get(),
                                         &fact_manager, &fuzzer_context,
                                         transformation_sequence_out);
@@ -276,6 +286,12 @@
   MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
       &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
       transformation_sequence_out);
+  MaybeAddPass<FuzzerPassSwapCommutableOperands>(
+      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
+  MaybeAddPass<FuzzerPassToggleAccessChainInstruction>(
+      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
   for (auto& pass : final_passes) {
     if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
       return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
index 1265772..2f9fc5a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
@@ -30,6 +30,8 @@
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingEquationInstruction = {5,
+                                                                          90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingLoad = {5, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
@@ -58,8 +60,11 @@
 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> kChanceOfPermutingParameters = {30, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
+    20, 90};
 
 // Default limits for various quantities that are chosen during fuzzing.
 // Keep them in alphabetical order.
@@ -102,6 +107,8 @@
       ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
   chance_of_adding_dead_continue_ =
       ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
+  chance_of_adding_equation_instruction_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingEquationInstruction);
   chance_of_adding_global_variable_ =
       ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
   chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
@@ -142,9 +149,13 @@
       ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
   chance_of_outlining_function_ =
       ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
+  chance_of_permuting_parameters_ =
+      ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
   chance_of_replacing_id_with_synonym_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+  chance_of_toggling_access_chain_instruction_ =
+      ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
 }
 
 FuzzerContext::~FuzzerContext() = default;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
index 23127ff..1529705 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
@@ -62,6 +62,40 @@
     return result;
   }
 
+  // Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively.
+  // |lo| and |hi| must be valid indices to the |sequence|
+  template <typename T>
+  void Shuffle(std::vector<T>* sequence, size_t lo, size_t hi) const {
+    auto& array = *sequence;
+
+    if (array.empty()) {
+      return;
+    }
+
+    assert(lo <= hi && hi < array.size() && "lo and/or hi indices are invalid");
+
+    // i > lo to account for potential infinite loop when lo == 0
+    for (size_t i = hi; i > lo; --i) {
+      auto index =
+          random_generator_->RandomUint32(static_cast<uint32_t>(i - lo + 1));
+
+      if (lo + index != i) {
+        // Introduce std::swap to the scope but don't use it
+        // directly since there might be a better overload
+        using std::swap;
+        swap(array[lo + index], array[i]);
+      }
+    }
+  }
+
+  // Ramdomly shuffles a |sequence|
+  template <typename T>
+  void Shuffle(std::vector<T>* sequence) const {
+    if (!sequence->empty()) {
+      Shuffle(sequence, 0, sequence->size() - 1);
+    }
+  }
+
   // Yields an id that is guaranteed not to be used in the module being fuzzed,
   // or to have been issued before.
   uint32_t GetFreshId();
@@ -82,6 +116,9 @@
   uint32_t GetChanceOfAddingDeadContinue() {
     return chance_of_adding_dead_continue_;
   }
+  uint32_t GetChanceOfAddingEquationInstruction() {
+    return chance_of_adding_equation_instruction_;
+  }
   uint32_t GetChanceOfAddingGlobalVariable() {
     return chance_of_adding_global_variable_;
   }
@@ -136,10 +173,16 @@
   uint32_t GetChanceOfOutliningFunction() {
     return chance_of_outlining_function_;
   }
+  uint32_t GetChanceOfPermutingParameters() {
+    return chance_of_permuting_parameters_;
+  }
   uint32_t GetChanceOfReplacingIdWithSynonym() {
     return chance_of_replacing_id_with_synonym_;
   }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+  uint32_t GetChanceOfTogglingAccessChainInstruction() {
+    return chance_of_toggling_access_chain_instruction_;
+  }
   uint32_t GetRandomLoopControlPeelCount() {
     return random_generator_->RandomUint32(max_loop_control_peel_count_);
   }
@@ -177,6 +220,7 @@
   uint32_t chance_of_adding_dead_block_;
   uint32_t chance_of_adding_dead_break_;
   uint32_t chance_of_adding_dead_continue_;
+  uint32_t chance_of_adding_equation_instruction_;
   uint32_t chance_of_adding_global_variable_;
   uint32_t chance_of_adding_load_;
   uint32_t chance_of_adding_local_variable_;
@@ -199,8 +243,10 @@
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_obfuscating_constant_;
   uint32_t chance_of_outlining_function_;
+  uint32_t chance_of_permuting_parameters_;
   uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_splitting_block_;
+  uint32_t chance_of_toggling_access_chain_instruction_;
 
   // Limits associated with various quantities for which random values are
   // chosen during fuzzing.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
index 4a22a21..a76f10d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
@@ -22,6 +22,7 @@
 #include "source/fuzz/transformation_add_global_undef.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"
@@ -89,12 +90,12 @@
   return result;
 }
 
-void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
+void FuzzerPass::ForEachInstructionWithInstructionDescriptor(
     std::function<
         void(opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)>
-        maybe_apply_transformation) {
+        action) {
   // Consider every block in every function.
   for (auto& function : *GetIRContext()->module()) {
     for (auto& block : function) {
@@ -132,11 +133,10 @@
         const SpvOp opcode = inst_it->opcode();
 
         // Invoke the provided function, which might apply a transformation.
-        maybe_apply_transformation(
-            &function, &block, inst_it,
-            MakeInstructionDescriptor(
-                base, opcode,
-                skip_count.count(opcode) ? skip_count.at(opcode) : 0));
+        action(&function, &block, inst_it,
+               MakeInstructionDescriptor(
+                   base, opcode,
+                   skip_count.count(opcode) ? skip_count.at(opcode) : 0));
 
         if (!inst_it->HasResultId()) {
           skip_count[opcode] =
@@ -180,6 +180,26 @@
   return result;
 }
 
+uint32_t FuzzerPass::FindOrCreateFunctionType(
+    uint32_t return_type_id, const std::vector<uint32_t>& argument_id) {
+  // FindFunctionType has a sigle argument for OpTypeFunction operands
+  // so we will have to copy them all in this vector
+  std::vector<uint32_t> type_ids(argument_id.size() + 1);
+  type_ids[0] = return_type_id;
+  std::copy(argument_id.begin(), argument_id.end(), type_ids.begin() + 1);
+
+  // Check if type exists
+  auto existing_id = fuzzerutil::FindFunctionType(GetIRContext(), type_ids);
+  if (existing_id) {
+    return existing_id;
+  }
+
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddTypeFunction(result, return_type_id, argument_id));
+  return result;
+}
+
 uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
                                             uint32_t component_count) {
   assert(component_count >= 2 && component_count <= 4 &&
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
index 7052685..46ee408 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
@@ -70,24 +70,24 @@
   // all times tracking an instruction descriptor that allows the latest
   // instruction to be located even if it has no result id.
   //
-  // The code to manipulate the instruction descriptor is a bit fiddly, and the
+  // The code to manipulate the instruction descriptor is a bit fiddly.  The
   // point of this method is to avoiding having to duplicate it in multiple
   // transformation passes.
   //
-  // The function |maybe_apply_transformation| is invoked for each instruction
-  // |inst_it| in block |block| of function |function| that is encountered.  The
+  // The function |action| is invoked for each instruction |inst_it| in block
+  // |block| of function |function| that is encountered.  The
   // |instruction_descriptor| parameter to the function object allows |inst_it|
   // to be identified.
   //
-  // The job of |maybe_apply_transformation| is to randomly decide whether to
-  // try to apply some transformation, and then - if selected - to attempt to
-  // apply it.
-  void MaybeAddTransformationBeforeEachInstruction(
+  // In most intended use cases, the job of |action| is to randomly decide
+  // whether to try to apply some transformation, and then - if selected - to
+  // attempt to apply it.
+  void ForEachInstructionWithInstructionDescriptor(
       std::function<
           void(opt::Function* function, opt::BasicBlock* block,
                opt::BasicBlock::iterator inst_it,
                const protobufs::InstructionDescriptor& instruction_descriptor)>
-          maybe_apply_transformation);
+          action);
 
   // A generic helper for applying a transformation that should be applicable
   // by construction, and adding it to the sequence of applied transformations.
@@ -112,6 +112,12 @@
   // instruction does not exist, a transformation is applied to add it.
   uint32_t FindOrCreate32BitFloatType();
 
+  // Returns the id of an OpTypeFunction %<return_type_id> %<...argument_id>
+  // instruction. If such an instruction doesn't exist, a transformation
+  // is applied to create a new one.
+  uint32_t FindOrCreateFunctionType(uint32_t return_type_id,
+                                    const std::vector<uint32_t>& argument_id);
+
   // Returns the id of an OpTypeVector instruction, with |component_type_id|
   // (which must already exist) as its base type, and |component_count|
   // elements (which must be in the range [2, 4]).  If such an instruction does
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
index 11f368e..cfc2812 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
@@ -29,7 +29,7 @@
 FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default;
 
 void FuzzerPassAddAccessChains::Apply() {
-  MaybeAddTransformationBeforeEachInstruction(
+  ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
index fa6b098..aefc2fc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
-
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_add_dead_break.h"
 #include "source/opt/ir_context.h"
 
@@ -34,11 +34,16 @@
   // We consider each function separately.
   for (auto& function : *GetIRContext()->module()) {
     // For a given function, we find all the merge blocks in that function.
-    std::vector<uint32_t> merge_block_ids;
+    std::vector<opt::BasicBlock*> merge_blocks;
     for (auto& block : function) {
       auto maybe_merge_id = block.MergeBlockIdIfAny();
       if (maybe_merge_id) {
-        merge_block_ids.push_back(maybe_merge_id);
+        auto merge_block =
+            fuzzerutil::MaybeFindBlock(GetIRContext(), maybe_merge_id);
+
+        assert(merge_block && "Merge block can't be null");
+
+        merge_blocks.push_back(merge_block);
       }
     }
     // We rather aggressively consider the possibility of adding a break from
@@ -46,12 +51,34 @@
     // inapplicable as they would be illegal.  That's OK - we later discard the
     // ones that turn out to be no good.
     for (auto& block : function) {
-      for (auto merge_block_id : merge_block_ids) {
-        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right
-        //  now we completely ignore OpPhi instructions at merge blocks.  This
-        //  will lead to interesting opportunities being missed.
+      for (auto* merge_block : merge_blocks) {
+        // Populate this vector with ids that are available at the branch point
+        // of this basic block. We will use these ids to update OpPhi
+        // instructions later.
+        std::vector<uint32_t> phi_ids;
+
+        // Determine how we need to adjust OpPhi instructions' operands
+        // for this transformation to be valid.
+        //
+        // If |block| has a branch to |merge_block|, the latter must have all of
+        // its OpPhi instructions set up correctly - we don't need to adjust
+        // anything.
+        if (!block.IsSuccessor(merge_block)) {
+          merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
+            // Add an additional operand for OpPhi instruction.
+            //
+            // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
+            // If we have a way to communicate to the fact manager
+            // that a specific id use is irrelevant and could be replaced with
+            // something else, we should add such a fact about the zero
+            // provided as an OpPhi operand
+            phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+          });
+        }
+
         auto candidate_transformation = TransformationAddDeadBreak(
-            block.id(), merge_block_id, GetFuzzerContext()->ChooseEven(), {});
+            block.id(), merge_block->id(), GetFuzzerContext()->ChooseEven(),
+            std::move(phi_ids));
         if (candidate_transformation.IsApplicable(GetIRContext(),
                                                   *GetFactManager())) {
           // Only consider a transformation as a candidate if it is applicable.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
index 51bcb91..852df3d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
-
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_add_dead_continue.h"
 #include "source/opt/ir_context.h"
 
@@ -32,15 +32,46 @@
   // Consider every block in every function.
   for (auto& function : *GetIRContext()->module()) {
     for (auto& block : function) {
+      // Get the label id of the continue target of the innermost loop.
+      auto continue_block_id =
+          block.IsLoopHeader()
+              ? block.ContinueBlockId()
+              : GetIRContext()->GetStructuredCFGAnalysis()->LoopContinueBlock(
+                    block.id());
+
+      // This transformation is not applicable if current block is not inside a
+      // loop.
+      if (continue_block_id == 0) {
+        continue;
+      }
+
+      auto* continue_block =
+          fuzzerutil::MaybeFindBlock(GetIRContext(), continue_block_id);
+      assert(continue_block && "Continue block is null");
+
+      // Analyze return type of each OpPhi instruction in the continue target
+      // and provide an id for the transformation if needed.
+      std::vector<uint32_t> phi_ids;
+      // Check whether current block has an edge to the continue target.
+      // If this is the case, we don't need to do anything.
+      if (!block.IsSuccessor(continue_block)) {
+        continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
+          // Add an additional operand for OpPhi instruction.
+          //
+          // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
+          // If we have a way to communicate to the fact manager
+          // that a specific id use is irrelevant and could be replaced with
+          // something else, we should add such a fact about the zero
+          // provided as an OpPhi operand
+          phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+        });
+      }
+
       // Make a transformation to add a dead continue from this node; if the
       // node turns out to be inappropriate (e.g. by not being in a loop) the
       // precondition for the transformation will fail and it will be ignored.
-      //
-      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right
-      //  now we completely ignore OpPhi instructions at continue targets.
-      //  This will lead to interesting opportunities being missed.
       auto candidate_transformation = TransformationAddDeadContinue(
-          block.id(), GetFuzzerContext()->ChooseEven(), {});
+          block.id(), GetFuzzerContext()->ChooseEven(), std::move(phi_ids));
       // Probabilistically decide whether to apply the transformation in the
       // case that it is applicable.
       if (candidate_transformation.IsApplicable(GetIRContext(),
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
new file mode 100644
index 0000000..7f34344
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
@@ -0,0 +1,238 @@
+// Copyright (c) 2020 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_add_equation_instructions.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_equation_instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() =
+    default;
+
+void FuzzerPassAddEquationInstructions::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor) {
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingEquationInstruction())) {
+          return;
+        }
+
+        // Check that it is OK to add an equation instruction before the given
+        // instruction in principle - e.g. check that this does not lead to
+        // inserting before an OpVariable or OpPhi instruction.  We use OpIAdd
+        // as an example opcode for this check, to be representative of *some*
+        // opcode that defines an equation, even though we may choose a
+        // different opcode below.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
+          return;
+        }
+
+        // Get all available instructions with result ids and types that are not
+        // OpUndef.
+        std::vector<opt::Instruction*> available_instructions =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [](opt::IRContext*, opt::Instruction* instruction) -> bool {
+                  return instruction->result_id() && instruction->type_id() &&
+                         instruction->opcode() != SpvOpUndef;
+                });
+
+        // Try the opcodes for which we know how to make ids at random until
+        // something works.
+        std::vector<SpvOp> candidate_opcodes = {SpvOpIAdd, SpvOpISub,
+                                                SpvOpLogicalNot, SpvOpSNegate};
+        do {
+          auto opcode =
+              GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
+          switch (opcode) {
+            case SpvOpIAdd:
+            case SpvOpISub: {
+              // Instructions of integer (scalar or vector) result type are
+              // suitable for these opcodes.
+              auto integer_instructions =
+                  GetIntegerInstructions(available_instructions);
+              if (!integer_instructions.empty()) {
+                // There is at least one such instruction, so pick one at random
+                // for the LHS of an equation.
+                auto lhs = integer_instructions.at(
+                    GetFuzzerContext()->RandomIndex(integer_instructions));
+
+                // For the RHS, we can use any instruction with an integer
+                // scalar/vector result type of the same number of components
+                // and the same bit-width for the underlying integer type.
+
+                // Work out the element count and bit-width.
+                auto lhs_type =
+                    GetIRContext()->get_type_mgr()->GetType(lhs->type_id());
+                uint32_t lhs_element_count;
+                uint32_t lhs_bit_width;
+                if (lhs_type->AsVector()) {
+                  lhs_element_count = lhs_type->AsVector()->element_count();
+                  lhs_bit_width = lhs_type->AsVector()
+                                      ->element_type()
+                                      ->AsInteger()
+                                      ->width();
+                } else {
+                  lhs_element_count = 1;
+                  lhs_bit_width = lhs_type->AsInteger()->width();
+                }
+
+                // Get all the instructions that match on element count and
+                // bit-width.
+                auto candidate_rhs_instructions = RestrictToElementBitWidth(
+                    RestrictToVectorWidth(integer_instructions,
+                                          lhs_element_count),
+                    lhs_bit_width);
+
+                // Choose a RHS instruction at random; there is guaranteed to
+                // be at least one choice as the LHS will be available.
+                auto rhs = candidate_rhs_instructions.at(
+                    GetFuzzerContext()->RandomIndex(
+                        candidate_rhs_instructions));
+
+                // Add the equation instruction.
+                ApplyTransformation(TransformationEquationInstruction(
+                    GetFuzzerContext()->GetFreshId(), opcode,
+                    {lhs->result_id(), rhs->result_id()},
+                    instruction_descriptor));
+                return;
+              }
+              break;
+            }
+            case SpvOpLogicalNot: {
+              // Choose any available instruction of boolean scalar/vector
+              // result type and equate its negation with a fresh id.
+              auto boolean_instructions =
+                  GetBooleanInstructions(available_instructions);
+              if (!boolean_instructions.empty()) {
+                ApplyTransformation(TransformationEquationInstruction(
+                    GetFuzzerContext()->GetFreshId(), opcode,
+                    {boolean_instructions
+                         .at(GetFuzzerContext()->RandomIndex(
+                             boolean_instructions))
+                         ->result_id()},
+                    instruction_descriptor));
+                return;
+              }
+              break;
+            }
+            case SpvOpSNegate: {
+              // Similar to OpLogicalNot, but for signed integer negation.
+              auto integer_instructions =
+                  GetIntegerInstructions(available_instructions);
+              if (!integer_instructions.empty()) {
+                ApplyTransformation(TransformationEquationInstruction(
+                    GetFuzzerContext()->GetFreshId(), opcode,
+                    {integer_instructions
+                         .at(GetFuzzerContext()->RandomIndex(
+                             integer_instructions))
+                         ->result_id()},
+                    instruction_descriptor));
+                return;
+              }
+              break;
+            }
+            default:
+              assert(false && "Unexpected opcode.");
+              break;
+          }
+        } while (!candidate_opcodes.empty());
+        // Reaching here means that we did not manage to apply any
+        // transformation at this point of the module.
+      });
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetIntegerInstructions(
+    const std::vector<opt::Instruction*>& instructions) const {
+  std::vector<opt::Instruction*> result;
+  for (auto& inst : instructions) {
+    auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    if (type->AsInteger() ||
+        (type->AsVector() && type->AsVector()->element_type()->AsInteger())) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetBooleanInstructions(
+    const std::vector<opt::Instruction*>& instructions) const {
+  std::vector<opt::Instruction*> result;
+  for (auto& inst : instructions) {
+    auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    if (type->AsBool() ||
+        (type->AsVector() && type->AsVector()->element_type()->AsBool())) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::RestrictToVectorWidth(
+    const std::vector<opt::Instruction*>& instructions,
+    uint32_t vector_width) const {
+  std::vector<opt::Instruction*> result;
+  for (auto& inst : instructions) {
+    auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    // Get the vector width of |inst|, which is 1 if |inst| is a scalar and is
+    // otherwise derived from its vector type.
+    uint32_t other_vector_width =
+        type->AsVector() ? type->AsVector()->element_count() : 1;
+    // Keep |inst| if the vector widths match.
+    if (vector_width == other_vector_width) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::RestrictToElementBitWidth(
+    const std::vector<opt::Instruction*>& instructions,
+    uint32_t bit_width) const {
+  std::vector<opt::Instruction*> result;
+  for (auto& inst : instructions) {
+    const opt::analysis::Type* type =
+        GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    if (type->AsVector()) {
+      type = type->AsVector()->element_type();
+    }
+    assert(type->AsInteger() &&
+           "Precondition: all input instructions must "
+           "have integer scalar or vector type.");
+    if (type->AsInteger()->width() == bit_width) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
new file mode 100644
index 0000000..84229c0
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 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_ADD_EQUATION_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that sprinkles instructions through the module that define
+// equations using various arithmetic and logical operators.
+class FuzzerPassAddEquationInstructions : public FuzzerPass {
+ public:
+  FuzzerPassAddEquationInstructions(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddEquationInstructions();
+
+  void Apply() override;
+
+ private:
+  // Yields those instructions in |instructions| that have integer scalar or
+  // vector result type.
+  std::vector<opt::Instruction*> GetIntegerInstructions(
+      const std::vector<opt::Instruction*>& instructions) const;
+
+  // Yields those instructions in |instructions| that have boolean scalar or
+  // vector result type.
+  std::vector<opt::Instruction*> GetBooleanInstructions(
+      const std::vector<opt::Instruction*>& instructions) const;
+
+  // Requires that |instructions| are scalars or vectors of some type.  Returns
+  // only those instructions whose width is |width|. If |width| is 1 this means
+  // the scalars.
+  std::vector<opt::Instruction*> RestrictToVectorWidth(
+      const std::vector<opt::Instruction*>& instructions,
+      uint32_t vector_width) const;
+
+  // Requires that |instructions| are integer scalars or vectors.  Returns only
+  // those instructions for which the bit-width of the underlying integer type
+  // is |bit_width|.
+  std::vector<opt::Instruction*> RestrictToElementBitWidth(
+      const std::vector<opt::Instruction*>& instructions,
+      uint32_t bit_width) const;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
index c89ae51..545aa16 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
@@ -32,7 +32,7 @@
 FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default;
 
 void FuzzerPassAddFunctionCalls::Apply() {
-  MaybeAddTransformationBeforeEachInstruction(
+  ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
@@ -62,8 +62,8 @@
         std::vector<opt::Function*> candidate_functions;
         for (auto& other_function : *GetIRContext()->module()) {
           if (&other_function != function &&
-              !TransformationFunctionCall::FunctionIsEntryPoint(
-                  GetIRContext(), other_function.result_id())) {
+              !fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
+                                                other_function.result_id())) {
             candidate_functions.push_back(&other_function);
           }
         }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
index 2fe1220..851787f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
@@ -29,7 +29,7 @@
 FuzzerPassAddLoads::~FuzzerPassAddLoads() = default;
 
 void FuzzerPassAddLoads::Apply() {
-  MaybeAddTransformationBeforeEachInstruction(
+  ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
index ead8c5c..82fb539 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
@@ -44,12 +44,7 @@
                       ->GetChanceOfAddingNoContractionDecoration())) {
             TransformationAddNoContractionDecoration transformation(
                 inst.result_id());
-            assert(transformation.IsApplicable(GetIRContext(),
-                                               *GetFactManager()) &&
-                   "Transformation should be applicable by construction.");
-            transformation.Apply(GetIRContext(), GetFactManager());
-            *GetTransformations()->add_transformation() =
-                transformation.ToMessage();
+            ApplyTransformation(transformation);
           }
         }
       }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
index d2c7b3d..794ddc3 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
@@ -29,7 +29,7 @@
 FuzzerPassAddStores::~FuzzerPassAddStores() = default;
 
 void FuzzerPassAddStores::Apply() {
-  MaybeAddTransformationBeforeEachInstruction(
+  ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
index 2a11988..fe229bc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
@@ -61,10 +61,7 @@
       // Create and add a transformation.
       TransformationSetFunctionControl transformation(
           function.DefInst().result_id(), new_function_control_mask);
-      assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-             "Transformation should be applicable by construction.");
-      transformation.Apply(GetIRContext(), GetFactManager());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
+      ApplyTransformation(transformation);
     }
   }
 }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
index ac2408a..c9843d0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
@@ -107,11 +107,7 @@
         // sequence.
         TransformationSetLoopControl transformation(block.id(), new_mask,
                                                     peel_count, partial_count);
-        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-               "Transformation should be applicable by construction.");
-        transformation.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() =
-            transformation.ToMessage();
+        ApplyTransformation(transformation);
       }
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
index a9d4b32..2d3d676 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
@@ -97,12 +97,7 @@
 
           TransformationSetMemoryOperandsMask transformation(
               MakeInstructionDescriptor(block, inst_it), new_mask, mask_index);
-          assert(
-              transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-              "Transformation should be applicable by construction.");
-          transformation.Apply(GetIRContext(), GetFactManager());
-          *GetTransformations()->add_transformation() =
-              transformation.ToMessage();
+          ApplyTransformation(transformation);
         }
       }
     }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
index 22654f2..397dfed 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
@@ -62,11 +62,7 @@
         // sequence.
         TransformationSetSelectionControl transformation(
             block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]);
-        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-               "Transformation should be applicable by construction.");
-        transformation.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() =
-            transformation.ToMessage();
+        ApplyTransformation(transformation);
       }
     }
   }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index e932017..5711f35 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -81,19 +81,19 @@
         synonyms_to_try.push_back(data_descriptor);
       }
       while (!synonyms_to_try.empty()) {
-        auto synonym_index = GetFuzzerContext()->RandomIndex(synonyms_to_try);
-        auto synonym_to_try = synonyms_to_try[synonym_index];
-        synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index);
+        auto synonym_to_try =
+            GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try);
 
+        // If the synonym's |index_size| is zero, the synonym represents an id.
+        // Otherwise it represents some element of a composite structure, in
+        // which case we need to be able to add an extract instruction to get
+        // that element out.
         if (synonym_to_try->index_size() > 0 &&
-            use_inst->opcode() == SpvOpPhi) {
-          // We are trying to replace an operand to an OpPhi.  This means
-          // we cannot use a composite synonym, because that requires
-          // extracting a component from a composite and we cannot insert
-          // an extract instruction before an OpPhi.
-          //
-          // TODO(afd): We could consider inserting the extract instruction
-          //  into the relevant parent block of the OpPhi.
+            !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
+                                                          use_inst) &&
+            use_inst->opcode() != SpvOpPhi) {
+          // We cannot insert an extract before this instruction, so this
+          // synonym is no good.
           continue;
         }
 
@@ -103,40 +103,43 @@
           continue;
         }
 
-        // We either replace the use with an id known to be synonymous, or
-        // an id that will hold the result of extracting a synonym from a
-        // composite.
+        // We either replace the use with an id known to be synonymous (when
+        // the synonym's |index_size| is 0), or an id that will hold the result
+        // of extracting a synonym from a composite (when the synonym's
+        // |index_size| is > 0).
         uint32_t id_with_which_to_replace_use;
         if (synonym_to_try->index_size() == 0) {
           id_with_which_to_replace_use = synonym_to_try->object();
         } else {
           id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId();
-          protobufs::InstructionDescriptor instruction_to_insert_before =
-              MakeInstructionDescriptor(GetIRContext(), use_inst);
-          TransformationCompositeExtract composite_extract_transformation(
-              instruction_to_insert_before, id_with_which_to_replace_use,
-              synonym_to_try->object(),
-              fuzzerutil::RepeatedFieldToVector(synonym_to_try->index()));
-          assert(composite_extract_transformation.IsApplicable(
-                     GetIRContext(), *GetFactManager()) &&
-                 "Transformation should be applicable by construction.");
-          composite_extract_transformation.Apply(GetIRContext(),
-                                                 GetFactManager());
-          *GetTransformations()->add_transformation() =
-              composite_extract_transformation.ToMessage();
+          opt::Instruction* instruction_to_insert_before = nullptr;
+
+          if (use_inst->opcode() != SpvOpPhi) {
+            instruction_to_insert_before = use_inst;
+          } else {
+            auto parent_block_id =
+                use_inst->GetSingleWordInOperand(use_in_operand_index + 1);
+            auto parent_block_instruction =
+                GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id);
+            auto parent_block =
+                GetIRContext()->get_instr_block(parent_block_instruction);
+
+            instruction_to_insert_before = parent_block->GetMergeInst()
+                                               ? parent_block->GetMergeInst()
+                                               : parent_block->terminator();
+          }
+
+          ApplyTransformation(TransformationCompositeExtract(
+              MakeInstructionDescriptor(GetIRContext(),
+                                        instruction_to_insert_before),
+              id_with_which_to_replace_use, synonym_to_try->object(),
+              fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())));
         }
 
-        TransformationReplaceIdWithSynonym replace_id_transformation(
+        ApplyTransformation(TransformationReplaceIdWithSynonym(
             MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
                                        use_in_operand_index),
-            id_with_which_to_replace_use);
-
-        // The transformation should be applicable by construction.
-        assert(replace_id_transformation.IsApplicable(GetIRContext(),
-                                                      *GetFactManager()));
-        replace_id_transformation.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() =
-            replace_id_transformation.ToMessage();
+            id_with_which_to_replace_use));
         break;
       }
     }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
index e160302..330b9cf 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -42,7 +42,7 @@
     }
   }
 
-  MaybeAddTransformationBeforeEachInstruction(
+  ForEachInstructionWithInstructionDescriptor(
       [this, &composite_type_ids](
           opt::Function* function, opt::BasicBlock* block,
           opt::BasicBlock::iterator inst_it,
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
index 0fbe5cb..588cfb6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
@@ -29,7 +29,7 @@
 FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
 
 void FuzzerPassCopyObjects::Apply() {
-  MaybeAddTransformationBeforeEachInstruction(
+  ForEachInstructionWithInstructionDescriptor(
       [this](opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
new file mode 100644
index 0000000..2c49860
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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 <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_permute_function_parameters.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() =
+    default;
+
+void FuzzerPassPermuteFunctionParameters::Apply() {
+  for (const auto& function : *GetIRContext()->module()) {
+    uint32_t function_id = function.result_id();
+
+    // Skip the function if it is an entry point
+    if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), function_id)) {
+      continue;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfPermutingParameters())) {
+      continue;
+    }
+
+    // Compute permutation for parameters
+    auto* function_type =
+        fuzzerutil::GetFunctionType(GetIRContext(), &function);
+    assert(function_type && "Function type is null");
+
+    // Don't take return type into account
+    uint32_t arg_size = function_type->NumInOperands() - 1;
+
+    // Create a vector, fill it with [0, n-1] values and shuffle it
+    std::vector<uint32_t> permutation(arg_size);
+    std::iota(permutation.begin(), permutation.end(), 0);
+    GetFuzzerContext()->Shuffle(&permutation);
+
+    // Create a new OpFunctionType instruction with permuted arguments
+    // if needed
+    auto result_type_id = function_type->GetSingleWordInOperand(0);
+    std::vector<uint32_t> argument_ids;
+
+    for (auto index : permutation) {
+      // +1 to take function's return type into account
+      argument_ids.push_back(function_type->GetSingleWordInOperand(index + 1));
+    }
+
+    // Apply our transformation
+    ApplyTransformation(TransformationPermuteFunctionParameters(
+        function_id, FindOrCreateFunctionType(result_type_id, argument_ids),
+        permutation));
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
new file mode 100644
index 0000000..bc79804
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_PERMUTE_FUNCTION_PARAMETERS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that, given a non-entry-point function taking n parameters
+// and a permutation of the set [0, n - 1]:
+//   1. Introduces a new function type that is the same as the original
+//      function's type but with the order of arguments permuted
+//      (only add this if it doesn't already exist)
+//   2. Changes the type of the function to this type
+//   3. Adjusts all calls to the function so that their arguments are permuted
+class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
+ public:
+  FuzzerPassPermuteFunctionParameters(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassPermuteFunctionParameters();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
new file mode 100644
index 0000000..4df97c9
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_swap_commutable_operands.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_swap_commutable_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default;
+
+void FuzzerPassSwapCommutableOperands::Apply() {
+  auto context = GetIRContext();
+  // Iterates over the module's instructions and checks whether it is
+  // commutative. In this case, the transformation is probabilistically applied.
+  context->module()->ForEachInst(
+      [this, context](opt::Instruction* instruction) {
+        if (spvOpcodeIsCommutativeBinaryOperator(instruction->opcode()) &&
+            GetFuzzerContext()->ChooseEven()) {
+          auto instructionDescriptor =
+              MakeInstructionDescriptor(context, instruction);
+          auto transformation =
+              TransformationSwapCommutableOperands(instructionDescriptor);
+          ApplyTransformation(transformation);
+        }
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h
new file mode 100644
index 0000000..b0206de
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_SWAP_COMMUTABLE_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for all commutative instructions in the module,
+// probabilistically choosing which of these instructions will have its input
+// operands swapped.
+class FuzzerPassSwapCommutableOperands : public FuzzerPass {
+ public:
+  FuzzerPassSwapCommutableOperands(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassSwapCommutableOperands();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
new file mode 100644
index 0000000..9fb175b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
@@ -0,0 +1,54 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_toggle_access_chain_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassToggleAccessChainInstruction::
+    ~FuzzerPassToggleAccessChainInstruction() = default;
+
+void FuzzerPassToggleAccessChainInstruction::Apply() {
+  auto context = GetIRContext();
+  // Iterates over the module's instructions and checks whether it is
+  // OpAccessChain or OpInBoundsAccessChain. In this case, the transformation is
+  // probabilistically applied.
+  context->module()->ForEachInst([this,
+                                  context](opt::Instruction* instruction) {
+    SpvOp opcode = instruction->opcode();
+    if ((opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) &&
+        GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfTogglingAccessChainInstruction())) {
+      auto instructionDescriptor =
+          MakeInstructionDescriptor(context, instruction);
+      auto transformation =
+          TransformationToggleAccessChainInstruction(instructionDescriptor);
+      ApplyTransformation(transformation);
+    }
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
new file mode 100644
index 0000000..ec8c3f7
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+#define SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for all access chain instructions in the module,
+// probabilistically choosing which of these instructions will be toggled.
+class FuzzerPassToggleAccessChainInstruction : public FuzzerPass {
+ public:
+  FuzzerPassToggleAccessChainInstruction(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassToggleAccessChainInstruction();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
index 26961c8..4bfa195 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
@@ -395,6 +395,12 @@
   return 0;
 }
 
+opt::Instruction* GetFunctionType(opt::IRContext* context,
+                                  const opt::Function* function) {
+  uint32_t type_id = function->DefInst().GetSingleWordInOperand(1);
+  return context->get_def_use_mgr()->GetDef(type_id);
+}
+
 opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
   for (auto& function : *ir_context->module()) {
     if (function.result_id() == function_id) {
@@ -404,6 +410,15 @@
   return nullptr;
 }
 
+bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) {
+  for (auto& entry_point : context->module()->entry_points()) {
+    if (entry_point.GetSingleWordInOperand(1) == function_id) {
+      return true;
+    }
+  }
+  return false;
+}
+
 bool IdIsAvailableAtUse(opt::IRContext* context,
                         opt::Instruction* use_instruction,
                         uint32_t use_input_operand_index, uint32_t id) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
index daa836c..7be0d59 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
@@ -152,10 +152,18 @@
 uint32_t FindFunctionType(opt::IRContext* ir_context,
                           const std::vector<uint32_t>& type_ids);
 
+// Returns a type instruction (OpTypeFunction) for |function|.
+// Returns |nullptr| if type is not found.
+opt::Instruction* GetFunctionType(opt::IRContext* context,
+                                  const opt::Function* function);
+
 // Returns the function with result id |function_id|, or |nullptr| if no such
 // function exists.
 opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
 
+// Returns |true| if one of entry points has function id |function_id|.
+bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id);
+
 // Checks whether |id| is available (according to dominance rules) at the use
 // point defined by input operand |use_input_operand_index| of
 // |use_instruction|.
diff --git a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
index 9773b60..b816e3b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -168,12 +168,22 @@
     FactDataSynonym data_synonym_fact = 2;
     FactBlockIsDead block_is_dead_fact = 3;
     FactFunctionIsLivesafe function_is_livesafe_fact = 4;
-    FactPointeeValueIsIrrelevant pointee_value_is_irrelevant = 5;
+    FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
+    FactIdEquation id_equation_fact = 6;
   }
 }
 
 // Keep fact message types in alphabetical order:
 
+message FactBlockIsDead {
+
+  // Records the fact that a block is guaranteed to be dynamically unreachable.
+  // This is useful because it informs the fuzzer that rather arbitrary changes
+  // can be made to this block.
+
+  uint32 block_id = 1;
+}
+
 message FactConstantUniform {
 
   // Records the fact that a uniform buffer element is guaranteed to be equal
@@ -203,15 +213,6 @@
 
 }
 
-message FactBlockIsDead {
-
-  // Records the fact that a block is guaranteed to be dynamically unreachable.
-  // This is useful because it informs the fuzzer that rather arbitrary changes
-  // can be made to this block.
-
-  uint32 block_id = 1;
-}
-
 message FactFunctionIsLivesafe {
 
   // Records the fact that a function is guaranteed to be "livesafe", meaning
@@ -223,6 +224,31 @@
   uint32 function_id = 1;
 }
 
+message FactIdEquation {
+
+  // Records the fact that the equation:
+  //
+  // lhs_id = opcode rhs_id[0] rhs_id[1] ... rhs_id[N-1]
+  //
+  // holds; e.g. that the equation:
+  //
+  // %12 = OpIAdd %13 %14
+  //
+  // holds in the case where lhs_id is 12, rhs_id is [13, 14], and the opcode is
+  // OpIAdd.
+
+  // The left-hand-side of the equation.
+  uint32 lhs_id = 1;
+
+  // A SPIR-V opcode, from a restricted set of instructions for which equation
+  // facts make sense.
+  uint32 opcode = 2;
+
+  // The operands to the right-hand-side of the equation.
+  repeated uint32 rhs_id = 3;
+
+}
+
 message FactPointeeValueIsIrrelevant {
 
   // Records the fact that value of the data pointed to by a pointer id does
@@ -342,6 +368,10 @@
     TransformationStore store = 37;
     TransformationFunctionCall function_call = 38;
     TransformationAccessChain access_chain = 39;
+    TransformationEquationInstruction equation_instruction = 40;
+    TransformationSwapCommutableOperands swap_commutable_operands = 41;
+    TransformationPermuteFunctionParameters permute_function_parameters = 42;
+    TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43;
     // Add additional option using the next available number.
   }
 }
@@ -752,6 +782,28 @@
 
 }
 
+message TransformationEquationInstruction {
+
+  // A transformation that adds an instruction to the module that defines an
+  // equation between its result id and input operand ids, such that the
+  // equation is guaranteed to hold at any program point where all ids involved
+  // are available (i.e. at any program point dominated by the instruction).
+
+  // The result id of the new instruction
+  uint32 fresh_id = 1;
+
+  // The instruction's opcode
+  uint32 opcode = 2;
+
+  // The input operands to the instruction
+  repeated uint32 in_operand_id = 3;
+
+  // A descriptor for an instruction in a block before which the new
+  // instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 4;
+
+}
+
 message TransformationFunctionCall {
 
   // A transformation that introduces an OpFunctionCall instruction.  The call
@@ -857,6 +909,36 @@
 
 }
 
+message TransformationPermuteFunctionParameters {
+
+  // A transformation that, given a non-entry-point function taking n
+  // parameters and a permutation of the set [0, n-1]:
+  //   - Introduces a new function type that is the same as the original
+  //     function's type but with the order of arguments permuted
+  //     (only if it doesn't already exist)
+  //   - Changes the type of the function to this type
+  //   - Adjusts all calls to the function so that their arguments are permuted
+
+  // Function, whose parameters will be permuted
+  uint32 function_id = 1;
+
+  // |new_type_id| is a result id of a valid OpTypeFunction instruction.
+  // New type is valid if:
+  //   - it has the same number of operands as the old one
+  //   - function's result type is the same as the old one
+  //   - function's arguments are permuted according to |permutation| vector
+  uint32 new_type_id = 2;
+
+  // An array of size |n|, where |n| is a number of arguments to a function
+  // with |function_id|. For each i: 0 <= permutation[i] < n.
+  //
+  // i-th element of this array contains a position for an i-th
+  // function's argument (i.e. i-th argument will be permutation[i]-th
+  // after running this transformation)
+  repeated uint32 permutation = 3;
+
+}
+
 message TransformationReplaceBooleanConstantWithConstantBinary {
 
   // A transformation to capture replacing a use of a boolean constant with
@@ -1019,6 +1101,24 @@
 
 }
 
+message TransformationSwapCommutableOperands {
+
+  // A transformation that swaps the operands of a commutative instruction.
+
+  // A descriptor for a commutative instruction
+  InstructionDescriptor instruction_descriptor = 1;
+
+}
+
+message TransformationToggleAccessChainInstruction {
+
+  // A transformation that toggles an access chain instruction.
+
+  // A descriptor for an access chain instruction
+  InstructionDescriptor instruction_descriptor = 1;
+
+}
+
 message TransformationVectorShuffle {
 
   // A transformation that adds a vector shuffle instruction.
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
index 52fcfd7..f18c86b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
@@ -41,11 +41,13 @@
 #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_equation_instruction.h"
 #include "source/fuzz/transformation_function_call.h"
 #include "source/fuzz/transformation_load.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_permute_function_parameters.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"
@@ -55,6 +57,8 @@
 #include "source/fuzz/transformation_set_selection_control.h"
 #include "source/fuzz/transformation_split_block.h"
 #include "source/fuzz/transformation_store.h"
+#include "source/fuzz/transformation_swap_commutable_operands.h"
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
 #include "source/fuzz/transformation_vector_shuffle.h"
 #include "source/util/make_unique.h"
 
@@ -128,6 +132,9 @@
           message.composite_extract());
     case protobufs::Transformation::TransformationCase::kCopyObject:
       return MakeUnique<TransformationCopyObject>(message.copy_object());
+    case protobufs::Transformation::TransformationCase::kEquationInstruction:
+      return MakeUnique<TransformationEquationInstruction>(
+          message.equation_instruction());
     case protobufs::Transformation::TransformationCase::kFunctionCall:
       return MakeUnique<TransformationFunctionCall>(message.function_call());
     case protobufs::Transformation::TransformationCase::kLoad:
@@ -140,6 +147,10 @@
       return MakeUnique<TransformationOutlineFunction>(
           message.outline_function());
     case protobufs::Transformation::TransformationCase::
+        kPermuteFunctionParameters:
+      return MakeUnique<TransformationPermuteFunctionParameters>(
+          message.permute_function_parameters());
+    case protobufs::Transformation::TransformationCase::
         kReplaceBooleanConstantWithConstantBinary:
       return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
           message.replace_boolean_constant_with_constant_binary());
@@ -166,6 +177,13 @@
       return MakeUnique<TransformationSplitBlock>(message.split_block());
     case protobufs::Transformation::TransformationCase::kStore:
       return MakeUnique<TransformationStore>(message.store());
+    case protobufs::Transformation::TransformationCase::kSwapCommutableOperands:
+      return MakeUnique<TransformationSwapCommutableOperands>(
+          message.swap_commutable_operands());
+    case protobufs::Transformation::TransformationCase::
+        kToggleAccessChainInstruction:
+      return MakeUnique<TransformationToggleAccessChainInstruction>(
+          message.toggle_access_chain_instruction());
     case protobufs::Transformation::TransformationCase::kVectorShuffle:
       return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
     case protobufs::Transformation::TRANSFORMATION_NOT_SET:
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
index ffa182e..3a4875e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
@@ -112,7 +112,7 @@
   // 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
+  // being placed 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);
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
index 2b59661..3880963 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
@@ -37,7 +37,7 @@
   // - |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
+  //   function type with the signature provided by the given return and
   //   argument types
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
new file mode 100644
index 0000000..21b67f6
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
@@ -0,0 +1,186 @@
+// Copyright (c) 2020 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_equation_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationEquationInstruction::TransformationEquationInstruction(
+    const spvtools::fuzz::protobufs::TransformationEquationInstruction& message)
+    : message_(message) {}
+
+TransformationEquationInstruction::TransformationEquationInstruction(
+    uint32_t fresh_id, SpvOp opcode, const std::vector<uint32_t>& in_operand_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_opcode(opcode);
+  for (auto id : in_operand_id) {
+    message_.add_in_operand_id(id);
+  }
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationEquationInstruction::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 instruction to insert before must exist.
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!insert_before) {
+    return false;
+  }
+  // The input ids must all exist, not be OpUndef, and be available before this
+  // instruction.
+  for (auto id : message_.in_operand_id()) {
+    auto inst = context->get_def_use_mgr()->GetDef(id);
+    if (!inst) {
+      return false;
+    }
+    if (inst->opcode() == SpvOpUndef) {
+      return false;
+    }
+    if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    id)) {
+      return false;
+    }
+  }
+
+  return MaybeGetResultType(context) != 0;
+}
+
+void TransformationEquationInstruction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+
+  opt::Instruction::OperandList in_operands;
+  std::vector<uint32_t> rhs_id;
+  for (auto id : message_.in_operand_id()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+    rhs_id.push_back(id);
+  }
+
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, static_cast<SpvOp>(message_.opcode()),
+          MaybeGetResultType(context), message_.fresh_id(), in_operands));
+
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  fact_manager->AddFactIdEquation(message_.fresh_id(),
+                                  static_cast<SpvOp>(message_.opcode()), rhs_id,
+                                  context);
+}
+
+protobufs::Transformation TransformationEquationInstruction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_equation_instruction() = message_;
+  return result;
+}
+
+uint32_t TransformationEquationInstruction::MaybeGetResultType(
+    opt::IRContext* context) const {
+  switch (static_cast<SpvOp>(message_.opcode())) {
+    case SpvOpIAdd:
+    case SpvOpISub: {
+      if (message_.in_operand_id().size() != 2) {
+        return 0;
+      }
+      uint32_t first_operand_width = 0;
+      uint32_t first_operand_type_id = 0;
+      for (uint32_t index = 0; index < 2; index++) {
+        auto operand_inst =
+            context->get_def_use_mgr()->GetDef(message_.in_operand_id(index));
+        if (!operand_inst || !operand_inst->type_id()) {
+          return 0;
+        }
+        auto operand_type =
+            context->get_type_mgr()->GetType(operand_inst->type_id());
+        if (!(operand_type->AsInteger() ||
+              (operand_type->AsVector() &&
+               operand_type->AsVector()->element_type()->AsInteger()))) {
+          return 0;
+        }
+        uint32_t operand_width =
+            operand_type->AsInteger()
+                ? 1
+                : operand_type->AsVector()->element_count();
+        if (index == 0) {
+          first_operand_width = operand_width;
+          first_operand_type_id = operand_inst->type_id();
+        } else {
+          assert(first_operand_width != 0 &&
+                 "The first operand should have been processed.");
+          if (operand_width != first_operand_width) {
+            return 0;
+          }
+        }
+      }
+      assert(first_operand_type_id != 0 &&
+             "A type must have been found for the first operand.");
+      return first_operand_type_id;
+    }
+    case SpvOpLogicalNot: {
+      if (message_.in_operand_id().size() != 1) {
+        return 0;
+      }
+      auto operand_inst =
+          context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+      if (!operand_inst || !operand_inst->type_id()) {
+        return 0;
+      }
+      auto operand_type =
+          context->get_type_mgr()->GetType(operand_inst->type_id());
+      if (!(operand_type->AsBool() ||
+            (operand_type->AsVector() &&
+             operand_type->AsVector()->element_type()->AsBool()))) {
+        return 0;
+      }
+      return operand_inst->type_id();
+    }
+    case SpvOpSNegate: {
+      if (message_.in_operand_id().size() != 1) {
+        return 0;
+      }
+      auto operand_inst =
+          context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+      if (!operand_inst || !operand_inst->type_id()) {
+        return 0;
+      }
+      auto operand_type =
+          context->get_type_mgr()->GetType(operand_inst->type_id());
+      if (!(operand_type->AsInteger() ||
+            (operand_type->AsVector() &&
+             operand_type->AsVector()->element_type()->AsInteger()))) {
+        return 0;
+      }
+      return operand_inst->type_id();
+    }
+
+    default:
+      assert(false && "Inappropriate opcode for equation instruction.");
+      return 0;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
new file mode 100644
index 0000000..2456ba5
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2020 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_EQUATION_INSTRUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_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 TransformationEquationInstruction : public Transformation {
+ public:
+  explicit TransformationEquationInstruction(
+      const protobufs::TransformationEquationInstruction& message);
+
+  TransformationEquationInstruction(
+      uint32_t fresh_id, SpvOp opcode,
+      const std::vector<uint32_t>& in_operand_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |message_.fresh_id| must be fresh.
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which an equation instruction can legitimately be inserted.
+  // - Each id in |message_.in_operand_id| must exist, not be an OpUndef, and
+  //   be available before |message_.instruction_to_insert_before|.
+  // - |message_.opcode| must be an opcode for which we know how to handle
+  //   equations, the types of the ids in |message_.in_operand_id| must be
+  //   suitable for use with this opcode, and the module must contain an
+  //   appropriate result type id.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction to the module, right before
+  // |message_.instruction_to_insert_before|, of the form:
+  //
+  // |message_.fresh_id| = |message_.opcode| %type |message_.in_operand_ids|
+  //
+  // where %type is a type id that already exists in the module and that is
+  // compatible with the opcode and input operands.
+  //
+  // The fact manager is also updated to inform it of this equation fact.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  // A helper that, in one fell swoop, checks that |message_.opcode| and the ids
+  // in |message_.in_operand_id| are compatible, and that the module contains
+  // an appropriate result type id.  If all is well, the result type id is
+  // returned.  Otherwise, 0 is returned.
+  uint32_t MaybeGetResultType(opt::IRContext* context) const;
+
+  protobufs::TransformationEquationInstruction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
index 6988664..cea8537 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
@@ -53,7 +53,7 @@
   }
 
   // The function must not be an entry point
-  if (FunctionIsEntryPoint(context, message_.callee_id())) {
+  if (fuzzerutil::FunctionIsEntryPoint(context, message_.callee_id())) {
     return false;
   }
 
@@ -181,15 +181,5 @@
   return result;
 }
 
-bool TransformationFunctionCall::FunctionIsEntryPoint(opt::IRContext* context,
-                                                      uint32_t function_id) {
-  for (auto& entry_point : context->module()->entry_points()) {
-    if (entry_point.GetSingleWordInOperand(1) == function_id) {
-      return true;
-    }
-  }
-  return false;
-}
-
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
index e977e1d..a9ae5be 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
@@ -55,10 +55,6 @@
 
   protobufs::Transformation ToMessage() const override;
 
-  // Helper to determine whether |function_id| is targeted by OpEntryPoint.
-  static bool FunctionIsEntryPoint(opt::IRContext* context,
-                                   uint32_t function_id);
-
  private:
   protobufs::TransformationFunctionCall message_;
 };
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
new file mode 100644
index 0000000..2141533
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
@@ -0,0 +1,184 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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 <unordered_set>
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_permute_function_parameters.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationPermuteFunctionParameters::
+    TransformationPermuteFunctionParameters(
+        const spvtools::fuzz::protobufs::
+            TransformationPermuteFunctionParameters& message)
+    : message_(message) {}
+
+TransformationPermuteFunctionParameters::
+    TransformationPermuteFunctionParameters(
+        uint32_t function_id, uint32_t new_type_id,
+        const std::vector<uint32_t>& permutation) {
+  message_.set_function_id(function_id);
+  message_.set_new_type_id(new_type_id);
+
+  for (auto index : permutation) {
+    message_.add_permutation(index);
+  }
+}
+
+bool TransformationPermuteFunctionParameters::IsApplicable(
+    opt::IRContext* context, const FactManager& /*unused*/) const {
+  // Check that function exists
+  const auto* function =
+      fuzzerutil::FindFunction(context, message_.function_id());
+  if (!function || function->DefInst().opcode() != SpvOpFunction ||
+      fuzzerutil::FunctionIsEntryPoint(context, function->result_id())) {
+    return false;
+  }
+
+  // Check that permutation has valid indices
+  const auto* function_type = fuzzerutil::GetFunctionType(context, function);
+  assert(function_type && "Function type is null");
+
+  const auto& permutation = message_.permutation();
+
+  // Don't take return type into account
+  auto arg_size = function_type->NumInOperands() - 1;
+
+  // |permutation| vector should be equal to the number of arguments
+  if (static_cast<uint32_t>(permutation.size()) != arg_size) {
+    return false;
+  }
+
+  // Check that all indices are valid
+  // and unique integers from the [0, n-1] set
+  std::unordered_set<uint32_t> unique_indices;
+  for (auto index : permutation) {
+    // We don't compare |index| with 0 since it's an unsigned integer
+    if (index >= arg_size) {
+      return false;
+    }
+
+    unique_indices.insert(index);
+  }
+
+  // Check that permutation doesn't have duplicated values
+  assert(unique_indices.size() == arg_size && "Permutation has duplicates");
+
+  // Check that new function's type is valid:
+  //   - Has the same number of operands
+  //   - Has the same result type as the old one
+  //   - Order of arguments is permuted
+  auto new_type_id = message_.new_type_id();
+  const auto* new_type = context->get_def_use_mgr()->GetDef(new_type_id);
+
+  if (!new_type || new_type->opcode() != SpvOpTypeFunction ||
+      new_type->NumInOperands() != function_type->NumInOperands()) {
+    return false;
+  }
+
+  // Check that both instructions have the same result type
+  if (new_type->GetSingleWordInOperand(0) !=
+      function_type->GetSingleWordInOperand(0)) {
+    return false;
+  }
+
+  // Check that new function type has its arguments permuted
+  for (int i = 0, n = static_cast<int>(permutation.size()); i < n; ++i) {
+    // +1 to take return type into account
+    if (new_type->GetSingleWordInOperand(i + 1) !=
+        function_type->GetSingleWordInOperand(permutation[i] + 1)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationPermuteFunctionParameters::Apply(
+    opt::IRContext* context, FactManager* /*unused*/) const {
+  // Retrieve all data from the message
+  uint32_t function_id = message_.function_id();
+  uint32_t new_type_id = message_.new_type_id();
+  const auto& permutation = message_.permutation();
+
+  // Find the function that will be transformed
+  auto* function = fuzzerutil::FindFunction(context, function_id);
+  assert(function && "Can't find the function");
+
+  // Change function's type
+  function->DefInst().SetInOperand(1, {new_type_id});
+
+  // Adjust OpFunctionParameter instructions
+
+  // Collect ids and types from OpFunctionParameter instructions
+  std::vector<uint32_t> param_id, param_type;
+  function->ForEachParam(
+      [&param_id, &param_type](const opt::Instruction* param) {
+        param_id.push_back(param->result_id());
+        param_type.push_back(param->type_id());
+      });
+
+  // Permute parameters' ids and types
+  std::vector<uint32_t> permuted_param_id, permuted_param_type;
+  for (auto index : permutation) {
+    permuted_param_id.push_back(param_id[index]);
+    permuted_param_type.push_back(param_type[index]);
+  }
+
+  // Set OpFunctionParameter instructions to point to new parameters
+  size_t i = 0;
+  function->ForEachParam(
+      [&i, &permuted_param_id, &permuted_param_type](opt::Instruction* param) {
+        param->SetResultType(permuted_param_type[i]);
+        param->SetResultId(permuted_param_id[i]);
+        ++i;
+      });
+
+  // Fix all OpFunctionCall instructions
+  context->get_def_use_mgr()->ForEachUser(
+      &function->DefInst(),
+      [function_id, &permutation](opt::Instruction* call) {
+        if (call->opcode() != SpvOpFunctionCall ||
+            call->GetSingleWordInOperand(0) != function_id) {
+          return;
+        }
+
+        opt::Instruction::OperandList call_operands = {
+            call->GetInOperand(0)  // Function id
+        };
+
+        for (auto index : permutation) {
+          // Take function id into account
+          call_operands.push_back(call->GetInOperand(index + 1));
+        }
+
+        call->SetInOperands(std::move(call_operands));
+      });
+
+  // Make sure our changes are analyzed
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_permute_function_parameters() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
new file mode 100644
index 0000000..c67a735
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_PERMUTE_FUNCTION_PARAMETERS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_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 TransformationPermuteFunctionParameters : public Transformation {
+ public:
+  explicit TransformationPermuteFunctionParameters(
+      const protobufs::TransformationPermuteFunctionParameters& message);
+
+  TransformationPermuteFunctionParameters(
+      uint32_t function_id, uint32_t new_type_id,
+      const std::vector<uint32_t>& permutation);
+
+  // - |function_id| is a valid non-entry-point OpFunction instruction
+  // - |new_type_id| is a result id of a valid OpTypeFunction instruction.
+  //   New type is valid if:
+  //     - it has the same number of operands as the old one
+  //     - function's result type is the same as the old one
+  //     - function's arguments are permuted according to |permutation| vector
+  // - |permutation| is a set of [0..(n - 1)], where n is a number of arguments
+  //   to the function
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // - OpFunction instruction with |result_id == function_id| is changed.
+  //   Its arguments are permuted according to the |permutation| vector
+  // - Changed function gets a new type specified by |type_id|
+  // - Calls to the function are adjusted accordingly
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationPermuteFunctionParameters message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp
new file mode 100644
index 0000000..49d9de8
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_swap_commutable_operands.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapCommutableOperands::TransformationSwapCommutableOperands(
+    const spvtools::fuzz::protobufs::TransformationSwapCommutableOperands&
+        message)
+    : message_(message) {}
+
+TransformationSwapCommutableOperands::TransformationSwapCommutableOperands(
+    const protobufs::InstructionDescriptor& instruction_descriptor) {
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationSwapCommutableOperands::IsApplicable(
+    opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/
+    ) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), context);
+  if (instruction == nullptr) return false;
+
+  SpvOp opcode = static_cast<SpvOp>(
+      message_.instruction_descriptor().target_instruction_opcode());
+  assert(instruction->opcode() == opcode &&
+         "The located instruction must have the same opcode as in the "
+         "descriptor.");
+  return spvOpcodeIsCommutativeBinaryOperator(opcode);
+}
+
+void TransformationSwapCommutableOperands::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/
+    ) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), context);
+  // By design, the instructions defined to be commutative have exactly two
+  // input parameters.
+  std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1));
+}
+
+protobufs::Transformation TransformationSwapCommutableOperands::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_swap_commutable_operands() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h
new file mode 100644
index 0000000..061e92d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_SWAP_COMMUTABLE_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_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 TransformationSwapCommutableOperands : public Transformation {
+ public:
+  explicit TransformationSwapCommutableOperands(
+      const protobufs::TransformationSwapCommutableOperands& message);
+
+  TransformationSwapCommutableOperands(
+      const protobufs::InstructionDescriptor& instruction_descriptor);
+
+  // - |message_.instruction_descriptor| must identify an existing
+  // commutative instruction
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Swaps the commutable operands.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationSwapCommutableOperands message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp
new file mode 100644
index 0000000..ace331a
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_toggle_access_chain_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationToggleAccessChainInstruction::
+    TransformationToggleAccessChainInstruction(
+        const spvtools::fuzz::protobufs::
+            TransformationToggleAccessChainInstruction& message)
+    : message_(message) {}
+
+TransformationToggleAccessChainInstruction::
+    TransformationToggleAccessChainInstruction(
+        const protobufs::InstructionDescriptor& instruction_descriptor) {
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationToggleAccessChainInstruction::IsApplicable(
+    opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/
+    ) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), context);
+  if (instruction == nullptr) {
+    return false;
+  }
+
+  SpvOp opcode = static_cast<SpvOp>(
+      message_.instruction_descriptor().target_instruction_opcode());
+
+  assert(instruction->opcode() == opcode &&
+         "The located instruction must have the same opcode as in the "
+         "descriptor.");
+
+  if (opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) {
+    return true;
+  }
+
+  return false;
+}
+
+void TransformationToggleAccessChainInstruction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/
+    ) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), context);
+  SpvOp opcode = instruction->opcode();
+
+  if (opcode == SpvOpAccessChain) {
+    instruction->SetOpcode(SpvOpInBoundsAccessChain);
+  } else {
+    assert(opcode == SpvOpInBoundsAccessChain &&
+           "The located instruction must be an OpInBoundsAccessChain "
+           "instruction.");
+    instruction->SetOpcode(SpvOpAccessChain);
+  }
+}
+
+protobufs::Transformation
+TransformationToggleAccessChainInstruction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_toggle_access_chain_instruction() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h
new file mode 100644
index 0000000..125e1ab
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_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 TransformationToggleAccessChainInstruction : public Transformation {
+ public:
+  explicit TransformationToggleAccessChainInstruction(
+      const protobufs::TransformationToggleAccessChainInstruction& message);
+
+  TransformationToggleAccessChainInstruction(
+      const protobufs::InstructionDescriptor& instruction_descriptor);
+
+  // - |message_.instruction_descriptor| must identify an existing
+  //   access chain instruction
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Toggles the access chain instruction.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationToggleAccessChainInstruction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
diff --git a/third_party/SPIRV-Tools/source/opcode.cpp b/third_party/SPIRV-Tools/source/opcode.cpp
index a837b95..80fe3b3 100644
--- a/third_party/SPIRV-Tools/source/opcode.cpp
+++ b/third_party/SPIRV-Tools/source/opcode.cpp
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -333,6 +335,9 @@
     case SpvOpTypeNamedBarrier:
     case SpvOpTypeAccelerationStructureNV:
     case SpvOpTypeCooperativeMatrixNV:
+    // case SpvOpTypeAccelerationStructureKHR: covered by
+    // SpvOpTypeAccelerationStructureNV
+    case SpvOpTypeRayQueryProvisionalKHR:
       return true;
     default:
       // In particular, OpTypeForwardPointer does not generate a type,
@@ -612,6 +617,39 @@
   }
 }
 
+bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpPtrEqual:
+    case SpvOpPtrNotEqual:
+    case SpvOpIAdd:
+    case SpvOpFAdd:
+    case SpvOpIMul:
+    case SpvOpFMul:
+    case SpvOpDot:
+    case SpvOpIAddCarry:
+    case SpvOpUMulExtended:
+    case SpvOpSMulExtended:
+    case SpvOpBitwiseOr:
+    case SpvOpBitwiseXor:
+    case SpvOpBitwiseAnd:
+    case SpvOpOrdered:
+    case SpvOpUnordered:
+    case SpvOpLogicalEqual:
+    case SpvOpLogicalNotEqual:
+    case SpvOpLogicalOr:
+    case SpvOpLogicalAnd:
+    case SpvOpIEqual:
+    case SpvOpINotEqual:
+    case SpvOpFOrdEqual:
+    case SpvOpFUnordEqual:
+    case SpvOpFOrdNotEqual:
+    case SpvOpFUnordNotEqual:
+      return true;
+    default:
+      return false;
+  }
+}
+
 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
   switch (opcode) {
     case SpvOpMemoryBarrier:
diff --git a/third_party/SPIRV-Tools/source/opcode.h b/third_party/SPIRV-Tools/source/opcode.h
index f79826f..b4f0271 100644
--- a/third_party/SPIRV-Tools/source/opcode.h
+++ b/third_party/SPIRV-Tools/source/opcode.h
@@ -130,6 +130,10 @@
 // Returns true if the given opcode is a debug instruction.
 bool spvOpcodeIsDebug(SpvOp opcode);
 
+// Returns true for opcodes that are binary operators,
+// where the order of the operands is irrelevant.
+bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode);
+
 // Returns a vector containing the indices of the memory semantics <id>
 // operands for |opcode|.
 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
diff --git a/third_party/SPIRV-Tools/source/operand.cpp b/third_party/SPIRV-Tools/source/operand.cpp
index 3042606..755ad6a 100644
--- a/third_party/SPIRV-Tools/source/operand.cpp
+++ b/third_party/SPIRV-Tools/source/operand.cpp
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -216,6 +218,14 @@
       return "kernel profiling info";
     case SPV_OPERAND_TYPE_CAPABILITY:
       return "capability";
+    case SPV_OPERAND_TYPE_RAY_FLAGS:
+      return "ray flags";
+    case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
+      return "ray query intersection";
+    case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
+      return "ray query committed intersection type";
+    case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
+      return "ray query candidate intersection type";
     case SPV_OPERAND_TYPE_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
       return "image";
@@ -323,6 +333,10 @@
     case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
     case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
     case SPV_OPERAND_TYPE_CAPABILITY:
+    case SPV_OPERAND_TYPE_RAY_FLAGS:
+    case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
+    case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
+    case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
     case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
diff --git a/third_party/SPIRV-Tools/source/opt/CMakeLists.txt b/third_party/SPIRV-Tools/source/opt/CMakeLists.txt
index 0f719cb..1428c74 100644
--- a/third_party/SPIRV-Tools/source/opt/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/opt/CMakeLists.txt
@@ -58,6 +58,7 @@
   inline_pass.h
   inst_bindless_check_pass.h
   inst_buff_addr_check_pass.h
+  inst_debug_printf_pass.h
   instruction.h
   instruction_list.h
   instrument_pass.h
@@ -164,6 +165,7 @@
   inline_pass.cpp
   inst_bindless_check_pass.cpp
   inst_buff_addr_check_pass.cpp
+  inst_debug_printf_pass.cpp
   instruction.cpp
   instruction_list.cpp
   instrument_pass.cpp
diff --git a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
index 761ff7c..db2b67b 100644
--- a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -923,6 +923,7 @@
       "SPV_GOOGLE_hlsl_functionality1",
       "SPV_GOOGLE_user_type",
       "SPV_NV_shader_subgroup_partitioned",
+      "SPV_EXT_demote_to_helper_invocation",
       "SPV_EXT_descriptor_indexing",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_compute_shader_derivatives",
@@ -930,6 +931,7 @@
       "SPV_NV_shading_rate",
       "SPV_NV_mesh_shader",
       "SPV_NV_ray_tracing",
+      "SPV_KHR_ray_tracing",
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
   });
diff --git a/third_party/SPIRV-Tools/source/opt/feature_manager.cpp b/third_party/SPIRV-Tools/source/opt/feature_manager.cpp
index 63d50b6..b4d6f1b 100644
--- a/third_party/SPIRV-Tools/source/opt/feature_manager.cpp
+++ b/third_party/SPIRV-Tools/source/opt/feature_manager.cpp
@@ -47,6 +47,11 @@
   }
 }
 
+void FeatureManager::RemoveExtension(Extension ext) {
+  if (!extensions_.Contains(ext)) return;
+  extensions_.Remove(ext);
+}
+
 void FeatureManager::AddCapability(SpvCapability cap) {
   if (capabilities_.Contains(cap)) return;
 
@@ -60,6 +65,11 @@
   }
 }
 
+void FeatureManager::RemoveCapability(SpvCapability cap) {
+  if (!capabilities_.Contains(cap)) return;
+  capabilities_.Remove(cap);
+}
+
 void FeatureManager::AddCapabilities(Module* module) {
   for (Instruction& inst : module->capabilities()) {
     AddCapability(static_cast<SpvCapability>(inst.GetSingleWordInOperand(0)));
diff --git a/third_party/SPIRV-Tools/source/opt/feature_manager.h b/third_party/SPIRV-Tools/source/opt/feature_manager.h
index 2fe3291..881d5e6 100644
--- a/third_party/SPIRV-Tools/source/opt/feature_manager.h
+++ b/third_party/SPIRV-Tools/source/opt/feature_manager.h
@@ -30,11 +30,17 @@
   // Returns true if |ext| is an enabled extension in the module.
   bool HasExtension(Extension ext) const { return extensions_.Contains(ext); }
 
+  // Removes the given |extension| from the current FeatureManager.
+  void RemoveExtension(Extension extension);
+
   // Returns true if |cap| is an enabled capability in the module.
   bool HasCapability(SpvCapability cap) const {
     return capabilities_.Contains(cap);
   }
 
+  // Removes the given |capability| from the current FeatureManager.
+  void RemoveCapability(SpvCapability capability);
+
   // Analyzes |module| and records enabled extensions and capabilities.
   void Analyze(Module* module);
 
diff --git a/third_party/SPIRV-Tools/source/opt/function.cpp b/third_party/SPIRV-Tools/source/opt/function.cpp
index efda68b..5d50f37 100644
--- a/third_party/SPIRV-Tools/source/opt/function.cpp
+++ b/third_party/SPIRV-Tools/source/opt/function.cpp
@@ -34,6 +34,11 @@
       },
       true);
 
+  for (const auto& i : debug_insts_in_header_) {
+    clone->AddDebugInstructionInHeader(
+        std::unique_ptr<Instruction>(i.Clone(ctx)));
+  }
+
   clone->blocks_.reserve(blocks_.size());
   for (const auto& b : blocks_) {
     std::unique_ptr<BasicBlock> bb(b->Clone(ctx));
@@ -79,6 +84,12 @@
     }
   }
 
+  for (auto& di : debug_insts_in_header_) {
+    if (!di.WhileEachInst(f, run_on_debug_line_insts)) {
+      return false;
+    }
+  }
+
   for (auto& bb : blocks_) {
     if (!bb->WhileEachInst(f, run_on_debug_line_insts)) {
       return false;
@@ -106,6 +117,12 @@
     }
   }
 
+  for (const auto& di : debug_insts_in_header_) {
+    if (!di.WhileEachInst(f, run_on_debug_line_insts)) {
+      return false;
+    }
+  }
+
   for (const auto& bb : blocks_) {
     if (!static_cast<const BasicBlock*>(bb.get())->WhileEachInst(
             f, run_on_debug_line_insts)) {
diff --git a/third_party/SPIRV-Tools/source/opt/function.h b/third_party/SPIRV-Tools/source/opt/function.h
index 3908568..f208d8e 100644
--- a/third_party/SPIRV-Tools/source/opt/function.h
+++ b/third_party/SPIRV-Tools/source/opt/function.h
@@ -56,6 +56,8 @@
 
   // Appends a parameter to this function.
   inline void AddParameter(std::unique_ptr<Instruction> p);
+  // Appends a debug instruction in function header to this function.
+  inline void AddDebugInstructionInHeader(std::unique_ptr<Instruction> p);
   // Appends a basic block to this function.
   inline void AddBasicBlock(std::unique_ptr<BasicBlock> b);
   // Appends a basic block to this function at the position |ip|.
@@ -151,6 +153,8 @@
   std::unique_ptr<Instruction> def_inst_;
   // All parameters to this function.
   std::vector<std::unique_ptr<Instruction>> params_;
+  // All debug instructions in this function's header.
+  InstructionList debug_insts_in_header_;
   // All basic blocks inside this function in specification order
   std::vector<std::unique_ptr<BasicBlock>> blocks_;
   // The OpFunctionEnd instruction.
@@ -167,6 +171,11 @@
   params_.emplace_back(std::move(p));
 }
 
+inline void Function::AddDebugInstructionInHeader(
+    std::unique_ptr<Instruction> p) {
+  debug_insts_in_header_.push_back(std::move(p));
+}
+
 inline void Function::AddBasicBlock(std::unique_ptr<BasicBlock> b) {
   AddBasicBlock(std::move(b), end());
 }
diff --git a/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.cpp b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.cpp
new file mode 100644
index 0000000..c0e6bc3
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.cpp
@@ -0,0 +1,266 @@
+// Copyright (c) 2020 The Khronos Group Inc.
+// Copyright (c) 2020 Valve Corporation
+// Copyright (c) 2020 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "inst_debug_printf_pass.h"
+
+#include "spirv/unified1/NonSemanticDebugPrintf.h"
+
+namespace spvtools {
+namespace opt {
+
+void InstDebugPrintfPass::GenOutputValues(Instruction* val_inst,
+                                          std::vector<uint32_t>* val_ids,
+                                          InstructionBuilder* builder) {
+  uint32_t val_ty_id = val_inst->type_id();
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::Type* val_ty = type_mgr->GetType(val_ty_id);
+  switch (val_ty->kind()) {
+    case analysis::Type::kVector: {
+      analysis::Vector* v_ty = val_ty->AsVector();
+      const analysis::Type* c_ty = v_ty->element_type();
+      uint32_t c_ty_id = type_mgr->GetId(c_ty);
+      for (uint32_t c = 0; c < v_ty->element_count(); ++c) {
+        Instruction* c_inst = builder->AddIdLiteralOp(
+            c_ty_id, SpvOpCompositeExtract, val_inst->result_id(), c);
+        GenOutputValues(c_inst, val_ids, builder);
+      }
+      return;
+    }
+    case analysis::Type::kBool: {
+      // Select between uint32 zero or one
+      uint32_t zero_id = builder->GetUintConstantId(0);
+      uint32_t one_id = builder->GetUintConstantId(1);
+      Instruction* sel_inst = builder->AddTernaryOp(
+          GetUintId(), SpvOpSelect, val_inst->result_id(), one_id, zero_id);
+      val_ids->push_back(sel_inst->result_id());
+      return;
+    }
+    case analysis::Type::kFloat: {
+      analysis::Float* f_ty = val_ty->AsFloat();
+      switch (f_ty->width()) {
+        case 16: {
+          // Convert float16 to float32 and recurse
+          Instruction* f32_inst = builder->AddUnaryOp(
+              GetFloatId(), SpvOpFConvert, val_inst->result_id());
+          GenOutputValues(f32_inst, val_ids, builder);
+          return;
+        }
+        case 64: {
+          // Bitcast float64 to uint64 and recurse
+          Instruction* ui64_inst = builder->AddUnaryOp(
+              GetUint64Id(), SpvOpBitcast, val_inst->result_id());
+          GenOutputValues(ui64_inst, val_ids, builder);
+          return;
+        }
+        case 32: {
+          // Bitcase float32 to uint32
+          Instruction* bc_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
+                                                     val_inst->result_id());
+          val_ids->push_back(bc_inst->result_id());
+          return;
+        }
+        default:
+          assert(false && "unsupported float width");
+          return;
+      }
+    }
+    case analysis::Type::kInteger: {
+      analysis::Integer* i_ty = val_ty->AsInteger();
+      switch (i_ty->width()) {
+        case 64: {
+          Instruction* ui64_inst = val_inst;
+          if (i_ty->IsSigned()) {
+            // Bitcast sint64 to uint64
+            ui64_inst = builder->AddUnaryOp(GetUint64Id(), SpvOpBitcast,
+                                            val_inst->result_id());
+          }
+          // Break uint64 into 2x uint32
+          Instruction* lo_ui64_inst = builder->AddUnaryOp(
+              GetUintId(), SpvOpUConvert, ui64_inst->result_id());
+          Instruction* rshift_ui64_inst = builder->AddBinaryOp(
+              GetUint64Id(), SpvOpShiftRightLogical, ui64_inst->result_id(),
+              builder->GetUintConstantId(32));
+          Instruction* hi_ui64_inst = builder->AddUnaryOp(
+              GetUintId(), SpvOpUConvert, rshift_ui64_inst->result_id());
+          val_ids->push_back(lo_ui64_inst->result_id());
+          val_ids->push_back(hi_ui64_inst->result_id());
+          return;
+        }
+        case 8: {
+          Instruction* ui8_inst = val_inst;
+          if (i_ty->IsSigned()) {
+            // Bitcast sint8 to uint8
+            ui8_inst = builder->AddUnaryOp(GetUint8Id(), SpvOpBitcast,
+                                           val_inst->result_id());
+          }
+          // Convert uint8 to uint32
+          Instruction* ui32_inst = builder->AddUnaryOp(
+              GetUintId(), SpvOpUConvert, ui8_inst->result_id());
+          val_ids->push_back(ui32_inst->result_id());
+          return;
+        }
+        case 32: {
+          Instruction* ui32_inst = val_inst;
+          if (i_ty->IsSigned()) {
+            // Bitcast sint32 to uint32
+            ui32_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
+                                            val_inst->result_id());
+          }
+          // uint32 needs no further processing
+          val_ids->push_back(ui32_inst->result_id());
+          return;
+        }
+        default:
+          // TODO(greg-lunarg): Support non-32-bit int
+          assert(false && "unsupported int width");
+          return;
+      }
+    }
+    default:
+      assert(false && "unsupported type");
+      return;
+  }
+}
+
+void InstDebugPrintfPass::GenOutputCode(
+    Instruction* printf_inst, uint32_t stage_idx,
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  BasicBlock* back_blk_ptr = &*new_blocks->back();
+  InstructionBuilder builder(
+      context(), back_blk_ptr,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  // Gen debug printf record validation-specific values. The format string
+  // will have its id written. Vectors will need to be broken down into
+  // component values. float16 will need to be converted to float32. Pointer
+  // and uint64 will need to be converted to two uint32 values. float32 will
+  // need to be bitcast to uint32. int32 will need to be bitcast to uint32.
+  std::vector<uint32_t> val_ids;
+  bool is_first_operand = false;
+  printf_inst->ForEachInId(
+      [&is_first_operand, &val_ids, &builder, this](const uint32_t* iid) {
+        // skip set operand
+        if (!is_first_operand) {
+          is_first_operand = true;
+          return;
+        }
+        Instruction* opnd_inst = get_def_use_mgr()->GetDef(*iid);
+        if (opnd_inst->opcode() == SpvOpString) {
+          uint32_t string_id_id = builder.GetUintConstantId(*iid);
+          val_ids.push_back(string_id_id);
+        } else {
+          GenOutputValues(opnd_inst, &val_ids, &builder);
+        }
+      });
+  GenDebugStreamWrite(uid2offset_[printf_inst->unique_id()], stage_idx, val_ids,
+                      &builder);
+  context()->KillInst(printf_inst);
+}
+
+void InstDebugPrintfPass::GenDebugPrintfCode(
+    BasicBlock::iterator ref_inst_itr,
+    UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  // If not DebugPrintf OpExtInst, return.
+  Instruction* printf_inst = &*ref_inst_itr;
+  if (printf_inst->opcode() != SpvOpExtInst) return;
+  if (printf_inst->GetSingleWordInOperand(0) != ext_inst_printf_id_) return;
+  if (printf_inst->GetSingleWordInOperand(1) !=
+      NonSemanticDebugPrintfDebugPrintf)
+    return;
+  // Initialize DefUse manager before dismantling module
+  (void)get_def_use_mgr();
+  // Move original block's preceding instructions into first new block
+  std::unique_ptr<BasicBlock> new_blk_ptr;
+  MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  // Generate instructions to output printf args to printf buffer
+  GenOutputCode(printf_inst, stage_idx, new_blocks);
+  // Caller expects at least two blocks with last block containing remaining
+  // code, so end block after instrumentation, create remainder block, and
+  // branch to it
+  uint32_t rem_blk_id = TakeNextId();
+  std::unique_ptr<Instruction> rem_label(NewLabel(rem_blk_id));
+  BasicBlock* back_blk_ptr = &*new_blocks->back();
+  InstructionBuilder builder(
+      context(), back_blk_ptr,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  (void)builder.AddBranch(rem_blk_id);
+  // Gen remainder block
+  new_blk_ptr.reset(new BasicBlock(std::move(rem_label)));
+  builder.SetInsertPoint(&*new_blk_ptr);
+  // Move original block's remaining code into remainder block and add
+  // to new blocks
+  MovePostludeCode(ref_block_itr, &*new_blk_ptr);
+  new_blocks->push_back(std::move(new_blk_ptr));
+}
+
+void InstDebugPrintfPass::InitializeInstDebugPrintf() {
+  // Initialize base class
+  InitializeInstrument();
+}
+
+Pass::Status InstDebugPrintfPass::ProcessImpl() {
+  // Perform printf instrumentation on each entry point function in module
+  InstProcessFunction pfn =
+      [this](BasicBlock::iterator ref_inst_itr,
+             UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+             std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+        return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, stage_idx,
+                                  new_blocks);
+      };
+  (void)InstProcessEntryPointCallTree(pfn);
+  // Remove DebugPrintf OpExtInstImport instruction
+  Instruction* ext_inst_import_inst =
+      get_def_use_mgr()->GetDef(ext_inst_printf_id_);
+  context()->KillInst(ext_inst_import_inst);
+  // If no remaining non-semantic instruction sets, remove non-semantic debug
+  // info extension from module and feature manager
+  bool non_sem_set_seen = false;
+  for (auto c_itr = context()->module()->ext_inst_import_begin();
+       c_itr != context()->module()->ext_inst_import_end(); ++c_itr) {
+    const char* set_name =
+        reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
+    const char* non_sem_str = "NonSemantic.";
+    if (!strncmp(set_name, non_sem_str, strlen(non_sem_str))) {
+      non_sem_set_seen = true;
+      break;
+    }
+  }
+  if (!non_sem_set_seen) {
+    for (auto c_itr = context()->module()->extension_begin();
+         c_itr != context()->module()->extension_end(); ++c_itr) {
+      const char* ext_name =
+          reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
+      if (!strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+        context()->KillInst(&*c_itr);
+        break;
+      }
+    }
+    context()->get_feature_mgr()->RemoveExtension(kSPV_KHR_non_semantic_info);
+  }
+  return Status::SuccessWithChange;
+}
+
+Pass::Status InstDebugPrintfPass::Process() {
+  ext_inst_printf_id_ =
+      get_module()->GetExtInstImportId("NonSemantic.DebugPrintf");
+  if (ext_inst_printf_id_ == 0) return Status::SuccessWithoutChange;
+  InitializeInstDebugPrintf();
+  return ProcessImpl();
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h
new file mode 100644
index 0000000..2968a20
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2020 The Khronos Group Inc.
+// Copyright (c) 2020 Valve Corporation
+// Copyright (c) 2020 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_
+#define LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_
+
+#include "instrument_pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// This class/pass is designed to support the debug printf GPU-assisted layer
+// of https://github.com/KhronosGroup/Vulkan-ValidationLayers. Its internal and
+// external design may change as the layer evolves.
+class InstDebugPrintfPass : public InstrumentPass {
+ public:
+  // For test harness only
+  InstDebugPrintfPass()
+      : InstrumentPass(7, 23, kInstValidationIdDebugPrintf, 2) {}
+  // For all other interfaces
+  InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id)
+      : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf, 2) {}
+
+  ~InstDebugPrintfPass() override = default;
+
+  // See optimizer.hpp for pass user documentation.
+  Status Process() override;
+
+  const char* name() const override { return "inst-printf-pass"; }
+
+ private:
+  // Generate instructions for OpDebugPrintf.
+  //
+  // If |ref_inst_itr| is an OpDebugPrintf, return in |new_blocks| the result
+  // of replacing it with buffer write instructions within its block at
+  // |ref_block_itr|.  The instructions write a record to the printf
+  // output buffer stream including |function_idx, instruction_idx, stage_idx|
+  // and removes the OpDebugPrintf. The block at |ref_block_itr| can just be
+  // replaced with the block in |new_blocks|. Besides the buffer writes, this
+  // block will comprise all instructions preceding and following
+  // |ref_inst_itr|.
+  //
+  // This function is designed to be passed to
+  // InstrumentPass::InstProcessEntryPointCallTree(), which applies the
+  // function to each instruction in a module and replaces the instruction
+  // if warranted.
+  //
+  // This instrumentation function utilizes GenDebugStreamWrite() to write its
+  // error records. The validation-specific part of the error record will
+  // consist of a uint32 which is the id of the format string plus a sequence
+  // of uint32s representing the values of the remaining operands of the
+  // DebugPrintf.
+  void GenDebugPrintfCode(BasicBlock::iterator ref_inst_itr,
+                          UptrVectorIterator<BasicBlock> ref_block_itr,
+                          uint32_t stage_idx,
+                          std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Generate a sequence of uint32 instructions in |builder| (if necessary)
+  // representing the value of |val_inst|, which must be a buffer pointer, a
+  // uint64, or a scalar or vector of type uint32, float32 or float16. Append
+  // the ids of all values to the end of |val_ids|.
+  void GenOutputValues(Instruction* val_inst, std::vector<uint32_t>* val_ids,
+                       InstructionBuilder* builder);
+
+  // Generate instructions to write a record containing the operands of
+  // |printf_inst| arguments to printf buffer, adding new code to the end of
+  // the last block in |new_blocks|. Kill OpDebugPrintf instruction.
+  void GenOutputCode(Instruction* printf_inst, uint32_t stage_idx,
+                     std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Initialize state for instrumenting bindless checking
+  void InitializeInstDebugPrintf();
+
+  // Apply GenDebugPrintfCode to every instruction in module.
+  Pass::Status ProcessImpl();
+
+  uint32_t ext_inst_printf_id_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.cpp b/third_party/SPIRV-Tools/source/opt/instruction.cpp
index 49f9142..3ce38a9 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instruction.cpp
@@ -16,6 +16,7 @@
 
 #include <initializer_list>
 
+#include "OpenCLDebugInfo100.h"
 #include "source/disassemble.h"
 #include "source/opt/fold.h"
 #include "source/opt/ir_context.h"
@@ -30,6 +31,11 @@
 const uint32_t kLoadBaseIndex = 0;
 const uint32_t kVariableStorageClassIndex = 0;
 const uint32_t kTypeImageSampledIndex = 5;
+
+// Constants for OpenCL.DebugInfo.100 extension instructions.
+const uint32_t kDebugScopeNumWords = 7;
+const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6;
+const uint32_t kDebugNoScopeNumWords = 5;
 }  // namespace
 
 Instruction::Instruction(IRContext* c)
@@ -38,7 +44,8 @@
       opcode_(SpvOpNop),
       has_type_id_(false),
       has_result_id_(false),
-      unique_id_(c->TakeNextUniqueId()) {}
+      unique_id_(c->TakeNextUniqueId()),
+      dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
 
 Instruction::Instruction(IRContext* c, SpvOp op)
     : utils::IntrusiveNodeBase<Instruction>(),
@@ -46,7 +53,8 @@
       opcode_(op),
       has_type_id_(false),
       has_result_id_(false),
-      unique_id_(c->TakeNextUniqueId()) {}
+      unique_id_(c->TakeNextUniqueId()),
+      dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
 
 Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
                          std::vector<Instruction>&& dbg_line)
@@ -55,7 +63,8 @@
       has_type_id_(inst.type_id != 0),
       has_result_id_(inst.result_id != 0),
       unique_id_(c->TakeNextUniqueId()),
-      dbg_line_insts_(std::move(dbg_line)) {
+      dbg_line_insts_(std::move(dbg_line)),
+      dbg_scope_(kNoDebugScope, kNoInlinedAt) {
   assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) &&
          "Op(No)Line attaching to Op(No)Line found");
   for (uint32_t i = 0; i < inst.num_operands; ++i) {
@@ -67,6 +76,23 @@
   }
 }
 
+Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
+                         const DebugScope& dbg_scope)
+    : context_(c),
+      opcode_(static_cast<SpvOp>(inst.opcode)),
+      has_type_id_(inst.type_id != 0),
+      has_result_id_(inst.result_id != 0),
+      unique_id_(c->TakeNextUniqueId()),
+      dbg_scope_(dbg_scope) {
+  for (uint32_t i = 0; i < inst.num_operands; ++i) {
+    const auto& current_payload = inst.operands[i];
+    std::vector<uint32_t> words(
+        inst.words + current_payload.offset,
+        inst.words + current_payload.offset + current_payload.num_words);
+    operands_.emplace_back(current_payload.type, std::move(words));
+  }
+}
+
 Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id,
                          uint32_t res_id, const OperandList& in_operands)
     : utils::IntrusiveNodeBase<Instruction>(),
@@ -75,7 +101,8 @@
       has_type_id_(ty_id != 0),
       has_result_id_(res_id != 0),
       unique_id_(c->TakeNextUniqueId()),
-      operands_() {
+      operands_(),
+      dbg_scope_(kNoDebugScope, kNoInlinedAt) {
   if (has_type_id_) {
     operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID,
                            std::initializer_list<uint32_t>{ty_id});
@@ -94,7 +121,12 @@
       has_result_id_(that.has_result_id_),
       unique_id_(that.unique_id_),
       operands_(std::move(that.operands_)),
-      dbg_line_insts_(std::move(that.dbg_line_insts_)) {}
+      dbg_line_insts_(std::move(that.dbg_line_insts_)),
+      dbg_scope_(that.dbg_scope_) {
+  for (auto& i : dbg_line_insts_) {
+    i.dbg_scope_ = that.dbg_scope_;
+  }
+}
 
 Instruction& Instruction::operator=(Instruction&& that) {
   opcode_ = that.opcode_;
@@ -103,6 +135,7 @@
   unique_id_ = that.unique_id_;
   operands_ = std::move(that.operands_);
   dbg_line_insts_ = std::move(that.dbg_line_insts_);
+  dbg_scope_ = that.dbg_scope_;
   return *this;
 }
 
@@ -114,6 +147,7 @@
   clone->unique_id_ = c->TakeNextUniqueId();
   clone->operands_ = operands_;
   clone->dbg_line_insts_ = dbg_line_insts_;
+  clone->dbg_scope_ = dbg_scope_;
   return clone;
 }
 
@@ -198,6 +232,14 @@
 
   Instruction* base_type =
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
+
+  // Unpack the optional layer of arraying.
+  if (base_type->opcode() == SpvOpTypeArray ||
+      base_type->opcode() == SpvOpTypeRuntimeArray) {
+    base_type = context()->get_def_use_mgr()->GetDef(
+        base_type->GetSingleWordInOperand(0));
+  }
+
   if (base_type->opcode() != SpvOpTypeImage) {
     return false;
   }
@@ -224,6 +266,14 @@
 
   Instruction* base_type =
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
+
+  // Unpack the optional layer of arraying.
+  if (base_type->opcode() == SpvOpTypeArray ||
+      base_type->opcode() == SpvOpTypeRuntimeArray) {
+    base_type = context()->get_def_use_mgr()->GetDef(
+        base_type->GetSingleWordInOperand(0));
+  }
+
   if (base_type->opcode() != SpvOpTypeImage) {
     return false;
   }
@@ -250,6 +300,14 @@
 
   Instruction* base_type =
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
+
+  // Unpack the optional layer of arraying.
+  if (base_type->opcode() == SpvOpTypeArray ||
+      base_type->opcode() == SpvOpTypeRuntimeArray) {
+    base_type = context()->get_def_use_mgr()->GetDef(
+        base_type->GetSingleWordInOperand(0));
+  }
+
   if (base_type->opcode() != SpvOpTypeImage) {
     return false;
   }
@@ -273,6 +331,13 @@
   Instruction* base_type =
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
 
+  // Unpack the optional layer of arraying.
+  if (base_type->opcode() == SpvOpTypeArray ||
+      base_type->opcode() == SpvOpTypeRuntimeArray) {
+    base_type = context()->get_def_use_mgr()->GetDef(
+        base_type->GetSingleWordInOperand(0));
+  }
+
   if (base_type->opcode() != SpvOpTypeStruct) {
     return false;
   }
@@ -306,6 +371,14 @@
 
   Instruction* base_type =
       context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
+
+  // Unpack the optional layer of arraying.
+  if (base_type->opcode() == SpvOpTypeArray ||
+      base_type->opcode() == SpvOpTypeRuntimeArray) {
+    base_type = context()->get_def_use_mgr()->GetDef(
+        base_type->GetSingleWordInOperand(0));
+  }
+
   if (base_type->opcode() != SpvOpTypeStruct) {
     return false;
   }
@@ -735,5 +808,28 @@
   }
 }
 
+void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id,
+                          uint32_t ext_set,
+                          std::vector<uint32_t>* binary) const {
+  uint32_t num_words = kDebugScopeNumWords;
+  OpenCLDebugInfo100Instructions dbg_opcode = OpenCLDebugInfo100DebugScope;
+  if (GetLexicalScope() == kNoDebugScope) {
+    num_words = kDebugNoScopeNumWords;
+    dbg_opcode = OpenCLDebugInfo100DebugNoScope;
+  } else if (GetInlinedAt() == kNoInlinedAt) {
+    num_words = kDebugScopeNumWordsWithoutInlinedAt;
+  }
+  std::vector<uint32_t> operands = {
+      (num_words << 16) | static_cast<uint16_t>(SpvOpExtInst),
+      type_id,
+      result_id,
+      ext_set,
+      static_cast<uint32_t>(dbg_opcode),
+  };
+  binary->insert(binary->end(), operands.begin(), operands.end());
+  if (GetLexicalScope() != kNoDebugScope) binary->push_back(GetLexicalScope());
+  if (GetInlinedAt() != kNoInlinedAt) binary->push_back(GetInlinedAt());
+}
+
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.h b/third_party/SPIRV-Tools/source/opt/instruction.h
index 322e0aa..a3342c6 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.h
+++ b/third_party/SPIRV-Tools/source/opt/instruction.h
@@ -32,6 +32,9 @@
 #include "source/opt/reflect.h"
 #include "spirv-tools/libspirv.h"
 
+const uint32_t kNoDebugScope = 0;
+const uint32_t kNoInlinedAt = 0;
+
 namespace spvtools {
 namespace opt {
 
@@ -80,6 +83,15 @@
   spv_operand_type_t type;  // Type of this logical operand.
   OperandData words;        // Binary segments of this logical operand.
 
+  // Returns a string operand as a C-style string.
+  const char* AsCString() const {
+    assert(type == SPV_OPERAND_TYPE_LITERAL_STRING);
+    return reinterpret_cast<const char*>(words.data());
+  }
+
+  // Returns a string operand as a std::string.
+  std::string AsString() const { return AsCString(); }
+
   friend bool operator==(const Operand& o1, const Operand& o2) {
     return o1.type == o2.type && o1.words == o2.words;
   }
@@ -91,6 +103,44 @@
   return !(o1 == o2);
 }
 
+// This structure is used to represent a DebugScope instruction from
+// the OpenCL.100.DebugInfo extened instruction set. Note that we can
+// ignore the result id of DebugScope instruction because it is not
+// used for anything. We do not keep it to reduce the size of
+// structure.
+// TODO: Let validator check that the result id is not used anywhere.
+class DebugScope {
+ public:
+  DebugScope(uint32_t lexical_scope, uint32_t inlined_at)
+      : lexical_scope_(lexical_scope), inlined_at_(inlined_at) {}
+
+  inline bool operator!=(const DebugScope& d) const {
+    return lexical_scope_ != d.lexical_scope_ || inlined_at_ != d.inlined_at_;
+  }
+
+  // Accessor functions for |lexical_scope_|.
+  uint32_t GetLexicalScope() const { return lexical_scope_; }
+  void SetLexicalScope(uint32_t scope) { lexical_scope_ = scope; }
+
+  // Accessor functions for |inlined_at_|.
+  uint32_t GetInlinedAt() const { return inlined_at_; }
+  void SetInlinedAt(uint32_t at) { inlined_at_ = at; }
+
+  // Pushes the binary segments for this DebugScope instruction into
+  // the back of *|binary|.
+  void ToBinary(uint32_t type_id, uint32_t result_id, uint32_t ext_set,
+                std::vector<uint32_t>* binary) const;
+
+ private:
+  // The result id of the lexical scope in which this debug scope is
+  // contained. The value is kNoDebugScope if there is no scope.
+  uint32_t lexical_scope_;
+
+  // The result id of DebugInlinedAt if instruction in this debug scope
+  // is inlined. The value is kNoInlinedAt if it is not inlined.
+  uint32_t inlined_at_;
+};
+
 // A SPIR-V instruction. It contains the opcode and any additional logical
 // operand, including the result id (if any) and result type id (if any). It
 // may also contain line-related debug instruction (OpLine, OpNoLine) directly
@@ -111,7 +161,8 @@
         opcode_(SpvOpNop),
         has_type_id_(false),
         has_result_id_(false),
-        unique_id_(0) {}
+        unique_id_(0),
+        dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
 
   // Creates a default OpNop instruction.
   Instruction(IRContext*);
@@ -125,6 +176,9 @@
   Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
               std::vector<Instruction>&& dbg_line = {});
 
+  Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
+              const DebugScope& dbg_scope);
+
   // Creates an instruction with the given opcode |op|, type id: |ty_id|,
   // result id: |res_id| and input operands: |in_operands|.
   Instruction(IRContext* c, SpvOp op, uint32_t ty_id, uint32_t res_id,
@@ -221,6 +275,9 @@
   // Sets the result id
   inline void SetResultId(uint32_t res_id);
   inline bool HasResultId() const { return has_result_id_; }
+  // Sets DebugScope.
+  inline void SetDebugScope(const DebugScope& scope);
+  inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
   // Remove the |index|-th operand
   void RemoveOperand(uint32_t index) {
     operands_.erase(operands_.begin() + index);
@@ -473,6 +530,9 @@
   // empty.
   std::vector<Instruction> dbg_line_insts_;
 
+  // DebugScope that wraps this instruction.
+  DebugScope dbg_scope_;
+
   friend InstructionList;
 };
 
@@ -544,6 +604,13 @@
   operands_[ridx].words = {res_id};
 }
 
+inline void Instruction::SetDebugScope(const DebugScope& scope) {
+  dbg_scope_ = scope;
+  for (auto& i : dbg_line_insts_) {
+    i.dbg_scope_ = scope;
+  }
+}
+
 inline void Instruction::SetResultType(uint32_t ty_id) {
   // TODO(dsinclair): Allow setting a type id if there wasn't one
   // previously. Need to make room in the operands_ array to place the result,
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
index b1a6edb..c8c6c21 100644
--- a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
@@ -380,6 +380,8 @@
       return kDebugOutputBindingStream;
     case kInstValidationIdBuffAddr:
       return kDebugOutputBindingStream;
+    case kInstValidationIdDebugPrintf:
+      return kDebugOutputPrintfStream;
     default:
       assert(false && "unexpected validation id");
   }
@@ -529,6 +531,16 @@
   return input_buffer_id_;
 }
 
+uint32_t InstrumentPass::GetFloatId() {
+  if (float_id_ == 0) {
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::Float float_ty(32);
+    analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty);
+    float_id_ = type_mgr->GetTypeInstruction(reg_float_ty);
+  }
+  return float_id_;
+}
+
 uint32_t InstrumentPass::GetVec4FloatId() {
   if (v4float_id_ == 0) {
     analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -561,6 +573,16 @@
   return uint64_id_;
 }
 
+uint32_t InstrumentPass::GetUint8Id() {
+  if (uint8_id_ == 0) {
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::Integer uint8_ty(8, false);
+    analysis::Type* reg_uint8_ty = type_mgr->GetRegisteredType(&uint8_ty);
+    uint8_id_ = type_mgr->GetTypeInstruction(reg_uint8_ty);
+  }
+  return uint8_id_;
+}
+
 uint32_t InstrumentPass::GetVecUintId(uint32_t len) {
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::Integer uint_ty(32, false);
@@ -606,21 +628,22 @@
   // Total param count is common params plus validation-specific
   // params
   uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt;
-  if (output_func_id_ == 0) {
+  if (param2output_func_id_[param_cnt] == 0) {
     // Create function
-    output_func_id_ = TakeNextId();
+    param2output_func_id_[param_cnt] = TakeNextId();
     analysis::TypeManager* type_mgr = context()->get_type_mgr();
     std::vector<const analysis::Type*> param_types;
     for (uint32_t c = 0; c < param_cnt; ++c)
       param_types.push_back(type_mgr->GetType(GetUintId()));
     analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types);
     analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
-    std::unique_ptr<Instruction> func_inst(new Instruction(
-        get_module()->context(), SpvOpFunction, GetVoidId(), output_func_id_,
-        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-          {SpvFunctionControlMaskNone}},
-         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-          {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
+    std::unique_ptr<Instruction> func_inst(
+        new Instruction(get_module()->context(), SpvOpFunction, GetVoidId(),
+                        param2output_func_id_[param_cnt],
+                        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+                          {SpvFunctionControlMaskNone}},
+                         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+                          {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
     get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
     std::unique_ptr<Function> output_func =
         MakeUnique<Function>(std::move(func_inst));
@@ -709,10 +732,8 @@
     get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
     output_func->SetFunctionEnd(std::move(func_end_inst));
     context()->AddFunction(std::move(output_func));
-    output_func_param_cnt_ = param_cnt;
   }
-  assert(param_cnt == output_func_param_cnt_ && "bad arg count");
-  return output_func_id_;
+  return param2output_func_id_[param_cnt];
 }
 
 uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
@@ -848,7 +869,7 @@
   std::unordered_set<uint32_t> done;
   // Don't process input and output functions
   for (auto& ifn : param2input_func_id_) done.insert(ifn.second);
-  if (output_func_id_ != 0) done.insert(output_func_id_);
+  for (auto& ofn : param2output_func_id_) done.insert(ofn.second);
   // Process all functions from roots
   while (!roots->empty()) {
     const uint32_t fi = roots->front();
@@ -926,12 +947,12 @@
   output_buffer_id_ = 0;
   output_buffer_ptr_id_ = 0;
   input_buffer_ptr_id_ = 0;
-  output_func_id_ = 0;
-  output_func_param_cnt_ = 0;
   input_buffer_id_ = 0;
+  float_id_ = 0;
   v4float_id_ = 0;
   uint_id_ = 0;
   uint64_id_ = 0;
+  uint8_id_ = 0;
   v4uint_id_ = 0;
   v3uint_id_ = 0;
   bool_id_ = 0;
@@ -944,6 +965,10 @@
   id2function_.clear();
   id2block_.clear();
 
+  // clear maps
+  param2input_func_id_.clear();
+  param2output_func_id_.clear();
+
   // Initialize function and block maps.
   for (auto& fn : *get_module()) {
     id2function_[fn.result_id()] = &fn;
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.h b/third_party/SPIRV-Tools/source/opt/instrument_pass.h
index 02568fb..11afdce 100644
--- a/third_party/SPIRV-Tools/source/opt/instrument_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.h
@@ -61,6 +61,7 @@
 // its output buffers.
 static const uint32_t kInstValidationIdBindless = 0;
 static const uint32_t kInstValidationIdBuffAddr = 1;
+static const uint32_t kInstValidationIdDebugPrintf = 2;
 
 class InstrumentPass : public Pass {
   using cbb_ptr = const BasicBlock*;
@@ -227,9 +228,12 @@
   // Return id for 32-bit unsigned type
   uint32_t GetUintId();
 
-  // Return id for 32-bit unsigned type
+  // Return id for 64-bit unsigned type
   uint32_t GetUint64Id();
 
+  // Return id for 8-bit unsigned type
+  uint32_t GetUint8Id();
+
   // Return id for 32-bit unsigned type
   uint32_t GetBoolId();
 
@@ -267,6 +271,9 @@
   // Return id for debug input buffer
   uint32_t GetInputBufferId();
 
+  // Return id for 32-bit float type
+  uint32_t GetFloatId();
+
   // Return id for v4float type
   uint32_t GetVec4FloatId();
 
@@ -383,17 +390,17 @@
   uint32_t input_buffer_ptr_id_;
 
   // id for debug output function
-  uint32_t output_func_id_;
+  std::unordered_map<uint32_t, uint32_t> param2output_func_id_;
 
   // ids for debug input functions
   std::unordered_map<uint32_t, uint32_t> param2input_func_id_;
 
-  // param count for output function
-  uint32_t output_func_param_cnt_;
-
   // id for input buffer variable
   uint32_t input_buffer_id_;
 
+  // id for 32-bit float type
+  uint32_t float_id_;
+
   // id for v4float type
   uint32_t v4float_id_;
 
@@ -406,9 +413,12 @@
   // id for 32-bit unsigned type
   uint32_t uint_id_;
 
-  // id for 32-bit unsigned type
+  // id for 64-bit unsigned type
   uint32_t uint64_id_;
 
+  // id for 8-bit unsigned type
+  uint32_t uint8_id_;
+
   // id for bool type
   uint32_t bool_id_;
 
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.cpp b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
index 7bca29b..72993fd 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_context.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
@@ -385,6 +385,8 @@
                                SpvOpTypeSampler,
                                SpvOpTypeSampledImage,
                                SpvOpTypeAccelerationStructureNV,
+                               SpvOpTypeAccelerationStructureKHR,
+                               SpvOpTypeRayQueryProvisionalKHR,
                                SpvOpTypeArray,
                                SpvOpTypeRuntimeArray,
                                SpvOpTypeStruct,
diff --git a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
index 836012f..fcde079 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
@@ -23,6 +23,10 @@
 #include "source/opt/reflect.h"
 #include "source/util/make_unique.h"
 
+static const uint32_t kExtInstSetIndex = 4;
+static const uint32_t kLexicalScopeIndex = 5;
+static const uint32_t kInlinedAtIndex = 6;
+
 namespace spvtools {
 namespace opt {
 
@@ -30,16 +34,60 @@
     : consumer_(consumer),
       module_(m),
       source_("<instruction>"),
-      inst_index_(0) {}
+      inst_index_(0),
+      last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
 
 bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
   ++inst_index_;
   const auto opcode = static_cast<SpvOp>(inst->opcode);
   if (IsDebugLineInst(opcode)) {
-    dbg_line_info_.push_back(Instruction(module()->context(), *inst));
+    dbg_line_info_.push_back(
+        Instruction(module()->context(), *inst, last_dbg_scope_));
     return true;
   }
 
+  // If it is a DebugScope or DebugNoScope of debug extension, we do not
+  // create a new instruction, but simply keep the information in
+  // struct DebugScope.
+  if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+    const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
+    if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+      const OpenCLDebugInfo100Instructions ext_inst_key =
+          OpenCLDebugInfo100Instructions(ext_inst_index);
+      if (ext_inst_key == OpenCLDebugInfo100DebugScope) {
+        uint32_t inlined_at = 0;
+        if (inst->num_words > kInlinedAtIndex)
+          inlined_at = inst->words[kInlinedAtIndex];
+        last_dbg_scope_ =
+            DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
+        module()->SetContainsDebugScope();
+        return true;
+      }
+      if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) {
+        last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
+        module()->SetContainsDebugScope();
+        return true;
+      }
+    } else {
+      const DebugInfoInstructions ext_inst_key =
+          DebugInfoInstructions(ext_inst_index);
+      if (ext_inst_key == DebugInfoDebugScope) {
+        uint32_t inlined_at = 0;
+        if (inst->num_words > kInlinedAtIndex)
+          inlined_at = inst->words[kInlinedAtIndex];
+        last_dbg_scope_ =
+            DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
+        module()->SetContainsDebugScope();
+        return true;
+      }
+      if (ext_inst_key == DebugInfoDebugNoScope) {
+        last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
+        module()->SetContainsDebugScope();
+        return true;
+      }
+    }
+  }
+
   std::unique_ptr<Instruction> spv_inst(
       new Instruction(module()->context(), *inst, std::move(dbg_line_info_)));
   dbg_line_info_.clear();
@@ -90,6 +138,7 @@
     block_->AddInstruction(std::move(spv_inst));
     function_->AddBasicBlock(std::move(block_));
     block_ = nullptr;
+    last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
   } else {
     if (function_ == nullptr) {  // Outside function definition
       SPIRV_ASSERT(consumer_, block_ == nullptr);
@@ -131,26 +180,32 @@
         return false;
       }
     } else {
-      if (block_ == nullptr) {  // Inside function but outside blocks
-        if (opcode != SpvOpFunctionParameter) {
-          Errorf(consumer_, src, loc,
-                 "Non-OpFunctionParameter (opcode: %d) found inside "
-                 "function but outside basic block",
-                 opcode);
-          return false;
-        }
-        function_->AddParameter(std::move(spv_inst));
-      } else {
-        if (opcode == SpvOpExtInst &&
-            spvExtInstIsDebugInfo(inst->ext_inst_type)) {
-          const uint32_t ext_inst_index = inst->words[4];
-          if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
-            const OpenCLDebugInfo100Instructions ext_inst_key =
-                OpenCLDebugInfo100Instructions(ext_inst_index);
-            if (ext_inst_key != OpenCLDebugInfo100DebugScope &&
-                ext_inst_key != OpenCLDebugInfo100DebugNoScope &&
-                ext_inst_key != OpenCLDebugInfo100DebugDeclare &&
-                ext_inst_key != OpenCLDebugInfo100DebugValue) {
+      if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge)
+        last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
+      if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
+        spv_inst->SetDebugScope(last_dbg_scope_);
+      if (opcode == SpvOpExtInst &&
+          spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+        const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
+        if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+          const OpenCLDebugInfo100Instructions ext_inst_key =
+              OpenCLDebugInfo100Instructions(ext_inst_index);
+          switch (ext_inst_key) {
+            case OpenCLDebugInfo100DebugDeclare: {
+              if (block_ == nullptr)  // Inside function but outside blocks
+                function_->AddDebugInstructionInHeader(std::move(spv_inst));
+              else
+                block_->AddInstruction(std::move(spv_inst));
+              break;
+            }
+            case OpenCLDebugInfo100DebugValue: {
+              if (block_ == nullptr)  // Inside function but outside blocks
+                function_->AddDebugInstructionInHeader(std::move(spv_inst));
+              else
+                block_->AddInstruction(std::move(spv_inst));
+              break;
+            }
+            default: {
               Errorf(consumer_, src, loc,
                      "Debug info extension instruction other than DebugScope, "
                      "DebugNoScope, DebugDeclare, and DebugValue found inside "
@@ -158,13 +213,26 @@
                      opcode);
               return false;
             }
-          } else {
-            const DebugInfoInstructions ext_inst_key =
-                DebugInfoInstructions(ext_inst_index);
-            if (ext_inst_key != DebugInfoDebugScope &&
-                ext_inst_key != DebugInfoDebugNoScope &&
-                ext_inst_key != DebugInfoDebugDeclare &&
-                ext_inst_key != DebugInfoDebugValue) {
+          }
+        } else {
+          const DebugInfoInstructions ext_inst_key =
+              DebugInfoInstructions(ext_inst_index);
+          switch (ext_inst_key) {
+            case DebugInfoDebugDeclare: {
+              if (block_ == nullptr)  // Inside function but outside blocks
+                function_->AddDebugInstructionInHeader(std::move(spv_inst));
+              else
+                block_->AddInstruction(std::move(spv_inst));
+              break;
+            }
+            case DebugInfoDebugValue: {
+              if (block_ == nullptr)  // Inside function but outside blocks
+                function_->AddDebugInstructionInHeader(std::move(spv_inst));
+              else
+                block_->AddInstruction(std::move(spv_inst));
+              break;
+            }
+            default: {
               Errorf(consumer_, src, loc,
                      "Debug info extension instruction other than DebugScope, "
                      "DebugNoScope, DebugDeclare, and DebugValue found inside "
@@ -174,7 +242,19 @@
             }
           }
         }
-        block_->AddInstruction(std::move(spv_inst));
+      } else {
+        if (block_ == nullptr) {  // Inside function but outside blocks
+          if (opcode != SpvOpFunctionParameter) {
+            Errorf(consumer_, src, loc,
+                   "Non-OpFunctionParameter (opcode: %d) found inside "
+                   "function but outside basic block",
+                   opcode);
+            return false;
+          }
+          function_->AddParameter(std::move(spv_inst));
+        } else {
+          block_->AddInstruction(std::move(spv_inst));
+        }
       }
     }
   }
diff --git a/third_party/SPIRV-Tools/source/opt/ir_loader.h b/third_party/SPIRV-Tools/source/opt/ir_loader.h
index 940d7b0..5079921 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_loader.h
+++ b/third_party/SPIRV-Tools/source/opt/ir_loader.h
@@ -78,6 +78,9 @@
   std::unique_ptr<BasicBlock> block_;
   // Line related debug instructions accumulated thus far.
   std::vector<Instruction> dbg_line_info_;
+
+  // The last DebugScope information that IrLoader::AddInstruction() handled.
+  DebugScope last_dbg_scope_;
 };
 
 }  // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
index 1921596..0afe798 100644
--- a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
@@ -371,6 +371,7 @@
       "SPV_GOOGLE_hlsl_functionality1",
       "SPV_GOOGLE_user_type",
       "SPV_NV_shader_subgroup_partitioned",
+      "SPV_EXT_demote_to_helper_invocation",
       "SPV_EXT_descriptor_indexing",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_compute_shader_derivatives",
@@ -378,6 +379,8 @@
       "SPV_NV_shading_rate",
       "SPV_NV_mesh_shader",
       "SPV_NV_ray_tracing",
+      "SPV_KHR_ray_tracing",
+      "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
   });
 }
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
index aebbd00..b5435bb 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
@@ -248,6 +248,7 @@
       "SPV_GOOGLE_hlsl_functionality1",
       "SPV_GOOGLE_user_type",
       "SPV_NV_shader_subgroup_partitioned",
+      "SPV_EXT_demote_to_helper_invocation",
       "SPV_EXT_descriptor_indexing",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_compute_shader_derivatives",
@@ -255,6 +256,8 @@
       "SPV_NV_shading_rate",
       "SPV_NV_mesh_shader",
       "SPV_NV_ray_tracing",
+      "SPV_KHR_ray_tracing",
+      "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
   });
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
index d6beeab..4c71ce1 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
@@ -118,6 +118,7 @@
       "SPV_NV_shading_rate",
       "SPV_NV_mesh_shader",
       "SPV_NV_ray_tracing",
+      "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
   });
diff --git a/third_party/SPIRV-Tools/source/opt/module.cpp b/third_party/SPIRV-Tools/source/opt/module.cpp
index 4403894..2959d3d 100644
--- a/third_party/SPIRV-Tools/source/opt/module.cpp
+++ b/third_party/SPIRV-Tools/source/opt/module.cpp
@@ -137,10 +137,27 @@
   binary->push_back(header_.bound);
   binary->push_back(header_.reserved);
 
-  auto write_inst = [binary, skip_nop](const Instruction* i) {
-    if (!(skip_nop && i->IsNop())) i->ToBinaryWithoutAttachedDebugInsts(binary);
+  size_t bound_idx = binary->size() - 2;
+  DebugScope last_scope(kNoDebugScope, kNoInlinedAt);
+  auto write_inst = [binary, skip_nop, &last_scope,
+                     this](const Instruction* i) {
+    if (!(skip_nop && i->IsNop())) {
+      const auto& scope = i->GetDebugScope();
+      if (scope != last_scope) {
+        // Emit DebugScope |scope| to |binary|.
+        auto dbg_inst = ext_inst_debuginfo_.begin();
+        scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(),
+                       dbg_inst->GetSingleWordOperand(2), binary);
+        last_scope = scope;
+      }
+
+      i->ToBinaryWithoutAttachedDebugInsts(binary);
+    }
   };
   ForEachInst(write_inst, true);
+
+  // We create new instructions for DebugScope. The bound must be updated.
+  binary->data()[bound_idx] = header_.bound;
 }
 
 uint32_t Module::ComputeIdBound() const {
diff --git a/third_party/SPIRV-Tools/source/opt/module.h b/third_party/SPIRV-Tools/source/opt/module.h
index fc53d35..2c96f02 100644
--- a/third_party/SPIRV-Tools/source/opt/module.h
+++ b/third_party/SPIRV-Tools/source/opt/module.h
@@ -17,6 +17,7 @@
 
 #include <functional>
 #include <memory>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -48,7 +49,7 @@
   using const_inst_iterator = InstructionList::const_iterator;
 
   // Creates an empty module with zero'd header.
-  Module() : header_({}) {}
+  Module() : header_({}), contains_debug_scope_(false) {}
 
   // Sets the header to the given |header|.
   void SetHeader(const ModuleHeader& header) { header_ = header; }
@@ -118,6 +119,10 @@
   // Appends a function to this module.
   inline void AddFunction(std::unique_ptr<Function> f);
 
+  // Sets |contains_debug_scope_| as true.
+  inline void SetContainsDebugScope();
+  inline bool ContainsDebugScope() { return contains_debug_scope_; }
+
   // Returns a vector of pointers to type-declaration instructions in this
   // module.
   std::vector<Instruction*> GetTypes();
@@ -295,6 +300,9 @@
   // If the module ends with Op*Line instruction, they will not be attached to
   // any instruction.  We record them here, so they will not be lost.
   std::vector<Instruction> trailing_dbg_line_info_;
+
+  // This module contains DebugScope or DebugNoScope.
+  bool contains_debug_scope_;
 };
 
 // Pretty-prints |module| to |str|. Returns |str|.
@@ -356,6 +364,8 @@
   functions_.emplace_back(std::move(f));
 }
 
+inline void Module::SetContainsDebugScope() { contains_debug_scope_ = true; }
+
 inline Module::inst_iterator Module::capability_begin() {
   return capabilities_.begin();
 }
diff --git a/third_party/SPIRV-Tools/source/opt/optimizer.cpp b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
index 241aa75..0a937e8 100644
--- a/third_party/SPIRV-Tools/source/opt/optimizer.cpp
+++ b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
@@ -425,6 +425,8 @@
     RegisterPass(CreateConvertRelaxedToHalfPass());
   } else if (pass_name == "relax-float-ops") {
     RegisterPass(CreateRelaxFloatOpsPass());
+  } else if (pass_name == "inst-debug-printf") {
+    RegisterPass(CreateInstDebugPrintfPass(7, 23));
   } else if (pass_name == "simplify-instructions") {
     RegisterPass(CreateSimplificationPass());
   } else if (pass_name == "ssa-rewrite") {
@@ -567,7 +569,12 @@
   }
 
 #ifndef NDEBUG
-  if (status == opt::Pass::Status::SuccessWithoutChange) {
+  // We do not keep the result id of DebugScope in struct DebugScope.
+  // Instead, we assign random ids for them, which results in sanity
+  // check failures. We want to skip the sanity check when the module
+  // contains DebugScope instructions.
+  if (status == opt::Pass::Status::SuccessWithoutChange &&
+      !context->module()->ContainsDebugScope()) {
     std::vector<uint32_t> optimized_binary_with_nop;
     context->module()->ToBinary(&optimized_binary_with_nop,
                                 /* skip_nop = */ false);
@@ -886,6 +893,12 @@
                                              input_init_enable, version));
 }
 
+Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
+                                               uint32_t shader_id) {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id));
+}
+
 Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
                                                  uint32_t shader_id,
                                                  uint32_t version) {
diff --git a/third_party/SPIRV-Tools/source/opt/passes.h b/third_party/SPIRV-Tools/source/opt/passes.h
index 1a3675c..5b4ab89 100644
--- a/third_party/SPIRV-Tools/source/opt/passes.h
+++ b/third_party/SPIRV-Tools/source/opt/passes.h
@@ -46,6 +46,7 @@
 #include "source/opt/inline_opaque_pass.h"
 #include "source/opt/inst_bindless_check_pass.h"
 #include "source/opt/inst_buff_addr_check_pass.h"
+#include "source/opt/inst_debug_printf_pass.h"
 #include "source/opt/legalize_vector_shuffle_pass.h"
 #include "source/opt/licm_pass.h"
 #include "source/opt/local_access_chain_convert_pass.h"
diff --git a/third_party/SPIRV-Tools/source/opt/reflect.h b/third_party/SPIRV-Tools/source/opt/reflect.h
index 8106442..51d23a7 100644
--- a/third_party/SPIRV-Tools/source/opt/reflect.h
+++ b/third_party/SPIRV-Tools/source/opt/reflect.h
@@ -46,6 +46,8 @@
   return (opcode >= SpvOpTypeVoid && opcode <= SpvOpTypeForwardPointer) ||
          opcode == SpvOpTypePipeStorage || opcode == SpvOpTypeNamedBarrier ||
          opcode == SpvOpTypeAccelerationStructureNV ||
+         opcode == SpvOpTypeAccelerationStructureKHR ||
+         opcode == SpvOpTypeRayQueryProvisionalKHR ||
          opcode == SpvOpTypeCooperativeMatrixNV;
 }
 inline bool IsConstantInst(SpvOp opcode) {
diff --git a/third_party/SPIRV-Tools/source/opt/type_manager.cpp b/third_party/SPIRV-Tools/source/opt/type_manager.cpp
index 166b828..27c7199 100644
--- a/third_party/SPIRV-Tools/source/opt/type_manager.cpp
+++ b/third_party/SPIRV-Tools/source/opt/type_manager.cpp
@@ -862,6 +862,9 @@
                                      inst.GetSingleWordInOperand(2),
                                      inst.GetSingleWordInOperand(3));
       break;
+    case SpvOpTypeRayQueryProvisionalKHR:
+      type = new RayQueryProvisionalKHR();
+      break;
     default:
       SPIRV_UNIMPLEMENTED(consumer_, "unhandled type");
       break;
diff --git a/third_party/SPIRV-Tools/source/opt/types.cpp b/third_party/SPIRV-Tools/source/opt/types.cpp
index 17f8fe9..426d3ea 100644
--- a/third_party/SPIRV-Tools/source/opt/types.cpp
+++ b/third_party/SPIRV-Tools/source/opt/types.cpp
@@ -128,6 +128,7 @@
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(CooperativeMatrixNV);
+    DeclareKindCase(RayQueryProvisionalKHR);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -173,6 +174,7 @@
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(CooperativeMatrixNV);
+    DeclareKindCase(RayQueryProvisionalKHR);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -223,6 +225,7 @@
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(CooperativeMatrixNV);
+    DeclareKindCase(RayQueryProvisionalKHR);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
diff --git a/third_party/SPIRV-Tools/source/opt/types.h b/third_party/SPIRV-Tools/source/opt/types.h
index 69071ea..ebeb476 100644
--- a/third_party/SPIRV-Tools/source/opt/types.h
+++ b/third_party/SPIRV-Tools/source/opt/types.h
@@ -59,6 +59,7 @@
 class NamedBarrier;
 class AccelerationStructureNV;
 class CooperativeMatrixNV;
+class RayQueryProvisionalKHR;
 
 // Abstract class for a SPIR-V type. It has a bunch of As<sublcass>() methods,
 // which is used as a way to probe the actual <subclass>.
@@ -94,7 +95,8 @@
     kPipeStorage,
     kNamedBarrier,
     kAccelerationStructureNV,
-    kCooperativeMatrixNV
+    kCooperativeMatrixNV,
+    kRayQueryProvisionalKHR
   };
 
   Type(Kind k) : kind_(k) {}
@@ -199,6 +201,7 @@
   DeclareCastMethod(NamedBarrier)
   DeclareCastMethod(AccelerationStructureNV)
   DeclareCastMethod(CooperativeMatrixNV)
+  DeclareCastMethod(RayQueryProvisionalKHR)
 #undef DeclareCastMethod
 
  protected:
@@ -659,6 +662,7 @@
 DefineParameterlessType(PipeStorage, pipe_storage);
 DefineParameterlessType(NamedBarrier, named_barrier);
 DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV);
+DefineParameterlessType(RayQueryProvisionalKHR, rayQueryProvisionalKHR);
 #undef DefineParameterlessType
 
 }  // namespace analysis
diff --git a/third_party/SPIRV-Tools/source/val/validate_builtins.cpp b/third_party/SPIRV-Tools/source/val/validate_builtins.cpp
index 7623d49..d86c91e 100644
--- a/third_party/SPIRV-Tools/source/val/validate_builtins.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_builtins.cpp
@@ -2263,8 +2263,11 @@
 spv_result_t BuiltInsValidator::ValidateVertexIdOrInstanceIdAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
-  bool allow_instance_id = _.HasCapability(SpvCapabilityRayTracingNV) &&
-                           label == SpvBuiltInInstanceId;
+  bool allow_instance_id =
+      (_.HasCapability(SpvCapabilityRayTracingNV) ||
+       _.HasCapability(SpvCapabilityRayTracingProvisionalKHR)) &&
+      label == SpvBuiltInInstanceId;
+
   if (spvIsVulkanEnv(_.context()->target_env) && !allow_instance_id) {
     return _.diag(SPV_ERROR_INVALID_DATA, &inst)
            << "Vulkan spec doesn't allow BuiltIn VertexId/InstanceId "
@@ -3085,7 +3088,8 @@
     case SpvBuiltInWorldToObjectNV:
     case SpvBuiltInHitTNV:
     case SpvBuiltInHitKindNV:
-    case SpvBuiltInIncomingRayFlagsNV: {
+    case SpvBuiltInIncomingRayFlagsNV:
+    case SpvBuiltInRayGeometryIndexKHR: {
       // No validation rules (for the moment).
       break;
     }
diff --git a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
index f3019d1..1c279f6 100644
--- a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
@@ -62,6 +62,15 @@
     }
   }
 
+  if (!_.options()->before_hlsl_legalization) {
+    if (type_opcode == SpvOpTypeSampledImage ||
+        (_.HasCapability(SpvCapabilityShader) &&
+         (type_opcode == SpvOpTypeImage || type_opcode == SpvOpTypeSampler))) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Result type cannot be Op" << spvOpcodeString(type_opcode);
+    }
+  }
+
   // Create a uniqued vector of predecessor ids for comparison against
   // incoming values. OpBranchConditional %cond %label %label produces two
   // predecessors in the CFG.
diff --git a/third_party/SPIRV-Tools/source/val/validate_extensions.cpp b/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
index 070cc4c..1e311c1 100644
--- a/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
@@ -14,12 +14,11 @@
 
 // Validates correctness of extension SPIR-V instructions.
 
-#include "source/val/validate.h"
-
 #include <sstream>
 #include <string>
 #include <vector>
 
+#include "OpenCLDebugInfo100.h"
 #include "source/diagnostic.h"
 #include "source/enum_string_mapping.h"
 #include "source/extensions.h"
@@ -28,6 +27,7 @@
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -42,6 +42,144 @@
   return 0;
 }
 
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of an instruction with |expected_opcode|.
+spv_result_t ValidateOperandForDebugInfo(
+    ValidationState_t& _, const std::string& operand_name,
+    SpvOp expected_opcode, const Instruction* inst, uint32_t word_index,
+    const std::function<std::string()>& ext_inst_name) {
+  auto* operand = _.FindDef(inst->word(word_index));
+  if (operand->opcode() != expected_opcode) {
+    spv_opcode_desc desc = nullptr;
+    if (_.grammar().lookupOpcode(expected_opcode, &desc) != SPV_SUCCESS ||
+        !desc) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << ext_inst_name() << ": "
+             << "expected operand " << operand_name << " is invalid";
+    }
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << ext_inst_name() << ": "
+           << "expected operand " << operand_name << " must be a result id of "
+           << "Op" << desc->name;
+  }
+  return SPV_SUCCESS;
+}
+
+#define CHECK_OPERAND(NAME, opcode, index)                                  \
+  do {                                                                      \
+    auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \
+                                              ext_inst_name);               \
+    if (result != SPV_SUCCESS) return result;                               \
+  } while (0)
+
+// True if the operand of a debug info instruction |inst| at |word_index|
+// satisifies |expectation| that is given as a function. Otherwise,
+// returns false.
+bool DoesDebugInfoOperandMatchExpectation(
+    const ValidationState_t& _,
+    const std::function<bool(OpenCLDebugInfo100Instructions)>& expectation,
+    const Instruction* inst, uint32_t word_index) {
+  auto* debug_inst = _.FindDef(inst->word(word_index));
+  if (debug_inst->opcode() != SpvOpExtInst ||
+      debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+      !expectation(OpenCLDebugInfo100Instructions(debug_inst->word(4)))) {
+    return false;
+  }
+  return true;
+}
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of an debug info instruction whose debug instruction type
+// is |expected_debug_inst|.
+spv_result_t ValidateDebugInfoOperand(
+    ValidationState_t& _, const std::string& debug_inst_name,
+    OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst,
+    uint32_t word_index, const std::function<std::string()>& ext_inst_name) {
+  std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
+      [expected_debug_inst](OpenCLDebugInfo100Instructions dbg_inst) {
+        return dbg_inst == expected_debug_inst;
+      };
+  if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+    return SPV_SUCCESS;
+
+  spv_ext_inst_desc desc = nullptr;
+  _.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+                            expected_debug_inst, &desc);
+  if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+                                expected_debug_inst, &desc) != SPV_SUCCESS ||
+      !desc) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << ext_inst_name() << ": "
+           << "expected operand " << debug_inst_name << " is invalid";
+  }
+  return _.diag(SPV_ERROR_INVALID_DATA, inst)
+         << ext_inst_name() << ": "
+         << "expected operand " << debug_inst_name << " must be a result id of "
+         << desc->name;
+}
+
+#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index)                         \
+  do {                                                                         \
+    auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \
+                                           ext_inst_name);                     \
+    if (result != SPV_SUCCESS) return result;                                  \
+  } while (0)
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of an debug info instruction with DebugTypeBasic.
+spv_result_t ValidateOperandBaseType(
+    ValidationState_t& _, const Instruction* inst, uint32_t word_index,
+    const std::function<std::string()>& ext_inst_name) {
+  return ValidateDebugInfoOperand(_, "Base Type",
+                                  OpenCLDebugInfo100DebugTypeBasic, inst,
+                                  word_index, ext_inst_name);
+}
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of a debug lexical scope instruction which is one of
+// DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or
+// DebugTypeComposite.
+spv_result_t ValidateOperandLexicalScope(
+    ValidationState_t& _, const std::string& debug_inst_name,
+    const Instruction* inst, uint32_t word_index,
+    const std::function<std::string()>& ext_inst_name) {
+  std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
+      [](OpenCLDebugInfo100Instructions dbg_inst) {
+        return dbg_inst == OpenCLDebugInfo100DebugCompilationUnit ||
+               dbg_inst == OpenCLDebugInfo100DebugFunction ||
+               dbg_inst == OpenCLDebugInfo100DebugLexicalBlock ||
+               dbg_inst == OpenCLDebugInfo100DebugTypeComposite;
+      };
+  if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+    return SPV_SUCCESS;
+
+  return _.diag(SPV_ERROR_INVALID_DATA, inst)
+         << ext_inst_name() << ": "
+         << "expected operand " << debug_inst_name
+         << " must be a result id of a lexical scope";
+}
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of a debug type instruction (See DebugTypeXXX in
+// "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec.
+spv_result_t ValidateOperandDebugType(
+    ValidationState_t& _, const std::string& debug_inst_name,
+    const Instruction* inst, uint32_t word_index,
+    const std::function<std::string()>& ext_inst_name) {
+  std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
+      [](OpenCLDebugInfo100Instructions dbg_inst) {
+        return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst &&
+               dbg_inst <= OpenCLDebugInfo100DebugTypePtrToMember;
+      };
+  if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+    return SPV_SUCCESS;
+
+  return _.diag(SPV_ERROR_INVALID_DATA, inst)
+         << ext_inst_name() << ": "
+         << "expected operand " << debug_inst_name
+         << " is not a valid debug type";
+}
+
 }  // anonymous namespace
 
 spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
@@ -2028,6 +2166,317 @@
         break;
       }
     }
+  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+    if (!_.IsVoidType(result_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << ext_inst_name() << ": "
+             << "expected result type must be a result id of "
+             << "OpTypeVoid";
+    }
+
+    auto num_words = inst->words().size();
+
+    const OpenCLDebugInfo100Instructions ext_inst_key =
+        OpenCLDebugInfo100Instructions(ext_inst_index);
+    switch (ext_inst_key) {
+      case OpenCLDebugInfo100DebugInfoNone:
+      case OpenCLDebugInfo100DebugNoScope:
+      case OpenCLDebugInfo100DebugOperation:
+        // The binary parser validates the opcode for DebugInfoNone,
+        // DebugNoScope, DebugOperation, and the literal values don't need
+        // further checks.
+        break;
+      case OpenCLDebugInfo100DebugCompilationUnit: {
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        break;
+      }
+      case OpenCLDebugInfo100DebugSource: {
+        CHECK_OPERAND("File", SpvOpString, 5);
+        if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6);
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeBasic: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        CHECK_OPERAND("Size", SpvOpConstant, 6);
+        // "Encoding" param is already validated by the binary parsing stage.
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypePointer:
+      case OpenCLDebugInfo100DebugTypeQualifier: {
+        auto validate_base_type =
+            ValidateOperandBaseType(_, inst, 5, ext_inst_name);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeVector: {
+        auto validate_base_type =
+            ValidateOperandBaseType(_, inst, 5, ext_inst_name);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+
+        uint32_t component_count = inst->word(6);
+        if (!component_count || component_count > 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": Component Count must be positive "
+                 << "integer less than or equal to 4";
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeArray: {
+        auto validate_base_type =
+            ValidateOperandDebugType(_, "Base Type", inst, 5, ext_inst_name);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        for (uint32_t i = 6; i < num_words; ++i) {
+          CHECK_OPERAND("Component Count", SpvOpConstant, i);
+          auto* component_count = _.FindDef(inst->word(i));
+          if (!_.IsIntScalarType(component_count->type_id()) ||
+              !component_count->word(3)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << ext_inst_name() << ": Component Count must be positive "
+                   << "integer";
+          }
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypedef: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_base_type =
+            ValidateOperandBaseType(_, inst, 6, ext_inst_name);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeFunction: {
+        auto* return_type = _.FindDef(inst->word(6));
+        if (return_type->opcode() != SpvOpTypeVoid) {
+          auto validate_return = ValidateOperandDebugType(
+              _, "Return Type", inst, 6, ext_inst_name);
+          if (validate_return != SPV_SUCCESS) return validate_return;
+        }
+        for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
+          auto validate_param = ValidateOperandDebugType(
+              _, "Parameter Types", inst, word_index, ext_inst_name);
+          if (validate_param != SPV_SUCCESS) return validate_param;
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeEnum: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](OpenCLDebugInfo100Instructions dbg_inst) {
+                  return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+                },
+                inst, 6)) {
+          auto validate_underlying_type = ValidateOperandDebugType(
+              _, "Underlying Types", inst, 6, ext_inst_name);
+          if (validate_underlying_type != SPV_SUCCESS)
+            return validate_underlying_type;
+        }
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Size", SpvOpConstant, 11);
+        auto* size = _.FindDef(inst->word(11));
+        if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": expected operand Size is a "
+                 << "positive integer";
+        }
+        for (uint32_t word_index = 13; word_index + 1 < num_words;
+             word_index += 2) {
+          CHECK_OPERAND("Value", SpvOpConstant, word_index);
+          CHECK_OPERAND("Name", SpvOpString, word_index + 1);
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeComposite: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+        CHECK_OPERAND("Size", SpvOpConstant, 12);
+        for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
+          if (!DoesDebugInfoOperandMatchExpectation(
+                  _,
+                  [](OpenCLDebugInfo100Instructions dbg_inst) {
+                    return dbg_inst == OpenCLDebugInfo100DebugTypeMember ||
+                           dbg_inst == OpenCLDebugInfo100DebugFunction ||
+                           dbg_inst == OpenCLDebugInfo100DebugTypeInheritance;
+                  },
+                  inst, word_index)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << ext_inst_name() << ": "
+                   << "expected operand Members "
+                   << "must be DebugTypeMember, DebugFunction, or "
+                      "DebugTypeInheritance";
+          }
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeMember: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10);
+        CHECK_OPERAND("Offset", SpvOpConstant, 11);
+        CHECK_OPERAND("Size", SpvOpConstant, 12);
+        if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14);
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeInheritance: {
+        CHECK_DEBUG_OPERAND("Child", OpenCLDebugInfo100DebugTypeComposite, 5);
+        auto* debug_inst = _.FindDef(inst->word(5));
+        auto composite_type =
+            OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+        if (composite_type != OpenCLDebugInfo100Class &&
+            composite_type != OpenCLDebugInfo100Structure) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": "
+                 << "expected operand Child must be class or struct debug type";
+        }
+        CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 6);
+        debug_inst = _.FindDef(inst->word(6));
+        composite_type =
+            OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+        if (composite_type != OpenCLDebugInfo100Class &&
+            composite_type != OpenCLDebugInfo100Structure) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": "
+                 << "expected operand Parent must be class or struct debug "
+                    "type";
+        }
+        CHECK_OPERAND("Offset", SpvOpConstant, 7);
+        CHECK_OPERAND("Size", SpvOpConstant, 8);
+        break;
+      }
+      case OpenCLDebugInfo100DebugFunction: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+        // TODO: The current OpenCL.100.DebugInfo spec says "Function
+        // is an OpFunction which is described by this instruction.".
+        // However, the function definition can be opted-out e.g.,
+        // inlining. We assume that Function operand can be a
+        // DebugInfoNone, but we must discuss it and update the spec.
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](OpenCLDebugInfo100Instructions dbg_inst) {
+                  return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+                },
+                inst, 14)) {
+          CHECK_OPERAND("Function", SpvOpFunction, 14);
+        }
+        if (num_words == 16) {
+          CHECK_DEBUG_OPERAND("Declaration",
+                              OpenCLDebugInfo100DebugFunctionDeclaration, 15);
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugFunctionDeclaration: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+        break;
+      }
+      case OpenCLDebugInfo100DebugLexicalBlock: {
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 5);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9);
+        break;
+      }
+      case OpenCLDebugInfo100DebugScope: {
+        // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We are
+        // still in spec discussion about what must be "Scope" operand of
+        // DebugScope. Update this code if the conclusion is different.
+        auto validate_scope =
+            ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        if (num_words == 7) {
+          CHECK_DEBUG_OPERAND("Inlined At", OpenCLDebugInfo100DebugInlinedAt,
+                              6);
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugLocalVariable: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        break;
+      }
+      case OpenCLDebugInfo100DebugDeclare: {
+        CHECK_DEBUG_OPERAND("Local Variable",
+                            OpenCLDebugInfo100DebugLocalVariable, 5);
+
+        // TODO: We must discuss DebugDeclare.Variable of OpenCL.100.DebugInfo.
+        // Currently, it says "Variable must be an id of OpVariable instruction
+        // which defines the local variable.", but we want to allow
+        // OpFunctionParameter as well.
+        auto* operand = _.FindDef(inst->word(6));
+        if (operand->opcode() != SpvOpVariable &&
+            operand->opcode() != SpvOpFunctionParameter) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": "
+                 << "expected operand Variable must be a result id of "
+                    "OpVariable or OpFunctionParameter";
+        }
+
+        CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7);
+        break;
+      }
+      case OpenCLDebugInfo100DebugExpression: {
+        for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
+          CHECK_DEBUG_OPERAND("Operation", OpenCLDebugInfo100DebugOperation,
+                              word_index);
+        }
+        break;
+      }
+
+      // TODO: Add validation rules for remaining cases as well.
+      case OpenCLDebugInfo100DebugTypePtrToMember:
+      case OpenCLDebugInfo100DebugTypeTemplate:
+      case OpenCLDebugInfo100DebugTypeTemplateParameter:
+      case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter:
+      case OpenCLDebugInfo100DebugTypeTemplateParameterPack:
+      case OpenCLDebugInfo100DebugGlobalVariable:
+      case OpenCLDebugInfo100DebugLexicalBlockDiscriminator:
+      case OpenCLDebugInfo100DebugInlinedAt:
+      case OpenCLDebugInfo100DebugInlinedVariable:
+      case OpenCLDebugInfo100DebugValue:
+      case OpenCLDebugInfo100DebugMacroDef:
+      case OpenCLDebugInfo100DebugMacroUndef:
+      case OpenCLDebugInfo100DebugImportedEntity:
+        break;
+      case OpenCLDebugInfo100InstructionsMax:
+        assert(0);
+        break;
+    }
   }
 
   return SPV_SUCCESS;
diff --git a/third_party/SPIRV-Tools/source/val/validate_id.cpp b/third_party/SPIRV-Tools/source/val/validate_id.cpp
index c171d31..e1a775a 100644
--- a/third_party/SPIRV-Tools/source/val/validate_id.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_id.cpp
@@ -171,8 +171,8 @@
           const auto opcode = inst->opcode();
           if (spvOpcodeGeneratesType(def->opcode()) &&
               !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
-              !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
-              opcode != SpvOpFunction &&
+              !inst->IsDebugInfo() && !inst->IsNonSemantic() &&
+              !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction &&
               opcode != SpvOpCooperativeMatrixLengthNV &&
               !(opcode == SpvOpSpecConstantOp &&
                 inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
@@ -180,8 +180,8 @@
                    << "Operand " << _.getIdName(operand_word)
                    << " cannot be a type";
           } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) &&
-                     !spvOpcodeIsDebug(opcode) && !inst->IsNonSemantic() &&
-                     !spvOpcodeIsDecoration(opcode) &&
+                     !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() &&
+                     !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
                      !spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi &&
                      opcode != SpvOpExtInst && opcode != SpvOpExtInstImport &&
                      opcode != SpvOpSelectionMerge &&
diff --git a/third_party/SPIRV-Tools/source/val/validate_image.cpp b/third_party/SPIRV-Tools/source/val/validate_image.cpp
index ed960d1..5b77058 100644
--- a/third_party/SPIRV-Tools/source/val/validate_image.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_image.cpp
@@ -847,6 +847,7 @@
     case SpvOpImageSparseSampleDrefExplicitLod:
     case SpvOpImageSparseGather:
     case SpvOpImageSparseDrefGather:
+    case SpvOpCopyObject:
       return true;
     default:
       return false;
diff --git a/third_party/SPIRV-Tools/source/val/validate_memory.cpp b/third_party/SPIRV-Tools/source/val/validate_memory.cpp
index bff8b20..1e1a38d 100644
--- a/third_party/SPIRV-Tools/source/val/validate_memory.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_memory.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2018 Google LLC.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -533,7 +535,9 @@
       if (!IsAllowedTypeOrArrayOfSame(
               _, pointee,
               {SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage,
-               SpvOpTypeAccelerationStructureNV})) {
+               SpvOpTypeAccelerationStructureNV,
+               SpvOpTypeAccelerationStructureKHR,
+               SpvOpTypeRayQueryProvisionalKHR})) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "UniformConstant OpVariable <id> '" << _.getIdName(inst->id())
                << "' has illegal type.\n"
@@ -542,6 +546,8 @@
                << "are used only as handles to refer to opaque resources. Such "
                << "variables must be typed as OpTypeImage, OpTypeSampler, "
                << "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
+                  "OpTypeAccelerationStructureKHR, "
+                  "OpTypeRayQueryProvisionalKHR, "
                << "or an array of one of these types.";
       }
     }
diff --git a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
index 320d828..ea3ebcb 100644
--- a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
@@ -32,6 +32,7 @@
     case SpvScopeSubgroup:
     case SpvScopeInvocation:
     case SpvScopeQueueFamilyKHR:
+    case SpvScopeShaderCallKHR:
       return true;
     case SpvScopeMax:
       break;
@@ -143,6 +144,20 @@
              << spvOpcodeString(opcode)
              << ": in WebGPU environment Execution Scope is limited to "
              << "Workgroup";
+    } else {
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [](SpvExecutionModel model, std::string* message) {
+                if (model != SpvExecutionModelGLCompute) {
+                  if (message) {
+                    *message =
+                        ": in WebGPU environment, Workgroup Execution Scope is "
+                        "limited to GLCompute execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
     }
   }
 
@@ -261,6 +276,22 @@
         }
         break;
     }
+
+    if (value == SpvScopeWorkgroup) {
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [](SpvExecutionModel model, std::string* message) {
+                if (model != SpvExecutionModelGLCompute) {
+                  if (message) {
+                    *message =
+                        ": in WebGPU environment, Workgroup Memory Scope is "
+                        "limited to GLCompute execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+    }
   }
 
   // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
diff --git a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
index 4211ff2..99a78fd 100644
--- a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
@@ -48,11 +48,13 @@
           transformation_composite_construct_test.cpp
           transformation_composite_extract_test.cpp
           transformation_copy_object_test.cpp
+          transformation_equation_instruction_test.cpp
           transformation_function_call_test.cpp
           transformation_load_test.cpp
           transformation_merge_blocks_test.cpp
           transformation_move_block_down_test.cpp
           transformation_outline_function_test.cpp
+          transformation_permute_function_parameters_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
@@ -62,6 +64,8 @@
           transformation_set_selection_control_test.cpp
           transformation_split_block_test.cpp
           transformation_store_test.cpp
+          transformation_swap_commutable_operands_test.cpp
+          transformation_toggle_access_chain_instruction_test.cpp
           transformation_vector_shuffle_test.cpp
           uniform_buffer_element_descriptor_test.cpp)
 
diff --git a/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
index b3f32cd..2c79f12 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
@@ -1173,6 +1173,206 @@
                                         context.get()));
 }
 
+TEST(FactManagerTest, LogicalNotEquationFacts) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %14 = OpLogicalNot %6 %7
+         %15 = OpCopyObject %6 %7
+         %16 = OpCopyObject %6 %14
+         %17 = OpLogicalNot %6 %16
+               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;
+
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(15, {}),
+                                  MakeDataDescriptor(7, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(16, {}),
+                                  MakeDataDescriptor(14, {}), context.get());
+  fact_manager.AddFactIdEquation(14, SpvOpLogicalNot, {7}, context.get());
+  fact_manager.AddFactIdEquation(17, SpvOpLogicalNot, {16}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(7, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(17, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(16, {}), MakeDataDescriptor(14, {}), context.get()));
+}
+
+TEST(FactManagerTest, SignedNegateEquationFacts) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpConstant %6 24
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %14 = OpSNegate %6 %7
+         %15 = OpSNegate %6 %14
+               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;
+
+  fact_manager.AddFactIdEquation(14, SpvOpSNegate, {7}, context.get());
+  fact_manager.AddFactIdEquation(15, SpvOpSNegate, {14}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(7, {}), MakeDataDescriptor(15, {}), context.get()));
+}
+
+TEST(FactManagerTest, AddSubNegateFacts1) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %15 = OpConstant %6 24
+         %16 = OpConstant %6 37
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %14 = OpIAdd %6 %15 %16
+         %17 = OpCopyObject %6 %15
+         %18 = OpCopyObject %6 %16
+         %19 = OpISub %6 %14 %18 ; ==> synonymous(%19, %15)
+         %20 = OpISub %6 %14 %17 ; ==> synonymous(%20, %16)
+         %21 = OpCopyObject %6 %14
+         %22 = OpISub %6 %16 %21
+         %23 = OpCopyObject %6 %22
+         %24 = OpSNegate %6 %23 ; ==> synonymous(%24, %15)
+               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;
+
+  fact_manager.AddFactIdEquation(14, SpvOpIAdd, {15, 16}, context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(17, {}),
+                                  MakeDataDescriptor(15, {}), context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(18, {}),
+                                  MakeDataDescriptor(16, {}), context.get());
+  fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 18}, context.get());
+  fact_manager.AddFactIdEquation(20, SpvOpISub, {14, 17}, context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {}),
+                                  MakeDataDescriptor(14, {}), context.get());
+  fact_manager.AddFactIdEquation(22, SpvOpISub, {16, 21}, context.get());
+  fact_manager.AddFactDataSynonym(MakeDataDescriptor(23, {}),
+                                  MakeDataDescriptor(22, {}), context.get());
+  fact_manager.AddFactIdEquation(24, SpvOpSNegate, {23}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(19, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get()));
+}
+
+TEST(FactManagerTest, AddSubNegateFacts2) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %15 = OpConstant %6 24
+         %16 = OpConstant %6 37
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %14 = OpISub %6 %15 %16
+         %17 = OpIAdd %6 %14 %16 ; ==> synonymous(%17, %15)
+         %18 = OpIAdd %6 %16 %14 ; ==> synonymous(%17, %18, %15)
+         %19 = OpISub %6 %14 %15
+         %20 = OpSNegate %6 %19 ; ==> synonymous(%20, %16)
+         %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15)
+         %22 = OpISub %6 %14 %18
+         %23 = OpSNegate %6 %22 ; ==> synonymous(%23, %16)
+               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;
+
+  fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16}, context.get());
+  fact_manager.AddFactIdEquation(17, SpvOpIAdd, {14, 16}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get()));
+
+  fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 14}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get()));
+
+  fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}, context.get());
+  fact_manager.AddFactIdEquation(20, SpvOpSNegate, {19}, context.get());
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+
+  fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get()));
+
+  fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}, context.get());
+  fact_manager.AddFactIdEquation(23, SpvOpSNegate, {22}, context.get());
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get()));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
index f444695..b91393e 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
@@ -1515,6 +1515,44 @@
                OpFunctionEnd
   )";
 
+// The SPIR-V comes from the following GLSL:
+//
+// #version 310 es
+// precision highp float;
+//
+// layout(location = 0) out vec4 color;
+//
+// void main()
+// {
+//   color = vec4(1.0, 0.0, 0.0, 1.0);
+// }
+
+const std::string kTestShader5 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "color"
+               OpDecorate %9 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 0
+         %12 = OpConstantComposite %7 %10 %11 %11 %10
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpStore %9 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
 void AddConstantUniformFact(protobufs::FactSequence* facts,
                             uint32_t descriptor_set, uint32_t binding,
                             std::vector<uint32_t>&& indices, uint32_t value) {
@@ -1552,8 +1590,8 @@
   ASSERT_TRUE(t.Validate(binary_in));
 
   std::vector<fuzzerutil::ModuleSupplier> donor_suppliers;
-  for (auto donor :
-       {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4}) {
+  for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4,
+                     &kTestShader5}) {
     donor_suppliers.emplace_back([donor]() {
       return BuildModule(env, kConsoleMessageConsumer, *donor,
                          kFuzzAssembleOption);
@@ -1636,6 +1674,13 @@
   RunFuzzerAndReplayer(kTestShader4, facts, 14, kNumFuzzerRuns);
 }
 
+TEST(FuzzerReplayerTest, Miscellaneous5) {
+  // Do some fuzzer runs, starting from an initial seed of 29 (seed value chosen
+  // arbitrarily).
+  RunFuzzerAndReplayer(kTestShader5, protobufs::FactSequence(), 29,
+                       kNumFuzzerRuns);
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
new file mode 100644
index 0000000..81d849b
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
@@ -0,0 +1,462 @@
+// Copyright (c) 2020 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_equation_instruction.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationEquationInstructionTest, SignedNegate) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpConstant %6 24
+         %40 = OpTypeBool
+         %41 = OpConstantTrue %40
+         %20 = OpUndef %6
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %30 = OpCopyObject %6 %7
+               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;
+
+  protobufs::InstructionDescriptor return_instruction =
+      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  // Bad: id already in use.
+  ASSERT_FALSE(TransformationEquationInstruction(7, SpvOpSNegate, {7},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: identified instruction does not exist.
+  ASSERT_FALSE(
+      TransformationEquationInstruction(
+          14, SpvOpSNegate, {7}, MakeInstructionDescriptor(13, SpvOpLoad, 0))
+          .IsApplicable(context.get(), fact_manager));
+
+  // Bad: id 100 does not exist
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {100},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: id 20 is an OpUndef
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {20},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: id 30 is not available right before its definition
+  ASSERT_FALSE(TransformationEquationInstruction(
+                   14, SpvOpSNegate, {30},
+                   MakeInstructionDescriptor(30, SpvOpCopyObject, 0))
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: too many arguments to OpSNegate.
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {7, 7},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: 40 is a type id.
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {40},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: wrong type of argument to OpSNegate.
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {41},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  auto transformation1 = TransformationEquationInstruction(
+      14, SpvOpSNegate, {7}, return_instruction);
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation2 = TransformationEquationInstruction(
+      15, SpvOpSNegate, {14}, return_instruction);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpConstant %6 24
+         %40 = OpTypeBool
+         %41 = OpConstantTrue %40
+         %20 = OpUndef %6
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %30 = OpCopyObject %6 %7
+         %14 = OpSNegate %6 %7
+         %15 = OpSNegate %6 %14
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, LogicalNot) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+         %20 = OpTypeInt 32 0
+         %21 = OpConstant %20 5
+         %12 = OpFunction %2 None %3
+         %13 = 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;
+
+  protobufs::InstructionDescriptor return_instruction =
+      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  // Bad: too few arguments to OpLogicalNot.
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: 6 is a type id.
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {6},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Bad: wrong type of argument to OpLogicalNot.
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {21},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  auto transformation1 = TransformationEquationInstruction(
+      14, SpvOpLogicalNot, {7}, return_instruction);
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation2 = TransformationEquationInstruction(
+      15, SpvOpLogicalNot, {14}, return_instruction);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6
+         %20 = OpTypeInt 32 0
+         %21 = OpConstant %20 5
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %14 = OpLogicalNot %6 %7
+         %15 = OpLogicalNot %6 %14
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, AddSubNegate1) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %30 = OpTypeVector %6 3
+         %15 = OpConstant %6 24
+         %16 = OpConstant %6 37
+         %31 = OpConstantComposite %30 %15 %16 %15
+         %33 = OpTypeBool
+         %32 = OpConstantTrue %33
+         %12 = OpFunction %2 None %3
+         %13 = 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;
+
+  protobufs::InstructionDescriptor return_instruction =
+      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  // Bad: too many arguments to OpIAdd.
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 16, 16},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+  // Bad: boolean argument to OpIAdd.
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 32},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+  // Bad: type as argument to OpIAdd.
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {33, 16},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+  // Bad: arguments of mismatched widths
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 31},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+  // Bad: arguments of mismatched widths
+  ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {31, 15},
+                                                 return_instruction)
+                   .IsApplicable(context.get(), fact_manager));
+
+  auto transformation1 = TransformationEquationInstruction(
+      14, SpvOpIAdd, {15, 16}, return_instruction);
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation2 = TransformationEquationInstruction(
+      19, SpvOpISub, {14, 16}, return_instruction);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}), context.get()));
+
+  auto transformation3 = TransformationEquationInstruction(
+      20, SpvOpISub, {14, 15}, return_instruction);
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+
+  auto transformation4 = TransformationEquationInstruction(
+      22, SpvOpISub, {16, 14}, return_instruction);
+  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation5 = TransformationEquationInstruction(
+      24, SpvOpSNegate, {22}, return_instruction);
+  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
+  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %30 = OpTypeVector %6 3
+         %15 = OpConstant %6 24
+         %16 = OpConstant %6 37
+         %31 = OpConstantComposite %30 %15 %16 %15
+         %33 = OpTypeBool
+         %32 = OpConstantTrue %33
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %14 = OpIAdd %6 %15 %16
+         %19 = OpISub %6 %14 %16 ; ==> synonymous(%19, %15)
+         %20 = OpISub %6 %14 %15 ; ==> synonymous(%20, %16)
+         %22 = OpISub %6 %16 %14
+         %24 = OpSNegate %6 %22 ; ==> synonymous(%24, %15)
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, AddSubNegate2) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %15 = OpConstant %6 24
+         %16 = OpConstant %6 37
+         %12 = OpFunction %2 None %3
+         %13 = 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;
+
+  protobufs::InstructionDescriptor return_instruction =
+      MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+  auto transformation1 = TransformationEquationInstruction(
+      14, SpvOpISub, {15, 16}, return_instruction);
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation2 = TransformationEquationInstruction(
+      17, SpvOpIAdd, {14, 16}, return_instruction);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get()));
+
+  auto transformation3 = TransformationEquationInstruction(
+      18, SpvOpIAdd, {16, 14}, return_instruction);
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get()));
+
+  auto transformation4 = TransformationEquationInstruction(
+      19, SpvOpISub, {14, 15}, return_instruction);
+  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+  transformation4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation5 = TransformationEquationInstruction(
+      20, SpvOpSNegate, {19}, return_instruction);
+  ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
+  transformation5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+
+  auto transformation6 = TransformationEquationInstruction(
+      21, SpvOpISub, {14, 19}, return_instruction);
+  ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
+  transformation6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get()));
+
+  auto transformation7 = TransformationEquationInstruction(
+      22, SpvOpISub, {14, 18}, return_instruction);
+  ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
+  transformation7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  auto transformation8 = TransformationEquationInstruction(
+      23, SpvOpSNegate, {22}, return_instruction);
+  ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager));
+  transformation8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(fact_manager.IsSynonymous(
+      MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get()));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %12 "main"
+               OpExecutionMode %12 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %15 = OpConstant %6 24
+         %16 = OpConstant %6 37
+         %12 = OpFunction %2 None %3
+         %13 = OpLabel
+         %14 = OpISub %6 %15 %16
+         %17 = OpIAdd %6 %14 %16 ; ==> synonymous(%17, %15)
+         %18 = OpIAdd %6 %16 %14 ; ==> synonymous(%17, %18, %15)
+         %19 = OpISub %6 %14 %15
+         %20 = OpSNegate %6 %19 ; ==> synonymous(%20, %16)
+         %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15)
+         %22 = OpISub %6 %14 %18
+         %23 = OpSNegate %6 %22 ; ==> synonymous(%23, %16)
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
new file mode 100644
index 0000000..1af4699
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
@@ -0,0 +1,430 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_permute_function_parameters.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %72 %74
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %12 "g(f1;f1;"
+               OpName %10 "x"
+               OpName %11 "y"
+               OpName %22 "f(f1;i1;vf2;"
+               OpName %19 "x"
+               OpName %20 "y"
+               OpName %21 "z"
+               OpName %28 "cond(i1;f1;"
+               OpName %26 "a"
+               OpName %27 "b"
+               OpName %53 "param"
+               OpName %54 "param"
+               OpName %66 "param"
+               OpName %67 "param"
+               OpName %72 "color"
+               OpName %74 "gl_FragCoord"
+               OpName %75 "param"
+               OpName %79 "param"
+               OpName %85 "param"
+               OpName %86 "param"
+               OpName %91 "param"
+               OpName %92 "param"
+               OpName %93 "param"
+               OpName %99 "param"
+               OpName %100 "param"
+               OpName %101 "param"
+               OpDecorate %20 RelaxedPrecision
+               OpDecorate %26 RelaxedPrecision
+               OpDecorate %47 RelaxedPrecision
+               OpDecorate %58 RelaxedPrecision
+               OpDecorate %72 Location 0
+               OpDecorate %74 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeVector %6 4
+          %9 = OpTypeFunction %8 %7 %7
+         %14 = OpTypeInt 32 1
+         %15 = OpTypePointer Function %14
+         %16 = OpTypeVector %6 2
+         %17 = OpTypePointer Function %16
+         %18 = OpTypeFunction %8 %7 %15 %17
+         %24 = OpTypeBool
+         %25 = OpTypeFunction %24 %15 %7
+         %105 = OpTypeFunction %24 %7 %15    ; predefined type for %28
+         %31 = OpConstant %6 255
+         %33 = OpConstant %6 0
+         %34 = OpConstant %6 1
+         %42 = OpTypeInt 32 0
+         %43 = OpConstant %42 0
+         %49 = OpConstant %42 1
+         %64 = OpConstant %14 4
+         %65 = OpConstant %6 5
+         %71 = OpTypePointer Output %8
+         %72 = OpVariable %71 Output
+         %73 = OpTypePointer Input %8
+         %74 = OpVariable %73 Input
+         %76 = OpTypePointer Input %6
+         %84 = OpConstant %14 5
+         %90 = OpConstant %6 3
+         %98 = OpConstant %6 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %66 = OpVariable %15 Function
+         %67 = OpVariable %7 Function
+         %75 = OpVariable %7 Function
+         %79 = OpVariable %7 Function
+         %85 = OpVariable %15 Function
+         %86 = OpVariable %7 Function
+         %91 = OpVariable %7 Function
+         %92 = OpVariable %15 Function
+         %93 = OpVariable %17 Function
+         %99 = OpVariable %7 Function
+        %100 = OpVariable %15 Function
+        %101 = OpVariable %17 Function
+               OpStore %66 %64
+               OpStore %67 %65
+         %68 = OpFunctionCall %24 %28 %66 %67
+               OpSelectionMerge %70 None
+               OpBranchConditional %68 %69 %83
+         %69 = OpLabel
+         %77 = OpAccessChain %76 %74 %43
+         %78 = OpLoad %6 %77
+               OpStore %75 %78
+         %80 = OpAccessChain %76 %74 %49
+         %81 = OpLoad %6 %80
+               OpStore %79 %81
+         %82 = OpFunctionCall %8 %12 %75 %79
+               OpStore %72 %82
+               OpBranch %70
+         %83 = OpLabel
+               OpStore %85 %84
+               OpStore %86 %65
+         %87 = OpFunctionCall %24 %28 %85 %86
+               OpSelectionMerge %89 None
+               OpBranchConditional %87 %88 %97
+         %88 = OpLabel
+               OpStore %91 %90
+               OpStore %92 %64
+         %94 = OpLoad %8 %74
+         %95 = OpVectorShuffle %16 %94 %94 0 1
+               OpStore %93 %95
+         %96 = OpFunctionCall %8 %22 %91 %92 %93
+               OpStore %72 %96
+               OpBranch %89
+         %97 = OpLabel
+               OpStore %99 %98
+               OpStore %100 %84
+        %102 = OpLoad %8 %74
+        %103 = OpVectorShuffle %16 %102 %102 0 1
+               OpStore %101 %103
+        %104 = OpFunctionCall %8 %22 %99 %100 %101
+               OpStore %72 %104
+               OpBranch %89
+         %89 = OpLabel
+               OpBranch %70
+         %70 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %8 None %9
+         %10 = OpFunctionParameter %7
+         %11 = OpFunctionParameter %7
+         %13 = OpLabel
+         %30 = OpLoad %6 %10
+         %32 = OpFDiv %6 %30 %31
+         %35 = OpLoad %6 %11
+         %36 = OpFDiv %6 %35 %31
+         %37 = OpFSub %6 %34 %36
+         %38 = OpCompositeConstruct %8 %32 %33 %37 %34
+               OpReturnValue %38
+               OpFunctionEnd
+         %22 = OpFunction %8 None %18
+         %19 = OpFunctionParameter %7
+         %20 = OpFunctionParameter %15
+         %21 = OpFunctionParameter %17
+         %23 = OpLabel
+         %53 = OpVariable %7 Function
+         %54 = OpVariable %7 Function
+         %41 = OpLoad %6 %19
+         %44 = OpAccessChain %7 %21 %43
+         %45 = OpLoad %6 %44
+         %46 = OpFAdd %6 %41 %45
+         %47 = OpLoad %14 %20
+         %48 = OpConvertSToF %6 %47
+         %50 = OpAccessChain %7 %21 %49
+         %51 = OpLoad %6 %50
+         %52 = OpFAdd %6 %48 %51
+               OpStore %53 %46
+               OpStore %54 %52
+         %55 = OpFunctionCall %8 %12 %53 %54
+               OpReturnValue %55
+               OpFunctionEnd
+         %28 = OpFunction %24 None %25
+         %26 = OpFunctionParameter %15
+         %27 = OpFunctionParameter %7
+         %29 = OpLabel
+         %58 = OpLoad %14 %26
+         %59 = OpConvertSToF %6 %58
+         %60 = OpLoad %6 %27
+         %61 = OpFOrdLessThan %24 %59 %60
+               OpReturnValue %61
+               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;
+
+  // Can't permute main function
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable(
+      context.get(), fact_manager));
+
+  // Can't permute invalid instruction
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Permutation has too many values
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Permutation has too few values
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Permutation has invalid values
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Type id is not an OpTypeFunction instruction
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Type id has incorrect number of operands
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // OpTypeFunction has operands out of order
+  ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0})
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Successful transformations
+  {
+    // Function has two operands of the same type:
+    // initial OpTypeFunction should be enough
+    TransformationPermuteFunctionParameters transformation(12, 9, {1, 0});
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+  {
+    TransformationPermuteFunctionParameters transformation(28, 105, {1, 0});
+    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" %72 %74
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %12 "g(f1;f1;"
+               OpName %10 "x"
+               OpName %11 "y"
+               OpName %22 "f(f1;i1;vf2;"
+               OpName %19 "x"
+               OpName %20 "y"
+               OpName %21 "z"
+               OpName %28 "cond(i1;f1;"
+               OpName %26 "a"
+               OpName %27 "b"
+               OpName %53 "param"
+               OpName %54 "param"
+               OpName %66 "param"
+               OpName %67 "param"
+               OpName %72 "color"
+               OpName %74 "gl_FragCoord"
+               OpName %75 "param"
+               OpName %79 "param"
+               OpName %85 "param"
+               OpName %86 "param"
+               OpName %91 "param"
+               OpName %92 "param"
+               OpName %93 "param"
+               OpName %99 "param"
+               OpName %100 "param"
+               OpName %101 "param"
+               OpDecorate %20 RelaxedPrecision
+               OpDecorate %26 RelaxedPrecision
+               OpDecorate %47 RelaxedPrecision
+               OpDecorate %58 RelaxedPrecision
+               OpDecorate %72 Location 0
+               OpDecorate %74 BuiltIn FragCoord
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeVector %6 4
+          %9 = OpTypeFunction %8 %7 %7
+         %14 = OpTypeInt 32 1
+         %15 = OpTypePointer Function %14
+         %16 = OpTypeVector %6 2
+         %17 = OpTypePointer Function %16
+         %18 = OpTypeFunction %8 %7 %15 %17
+         %24 = OpTypeBool
+         %25 = OpTypeFunction %24 %15 %7
+         %105 = OpTypeFunction %24 %7 %15    ; predefined type for %28
+         %31 = OpConstant %6 255
+         %33 = OpConstant %6 0
+         %34 = OpConstant %6 1
+         %42 = OpTypeInt 32 0
+         %43 = OpConstant %42 0
+         %49 = OpConstant %42 1
+         %64 = OpConstant %14 4
+         %65 = OpConstant %6 5
+         %71 = OpTypePointer Output %8
+         %72 = OpVariable %71 Output
+         %73 = OpTypePointer Input %8
+         %74 = OpVariable %73 Input
+         %76 = OpTypePointer Input %6
+         %84 = OpConstant %14 5
+         %90 = OpConstant %6 3
+         %98 = OpConstant %6 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %66 = OpVariable %15 Function
+         %67 = OpVariable %7 Function
+         %75 = OpVariable %7 Function
+         %79 = OpVariable %7 Function
+         %85 = OpVariable %15 Function
+         %86 = OpVariable %7 Function
+         %91 = OpVariable %7 Function
+         %92 = OpVariable %15 Function
+         %93 = OpVariable %17 Function
+         %99 = OpVariable %7 Function
+        %100 = OpVariable %15 Function
+        %101 = OpVariable %17 Function
+               OpStore %66 %64
+               OpStore %67 %65
+         %68 = OpFunctionCall %24 %28 %67 %66
+               OpSelectionMerge %70 None
+               OpBranchConditional %68 %69 %83
+         %69 = OpLabel
+         %77 = OpAccessChain %76 %74 %43
+         %78 = OpLoad %6 %77
+               OpStore %75 %78
+         %80 = OpAccessChain %76 %74 %49
+         %81 = OpLoad %6 %80
+               OpStore %79 %81
+         %82 = OpFunctionCall %8 %12 %79 %75
+               OpStore %72 %82
+               OpBranch %70
+         %83 = OpLabel
+               OpStore %85 %84
+               OpStore %86 %65
+         %87 = OpFunctionCall %24 %28 %86 %85
+               OpSelectionMerge %89 None
+               OpBranchConditional %87 %88 %97
+         %88 = OpLabel
+               OpStore %91 %90
+               OpStore %92 %64
+         %94 = OpLoad %8 %74
+         %95 = OpVectorShuffle %16 %94 %94 0 1
+               OpStore %93 %95
+         %96 = OpFunctionCall %8 %22 %91 %92 %93
+               OpStore %72 %96
+               OpBranch %89
+         %97 = OpLabel
+               OpStore %99 %98
+               OpStore %100 %84
+        %102 = OpLoad %8 %74
+        %103 = OpVectorShuffle %16 %102 %102 0 1
+               OpStore %101 %103
+        %104 = OpFunctionCall %8 %22 %99 %100 %101
+               OpStore %72 %104
+               OpBranch %89
+         %89 = OpLabel
+               OpBranch %70
+         %70 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %12 = OpFunction %8 None %9
+         %11 = OpFunctionParameter %7
+         %10 = OpFunctionParameter %7
+         %13 = OpLabel
+         %30 = OpLoad %6 %10
+         %32 = OpFDiv %6 %30 %31
+         %35 = OpLoad %6 %11
+         %36 = OpFDiv %6 %35 %31
+         %37 = OpFSub %6 %34 %36
+         %38 = OpCompositeConstruct %8 %32 %33 %37 %34
+               OpReturnValue %38
+               OpFunctionEnd
+         %22 = OpFunction %8 None %18
+         %19 = OpFunctionParameter %7
+         %20 = OpFunctionParameter %15
+         %21 = OpFunctionParameter %17
+         %23 = OpLabel
+         %53 = OpVariable %7 Function
+         %54 = OpVariable %7 Function
+         %41 = OpLoad %6 %19
+         %44 = OpAccessChain %7 %21 %43
+         %45 = OpLoad %6 %44
+         %46 = OpFAdd %6 %41 %45
+         %47 = OpLoad %14 %20
+         %48 = OpConvertSToF %6 %47
+         %50 = OpAccessChain %7 %21 %49
+         %51 = OpLoad %6 %50
+         %52 = OpFAdd %6 %48 %51
+               OpStore %53 %46
+               OpStore %54 %52
+         %55 = OpFunctionCall %8 %12 %54 %53
+               OpReturnValue %55
+               OpFunctionEnd
+         %28 = OpFunction %24 None %105
+         %27 = OpFunctionParameter %7
+         %26 = OpFunctionParameter %15
+         %29 = OpLabel
+         %58 = OpLoad %14 %26
+         %59 = OpConvertSToF %6 %58
+         %60 = OpLoad %6 %27
+         %61 = OpFOrdLessThan %24 %59 %60
+               OpReturnValue %61
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp
new file mode 100644
index 0000000..f0591cf
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp
@@ -0,0 +1,427 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_swap_commutable_operands.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSwapCommutableOperandsTest, IsApplicableTest) {
+  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 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %6 2
+         %14 = OpConstantComposite %9 %12 %13
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %6 0
+         %29 = OpTypeFloat 32
+         %30 = OpTypeArray %29 %8
+         %31 = OpTypePointer Function %30
+         %33 = OpConstant %29 1
+         %34 = OpConstant %29 2
+         %35 = OpConstantComposite %30 %33 %34
+         %36 = OpTypePointer Function %29
+         %49 = OpTypeVector %29 3
+         %50 = OpTypeArray %49 %8
+         %51 = OpTypePointer Function %50
+         %53 = OpConstant %29 3
+         %54 = OpConstantComposite %49 %33 %34 %53
+         %55 = OpConstant %29 4
+         %56 = OpConstant %29 5
+         %57 = OpConstant %29 6
+         %58 = OpConstantComposite %49 %55 %56 %57
+         %59 = OpConstantComposite %50 %54 %58
+         %61 = OpTypePointer Function %49
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %16 = OpVariable %15 Function
+         %23 = OpVariable %15 Function
+         %32 = OpVariable %31 Function
+         %37 = OpVariable %36 Function
+         %43 = OpVariable %36 Function
+         %52 = OpVariable %51 Function
+         %60 = OpVariable %36 Function
+               OpStore %11 %14
+         %18 = OpAccessChain %15 %11 %17
+         %19 = OpLoad %6 %18
+         %20 = OpAccessChain %15 %11 %12
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %19 %21
+               OpStore %16 %22
+         %24 = OpAccessChain %15 %11 %17
+         %25 = OpLoad %6 %24
+         %26 = OpAccessChain %15 %11 %12
+         %27 = OpLoad %6 %26
+         %28 = OpIMul %6 %25 %27
+               OpStore %23 %28
+               OpStore %32 %35
+         %38 = OpAccessChain %36 %32 %17
+         %39 = OpLoad %29 %38
+         %40 = OpAccessChain %36 %32 %12
+         %41 = OpLoad %29 %40
+         %42 = OpFAdd %29 %39 %41
+               OpStore %37 %42
+         %44 = OpAccessChain %36 %32 %17
+         %45 = OpLoad %29 %44
+         %46 = OpAccessChain %36 %32 %12
+         %47 = OpLoad %29 %46
+         %48 = OpFMul %29 %45 %47
+               OpStore %43 %48
+               OpStore %52 %59
+         %62 = OpAccessChain %61 %52 %17
+         %63 = OpLoad %49 %62
+         %64 = OpAccessChain %61 %52 %12
+         %65 = OpLoad %49 %64
+         %66 = OpDot %29 %63 %65
+               OpStore %60 %66
+               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 factManager;
+
+  // Tests existing commutative instructions
+  auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0);
+  auto transformation =
+      TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+  // Tests existing non-commutative instructions
+  instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(8, SpvOpConstant, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(11, SpvOpVariable, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(14, SpvOpConstantComposite, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  // Tests the base instruction id not existing
+  instructionDescriptor = MakeInstructionDescriptor(67, SpvOpIAddCarry, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(68, SpvOpIEqual, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(69, SpvOpINotEqual, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(70, SpvOpFOrdEqual, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(71, SpvOpPtrEqual, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  // Tests there being no instruction with the desired opcode after the base
+  // instruction id
+  instructionDescriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(38, SpvOpIMul, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(45, SpvOpFAdd, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(66, SpvOpFMul, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  // Tests there being an instruction with the desired opcode after the base
+  // instruction id, but the skip count associated with the instruction
+  // descriptor being so high.
+  instructionDescriptor = MakeInstructionDescriptor(11, SpvOpIAdd, 100);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(16, SpvOpIMul, 100);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(23, SpvOpFAdd, 100);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(32, SpvOpFMul, 100);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(37, SpvOpDot, 100);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+}
+
+TEST(TransformationSwapCommutableOperandsTest, ApplyTest) {
+  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 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %6 2
+         %14 = OpConstantComposite %9 %12 %13
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %6 0
+         %29 = OpTypeFloat 32
+         %30 = OpTypeArray %29 %8
+         %31 = OpTypePointer Function %30
+         %33 = OpConstant %29 1
+         %34 = OpConstant %29 2
+         %35 = OpConstantComposite %30 %33 %34
+         %36 = OpTypePointer Function %29
+         %49 = OpTypeVector %29 3
+         %50 = OpTypeArray %49 %8
+         %51 = OpTypePointer Function %50
+         %53 = OpConstant %29 3
+         %54 = OpConstantComposite %49 %33 %34 %53
+         %55 = OpConstant %29 4
+         %56 = OpConstant %29 5
+         %57 = OpConstant %29 6
+         %58 = OpConstantComposite %49 %55 %56 %57
+         %59 = OpConstantComposite %50 %54 %58
+         %61 = OpTypePointer Function %49
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %16 = OpVariable %15 Function
+         %23 = OpVariable %15 Function
+         %32 = OpVariable %31 Function
+         %37 = OpVariable %36 Function
+         %43 = OpVariable %36 Function
+         %52 = OpVariable %51 Function
+         %60 = OpVariable %36 Function
+               OpStore %11 %14
+         %18 = OpAccessChain %15 %11 %17
+         %19 = OpLoad %6 %18
+         %20 = OpAccessChain %15 %11 %12
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %19 %21
+               OpStore %16 %22
+         %24 = OpAccessChain %15 %11 %17
+         %25 = OpLoad %6 %24
+         %26 = OpAccessChain %15 %11 %12
+         %27 = OpLoad %6 %26
+         %28 = OpIMul %6 %25 %27
+               OpStore %23 %28
+               OpStore %32 %35
+         %38 = OpAccessChain %36 %32 %17
+         %39 = OpLoad %29 %38
+         %40 = OpAccessChain %36 %32 %12
+         %41 = OpLoad %29 %40
+         %42 = OpFAdd %29 %39 %41
+               OpStore %37 %42
+         %44 = OpAccessChain %36 %32 %17
+         %45 = OpLoad %29 %44
+         %46 = OpAccessChain %36 %32 %12
+         %47 = OpLoad %29 %46
+         %48 = OpFMul %29 %45 %47
+               OpStore %43 %48
+               OpStore %52 %59
+         %62 = OpAccessChain %61 %52 %17
+         %63 = OpLoad %49 %62
+         %64 = OpAccessChain %61 %52 %12
+         %65 = OpLoad %49 %64
+         %66 = OpDot %29 %63 %65
+               OpStore %60 %66
+               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 factManager;
+
+  auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0);
+  auto transformation =
+      TransformationSwapCommutableOperands(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0);
+  transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  std::string variantShader = 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 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %6 2
+         %14 = OpConstantComposite %9 %12 %13
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %6 0
+         %29 = OpTypeFloat 32
+         %30 = OpTypeArray %29 %8
+         %31 = OpTypePointer Function %30
+         %33 = OpConstant %29 1
+         %34 = OpConstant %29 2
+         %35 = OpConstantComposite %30 %33 %34
+         %36 = OpTypePointer Function %29
+         %49 = OpTypeVector %29 3
+         %50 = OpTypeArray %49 %8
+         %51 = OpTypePointer Function %50
+         %53 = OpConstant %29 3
+         %54 = OpConstantComposite %49 %33 %34 %53
+         %55 = OpConstant %29 4
+         %56 = OpConstant %29 5
+         %57 = OpConstant %29 6
+         %58 = OpConstantComposite %49 %55 %56 %57
+         %59 = OpConstantComposite %50 %54 %58
+         %61 = OpTypePointer Function %49
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %16 = OpVariable %15 Function
+         %23 = OpVariable %15 Function
+         %32 = OpVariable %31 Function
+         %37 = OpVariable %36 Function
+         %43 = OpVariable %36 Function
+         %52 = OpVariable %51 Function
+         %60 = OpVariable %36 Function
+               OpStore %11 %14
+         %18 = OpAccessChain %15 %11 %17
+         %19 = OpLoad %6 %18
+         %20 = OpAccessChain %15 %11 %12
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %21 %19
+               OpStore %16 %22
+         %24 = OpAccessChain %15 %11 %17
+         %25 = OpLoad %6 %24
+         %26 = OpAccessChain %15 %11 %12
+         %27 = OpLoad %6 %26
+         %28 = OpIMul %6 %27 %25
+               OpStore %23 %28
+               OpStore %32 %35
+         %38 = OpAccessChain %36 %32 %17
+         %39 = OpLoad %29 %38
+         %40 = OpAccessChain %36 %32 %12
+         %41 = OpLoad %29 %40
+         %42 = OpFAdd %29 %41 %39
+               OpStore %37 %42
+         %44 = OpAccessChain %36 %32 %17
+         %45 = OpLoad %29 %44
+         %46 = OpAccessChain %36 %32 %12
+         %47 = OpLoad %29 %46
+         %48 = OpFMul %29 %47 %45
+               OpStore %43 %48
+               OpStore %52 %59
+         %62 = OpAccessChain %61 %52 %17
+         %63 = OpLoad %49 %62
+         %64 = OpAccessChain %61 %52 %12
+         %65 = OpLoad %49 %64
+         %66 = OpDot %29 %65 %63
+               OpStore %60 %66
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, variantShader, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
new file mode 100644
index 0000000..98e0a64
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
@@ -0,0 +1,413 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_toggle_access_chain_instruction.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationToggleAccessChainInstructionTest, IsApplicableTest) {
+  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 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %6 2
+         %14 = OpConstantComposite %9 %12 %13
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %6 0
+         %29 = OpTypeFloat 32
+         %30 = OpTypeArray %29 %8
+         %31 = OpTypePointer Function %30
+         %33 = OpConstant %29 1
+         %34 = OpConstant %29 2
+         %35 = OpConstantComposite %30 %33 %34
+         %36 = OpTypePointer Function %29
+         %49 = OpTypeVector %29 3
+         %50 = OpTypeArray %49 %8
+         %51 = OpTypePointer Function %50
+         %53 = OpConstant %29 3
+         %54 = OpConstantComposite %49 %33 %34 %53
+         %55 = OpConstant %29 4
+         %56 = OpConstant %29 5
+         %57 = OpConstant %29 6
+         %58 = OpConstantComposite %49 %55 %56 %57
+         %59 = OpConstantComposite %50 %54 %58
+         %61 = OpTypePointer Function %49
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %16 = OpVariable %15 Function
+         %23 = OpVariable %15 Function
+         %32 = OpVariable %31 Function
+         %37 = OpVariable %36 Function
+         %43 = OpVariable %36 Function
+         %52 = OpVariable %51 Function
+         %60 = OpVariable %36 Function
+               OpStore %11 %14
+         %18 = OpAccessChain %15 %11 %17
+         %19 = OpLoad %6 %18
+         %20 = OpInBoundsAccessChain %15 %11 %12
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %19 %21
+               OpStore %16 %22
+         %24 = OpAccessChain %15 %11 %17
+         %25 = OpLoad %6 %24
+         %26 = OpInBoundsAccessChain %15 %11 %12
+         %27 = OpLoad %6 %26
+         %28 = OpIMul %6 %25 %27
+               OpStore %23 %28
+               OpStore %32 %35
+         %38 = OpAccessChain %36 %32 %17
+         %39 = OpLoad %29 %38
+         %40 = OpAccessChain %36 %32 %12
+         %41 = OpLoad %29 %40
+         %42 = OpFAdd %29 %39 %41
+               OpStore %37 %42
+         %44 = OpAccessChain %36 %32 %17
+         %45 = OpLoad %29 %44
+         %46 = OpAccessChain %36 %32 %12
+         %47 = OpLoad %29 %46
+         %48 = OpFMul %29 %45 %47
+               OpStore %43 %48
+               OpStore %52 %59
+         %62 = OpAccessChain %61 %52 %17
+         %63 = OpLoad %49 %62
+         %64 = OpAccessChain %61 %52 %12
+         %65 = OpLoad %49 %64
+         %66 = OpDot %29 %63 %65
+               OpStore %60 %66
+               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 factManager;
+
+  // Tests existing access chain instructions
+  auto instructionDescriptor =
+      MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
+  auto transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+  // Tests existing non-access chain instructions
+  instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(14, SpvOpConstantComposite, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  // Tests the base instruction id not existing
+  instructionDescriptor = MakeInstructionDescriptor(67, SpvOpAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor = MakeInstructionDescriptor(68, SpvOpAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(69, SpvOpInBoundsAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  // Tests there being no instruction with the desired opcode after the base
+  // instruction id
+  instructionDescriptor = MakeInstructionDescriptor(65, SpvOpAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(66, SpvOpInBoundsAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  // Tests there being an instruction with the desired opcode after the base
+  // instruction id, but the skip count associated with the instruction
+  // descriptor being so high.
+  instructionDescriptor = MakeInstructionDescriptor(11, SpvOpAccessChain, 100);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(16, SpvOpInBoundsAccessChain, 100);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+}
+
+TEST(TransformationToggleAccessChainInstructionTest, ApplyTest) {
+  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 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %6 2
+         %14 = OpConstantComposite %9 %12 %13
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %6 0
+         %29 = OpTypeFloat 32
+         %30 = OpTypeArray %29 %8
+         %31 = OpTypePointer Function %30
+         %33 = OpConstant %29 1
+         %34 = OpConstant %29 2
+         %35 = OpConstantComposite %30 %33 %34
+         %36 = OpTypePointer Function %29
+         %49 = OpTypeVector %29 3
+         %50 = OpTypeArray %49 %8
+         %51 = OpTypePointer Function %50
+         %53 = OpConstant %29 3
+         %54 = OpConstantComposite %49 %33 %34 %53
+         %55 = OpConstant %29 4
+         %56 = OpConstant %29 5
+         %57 = OpConstant %29 6
+         %58 = OpConstantComposite %49 %55 %56 %57
+         %59 = OpConstantComposite %50 %54 %58
+         %61 = OpTypePointer Function %49
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %16 = OpVariable %15 Function
+         %23 = OpVariable %15 Function
+         %32 = OpVariable %31 Function
+         %37 = OpVariable %36 Function
+         %43 = OpVariable %36 Function
+         %52 = OpVariable %51 Function
+         %60 = OpVariable %36 Function
+               OpStore %11 %14
+         %18 = OpAccessChain %15 %11 %17
+         %19 = OpLoad %6 %18
+         %20 = OpInBoundsAccessChain %15 %11 %12
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %19 %21
+               OpStore %16 %22
+         %24 = OpAccessChain %15 %11 %17
+         %25 = OpLoad %6 %24
+         %26 = OpInBoundsAccessChain %15 %11 %12
+         %27 = OpLoad %6 %26
+         %28 = OpIMul %6 %25 %27
+               OpStore %23 %28
+               OpStore %32 %35
+         %38 = OpAccessChain %36 %32 %17
+         %39 = OpLoad %29 %38
+         %40 = OpAccessChain %36 %32 %12
+         %41 = OpLoad %29 %40
+         %42 = OpFAdd %29 %39 %41
+               OpStore %37 %42
+         %44 = OpAccessChain %36 %32 %17
+         %45 = OpLoad %29 %44
+         %46 = OpAccessChain %36 %32 %12
+         %47 = OpLoad %29 %46
+         %48 = OpFMul %29 %45 %47
+               OpStore %43 %48
+               OpStore %52 %59
+         %62 = OpAccessChain %61 %52 %17
+         %63 = OpLoad %49 %62
+         %64 = OpAccessChain %61 %52 %12
+         %65 = OpLoad %49 %64
+         %66 = OpDot %29 %63 %65
+               OpStore %60 %66
+               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 factManager;
+
+  auto instructionDescriptor =
+      MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
+  auto transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  instructionDescriptor =
+      MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  instructionDescriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+  transformation =
+      TransformationToggleAccessChainInstruction(instructionDescriptor);
+  transformation.Apply(context.get(), &factManager);
+
+  std::string variantShader = 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 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %6 2
+         %14 = OpConstantComposite %9 %12 %13
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %6 0
+         %29 = OpTypeFloat 32
+         %30 = OpTypeArray %29 %8
+         %31 = OpTypePointer Function %30
+         %33 = OpConstant %29 1
+         %34 = OpConstant %29 2
+         %35 = OpConstantComposite %30 %33 %34
+         %36 = OpTypePointer Function %29
+         %49 = OpTypeVector %29 3
+         %50 = OpTypeArray %49 %8
+         %51 = OpTypePointer Function %50
+         %53 = OpConstant %29 3
+         %54 = OpConstantComposite %49 %33 %34 %53
+         %55 = OpConstant %29 4
+         %56 = OpConstant %29 5
+         %57 = OpConstant %29 6
+         %58 = OpConstantComposite %49 %55 %56 %57
+         %59 = OpConstantComposite %50 %54 %58
+         %61 = OpTypePointer Function %49
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %16 = OpVariable %15 Function
+         %23 = OpVariable %15 Function
+         %32 = OpVariable %31 Function
+         %37 = OpVariable %36 Function
+         %43 = OpVariable %36 Function
+         %52 = OpVariable %51 Function
+         %60 = OpVariable %36 Function
+               OpStore %11 %14
+         %18 = OpInBoundsAccessChain %15 %11 %17
+         %19 = OpLoad %6 %18
+         %20 = OpAccessChain %15 %11 %12
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %19 %21
+               OpStore %16 %22
+         %24 = OpInBoundsAccessChain %15 %11 %17
+         %25 = OpLoad %6 %24
+         %26 = OpAccessChain %15 %11 %12
+         %27 = OpLoad %6 %26
+         %28 = OpIMul %6 %25 %27
+               OpStore %23 %28
+               OpStore %32 %35
+         %38 = OpInBoundsAccessChain %36 %32 %17
+         %39 = OpLoad %29 %38
+         %40 = OpAccessChain %36 %32 %12
+         %41 = OpLoad %29 %40
+         %42 = OpFAdd %29 %39 %41
+               OpStore %37 %42
+         %44 = OpAccessChain %36 %32 %17
+         %45 = OpLoad %29 %44
+         %46 = OpAccessChain %36 %32 %12
+         %47 = OpLoad %29 %46
+         %48 = OpFMul %29 %45 %47
+               OpStore %43 %48
+               OpStore %52 %59
+         %62 = OpAccessChain %61 %52 %17
+         %63 = OpLoad %49 %62
+         %64 = OpAccessChain %61 %52 %12
+         %65 = OpLoad %49 %64
+         %66 = OpDot %29 %63 %65
+               OpStore %60 %66
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, variantShader, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/operand_capabilities_test.cpp b/third_party/SPIRV-Tools/test/operand_capabilities_test.cpp
index 1195597..addb08a 100644
--- a/third_party/SPIRV-Tools/test/operand_capabilities_test.cpp
+++ b/third_party/SPIRV-Tools/test/operand_capabilities_test.cpp
@@ -82,6 +82,13 @@
       SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3 \
     }                                                               \
   }
+#define CASE4(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4)                   \
+  {                                                                  \
+    SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet {   \
+      SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \
+          SpvCapability##CAP4                                        \
+    }                                                                \
+  }
 #define CASE5(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5)             \
   {                                                                  \
     SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet {   \
@@ -491,8 +498,8 @@
             CASE1(BUILT_IN, BuiltInCullDistance, CullDistance),  // Bug 1407, 15234
             CASE1(BUILT_IN, BuiltInVertexId, Shader),
             CASE1(BUILT_IN, BuiltInInstanceId, Shader),
-            CASE3(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation,
-                  RayTracingNV),
+            CASE4(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation,
+                  RayTracingNV, RayTracingProvisionalKHR),
             CASE2(BUILT_IN, BuiltInInvocationId, Geometry, Tessellation),
             CASE2(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT),
             CASE2(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT),  // Bug 15234
diff --git a/third_party/SPIRV-Tools/test/opt/CMakeLists.txt b/third_party/SPIRV-Tools/test/opt/CMakeLists.txt
index 327f265..3954338 100644
--- a/third_party/SPIRV-Tools/test/opt/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/opt/CMakeLists.txt
@@ -55,6 +55,7 @@
        insert_extract_elim_test.cpp
        inst_bindless_check_test.cpp
        inst_buff_addr_check_test.cpp
+       inst_debug_printf_test.cpp
        instruction_list_test.cpp
        instruction_test.cpp
        ir_builder.cpp
diff --git a/third_party/SPIRV-Tools/test/opt/inst_debug_printf_test.cpp b/third_party/SPIRV-Tools/test/opt/inst_debug_printf_test.cpp
new file mode 100644
index 0000000..8123ffb
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/opt/inst_debug_printf_test.cpp
@@ -0,0 +1,215 @@
+// Copyright (c) 2020 Valve Corporation
+// Copyright (c) 2020 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Debug Printf Instrumentation Tests.
+
+#include <string>
+#include <vector>
+
+#include "test/opt/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using InstDebugPrintfTest = PassTest<::testing::Test>;
+
+TEST_F(InstDebugPrintfTest, V4Float32) {
+  // SamplerState g_sDefault;
+  // Texture2D g_tColor;
+  //
+  // struct PS_INPUT
+  // {
+  //   float2 vBaseTexCoord : TEXCOORD0;
+  // };
+  //
+  // struct PS_OUTPUT
+  // {
+  //   float4 vDiffuse : SV_Target0;
+  // };
+  //
+  // PS_OUTPUT MainPs(PS_INPUT i)
+  // {
+  //   PS_OUTPUT o;
+  //
+  //   o.vDiffuse.rgba = g_tColor.Sample(g_sDefault, (i.vBaseTexCoord.xy).xy);
+  //   debugPrintfEXT("diffuse: %v4f", o.vDiffuse.rgba);
+  //   return o;
+  // }
+
+  const std::string defs =
+      R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.DebugPrintf"
+; CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info"
+; CHECK-NOT: %1 = OpExtInstImport "NonSemantic.DebugPrintf"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "MainPs" %3 %4
+; CHECK: OpEntryPoint Fragment %2 "MainPs" %3 %4 %gl_FragCoord
+OpExecutionMode %2 OriginUpperLeft
+%5 = OpString "Color is %vn"
+)";
+
+  const std::string decorates =
+      R"(OpDecorate %6 DescriptorSet 0
+OpDecorate %6 Binding 1
+OpDecorate %7 DescriptorSet 0
+OpDecorate %7 Binding 0
+OpDecorate %3 Location 0
+OpDecorate %4 Location 0
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+; CHECK: OpDecorate %_struct_47 Block
+; CHECK: OpMemberDecorate %_struct_47 0 Offset 0
+; CHECK: OpMemberDecorate %_struct_47 1 Offset 4
+; CHECK: OpDecorate %49 DescriptorSet 7
+; CHECK: OpDecorate %49 Binding 3
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)";
+
+  const std::string globals =
+      R"(%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v4float = OpTypeVector %float 4
+%13 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%6 = OpVariable %_ptr_UniformConstant_13 UniformConstant
+%15 = OpTypeSampler
+%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15
+%7 = OpVariable %_ptr_UniformConstant_15 UniformConstant
+%17 = OpTypeSampledImage %13
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%3 = OpVariable %_ptr_Input_v2float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%4 = OpVariable %_ptr_Output_v4float Output
+; CHECK: %uint = OpTypeInt 32 0
+; CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+; CHECK: %_struct_47 = OpTypeStruct %uint %_runtimearr_uint
+; CHECK: %_ptr_StorageBuffer__struct_47 = OpTypePointer StorageBuffer %_struct_47
+; CHECK: %49 = OpVariable %_ptr_StorageBuffer__struct_47 StorageBuffer
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+)";
+
+  const std::string main =
+      R"(%2 = OpFunction %void None %9
+%20 = OpLabel
+%21 = OpLoad %v2float %3
+%22 = OpLoad %13 %6
+%23 = OpLoad %15 %7
+%24 = OpSampledImage %17 %22 %23
+%25 = OpImageSampleImplicitLod %v4float %24 %21
+%26 = OpExtInst %void %1 1 %5 %25
+; CHECK-NOT: %26 = OpExtInst %void %1 1 %5 %25
+; CHECK: %29 = OpCompositeExtract %float %25 0
+; CHECK: %30 = OpBitcast %uint %29
+; CHECK: %31 = OpCompositeExtract %float %25 1
+; CHECK: %32 = OpBitcast %uint %31
+; CHECK: %33 = OpCompositeExtract %float %25 2
+; CHECK: %34 = OpBitcast %uint %33
+; CHECK: %35 = OpCompositeExtract %float %25 3
+; CHECK: %36 = OpBitcast %uint %35
+; CHECK: %101 = OpFunctionCall %void %37 %uint_36 %uint_5 %30 %32 %34 %36
+; CHECK: OpBranch %102
+; CHECK: %102 = OpLabel
+OpStore %4 %25
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string output_func =
+      R"(; CHECK: %37 = OpFunction %void None %38
+; CHECK: %39 = OpFunctionParameter %uint
+; CHECK: %40 = OpFunctionParameter %uint
+; CHECK: %41 = OpFunctionParameter %uint
+; CHECK: %42 = OpFunctionParameter %uint
+; CHECK: %43 = OpFunctionParameter %uint
+; CHECK: %44 = OpFunctionParameter %uint
+; CHECK: %45 = OpLabel
+; CHECK: %52 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_0
+; CHECK: %55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_12
+; CHECK: %56 = OpIAdd %uint %55 %uint_12
+; CHECK: %57 = OpArrayLength %uint %49 1
+; CHECK: %59 = OpULessThanEqual %bool %56 %57
+; CHECK: OpSelectionMerge %60 None
+; CHECK: OpBranchConditional %59 %61 %60
+; CHECK: %61 = OpLabel
+; CHECK: %62 = OpIAdd %uint %55 %uint_0
+; CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %62
+; CHECK: OpStore %64 %uint_12
+; CHECK: %66 = OpIAdd %uint %55 %uint_1
+; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %66
+; CHECK: OpStore %67 %uint_23
+; CHECK: %69 = OpIAdd %uint %55 %uint_2
+; CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %69
+; CHECK: OpStore %70 %39
+; CHECK: %72 = OpIAdd %uint %55 %uint_3
+; CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %72
+; CHECK: OpStore %73 %uint_4
+; CHECK: %76 = OpLoad %v4float %gl_FragCoord
+; CHECK: %78 = OpBitcast %v4uint %76
+; CHECK: %79 = OpCompositeExtract %uint %78 0
+; CHECK: %80 = OpIAdd %uint %55 %uint_4
+; CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %80
+; CHECK: OpStore %81 %79
+; CHECK: %82 = OpCompositeExtract %uint %78 1
+; CHECK: %83 = OpIAdd %uint %55 %uint_5
+; CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %83
+; CHECK: OpStore %84 %82
+; CHECK: %86 = OpIAdd %uint %55 %uint_7
+; CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %86
+; CHECK: OpStore %87 %40
+; CHECK: %89 = OpIAdd %uint %55 %uint_8
+; CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %89
+; CHECK: OpStore %90 %41
+; CHECK: %92 = OpIAdd %uint %55 %uint_9
+; CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %92
+; CHECK: OpStore %93 %42
+; CHECK: %95 = OpIAdd %uint %55 %uint_10
+; CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %95
+; CHECK: OpStore %96 %43
+; CHECK: %98 = OpIAdd %uint %55 %uint_11
+; CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %98
+; CHECK: OpStore %99 %44
+; CHECK: OpBranch %60
+; CHECK: %60 = OpLabel
+; CHECK: OpReturn
+; CHECK: OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<InstDebugPrintfPass>(
+      defs + decorates + globals + main + output_func, true);
+}
+
+// TODO(greg-lunarg): Add tests to verify handling of these cases:
+//
+//   Compute shader
+//   Geometry shader
+//   Tesselation control shader
+//   Tesselation eval shader
+//   Vertex shader
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/instruction_test.cpp b/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
index a697201..1995c5b 100644
--- a/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
@@ -14,6 +14,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "gmock/gmock.h"
@@ -34,6 +35,7 @@
 using OpaqueTypeTest = PassTest<::testing::Test>;
 using GetBaseTest = PassTest<::testing::Test>;
 using ValidBasePointerTest = PassTest<::testing::Test>;
+using VulkanBufferTest = PassTest<::testing::Test>;
 
 TEST(InstructionTest, CreateTrivial) {
   Instruction empty;
@@ -60,6 +62,18 @@
   EXPECT_EQ(inst.end(), inst.begin());
 }
 
+TEST(InstructionTest, OperandAsCString) {
+  Operand::OperandData abcde{0x64636261, 0x65};
+  Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde));
+  EXPECT_STREQ("abcde", operand.AsCString());
+}
+
+TEST(InstructionTest, OperandAsString) {
+  Operand::OperandData abcde{0x64636261, 0x65};
+  Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde));
+  EXPECT_EQ("abcde", operand.AsString());
+}
+
 // The words for an OpTypeInt for 32-bit signed integer resulting in Id 44.
 uint32_t kSampleInstructionWords[] = {(4 << 16) | uint32_t(SpvOpTypeInt), 44,
                                       32, 1};
@@ -1130,6 +1144,260 @@
   EXPECT_TRUE(null_inst->IsValidBasePointer());
 }
 
+TEST_F(VulkanBufferTest, VulkanStorageBuffer) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability RuntimeDescriptorArray
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %1 "main"
+OpExecutionMode %1 LocalSize 1 1 1
+OpDecorate %2 Block
+OpMemberDecorate %2 0 Offset 0
+OpDecorate %3 BufferBlock
+OpMemberDecorate %3 0 Offset 0
+%4 = OpTypeVoid
+%5 = OpTypeInt 32 0
+%2 = OpTypeStruct %5
+%3 = OpTypeStruct %5
+
+%6 = OpTypePointer StorageBuffer %2
+%7 = OpTypePointer Uniform %2
+%8 = OpTypePointer Uniform %3
+
+%9 = OpConstant %5 1
+%10 = OpTypeArray %2 %9
+%11 = OpTypeArray %3 %9
+%12 = OpTypePointer StorageBuffer %10
+%13 = OpTypePointer Uniform %10
+%14 = OpTypePointer Uniform %11
+
+%15 = OpTypeRuntimeArray %2
+%16 = OpTypeRuntimeArray %3
+%17 = OpTypePointer StorageBuffer %15
+%18 = OpTypePointer Uniform %15
+%19 = OpTypePointer Uniform %16
+
+%50 = OpTypeFunction %4
+%1 = OpFunction %4 None %50
+%51 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text);
+  EXPECT_NE(context, nullptr);
+
+  // Standard SSBO and UBO
+  Instruction* inst = context->get_def_use_mgr()->GetDef(6);
+  EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+  inst = context->get_def_use_mgr()->GetDef(7);
+  EXPECT_EQ(false, inst->IsVulkanStorageBuffer());
+  inst = context->get_def_use_mgr()->GetDef(8);
+  EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+
+  // Arrayed SSBO and UBO
+  inst = context->get_def_use_mgr()->GetDef(12);
+  EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+  inst = context->get_def_use_mgr()->GetDef(13);
+  EXPECT_EQ(false, inst->IsVulkanStorageBuffer());
+  inst = context->get_def_use_mgr()->GetDef(14);
+  EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+
+  // Runtime arrayed SSBO and UBO
+  inst = context->get_def_use_mgr()->GetDef(17);
+  EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+  inst = context->get_def_use_mgr()->GetDef(18);
+  EXPECT_EQ(false, inst->IsVulkanStorageBuffer());
+  inst = context->get_def_use_mgr()->GetDef(19);
+  EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+}
+
+TEST_F(VulkanBufferTest, VulkanUniformBuffer) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability RuntimeDescriptorArray
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %1 "main"
+OpExecutionMode %1 LocalSize 1 1 1
+OpDecorate %2 Block
+OpMemberDecorate %2 0 Offset 0
+OpDecorate %3 BufferBlock
+OpMemberDecorate %3 0 Offset 0
+%4 = OpTypeVoid
+%5 = OpTypeInt 32 0
+%2 = OpTypeStruct %5
+%3 = OpTypeStruct %5
+
+%6 = OpTypePointer StorageBuffer %2
+%7 = OpTypePointer Uniform %2
+%8 = OpTypePointer Uniform %3
+
+%9 = OpConstant %5 1
+%10 = OpTypeArray %2 %9
+%11 = OpTypeArray %3 %9
+%12 = OpTypePointer StorageBuffer %10
+%13 = OpTypePointer Uniform %10
+%14 = OpTypePointer Uniform %11
+
+%15 = OpTypeRuntimeArray %2
+%16 = OpTypeRuntimeArray %3
+%17 = OpTypePointer StorageBuffer %15
+%18 = OpTypePointer Uniform %15
+%19 = OpTypePointer Uniform %16
+
+%50 = OpTypeFunction %4
+%1 = OpFunction %4 None %50
+%51 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text);
+  EXPECT_NE(context, nullptr);
+
+  // Standard SSBO and UBO
+  Instruction* inst = context->get_def_use_mgr()->GetDef(6);
+  EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+  inst = context->get_def_use_mgr()->GetDef(7);
+  EXPECT_EQ(true, inst->IsVulkanUniformBuffer());
+  inst = context->get_def_use_mgr()->GetDef(8);
+  EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+
+  // Arrayed SSBO and UBO
+  inst = context->get_def_use_mgr()->GetDef(12);
+  EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+  inst = context->get_def_use_mgr()->GetDef(13);
+  EXPECT_EQ(true, inst->IsVulkanUniformBuffer());
+  inst = context->get_def_use_mgr()->GetDef(14);
+  EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+
+  // Runtime arrayed SSBO and UBO
+  inst = context->get_def_use_mgr()->GetDef(17);
+  EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+  inst = context->get_def_use_mgr()->GetDef(18);
+  EXPECT_EQ(true, inst->IsVulkanUniformBuffer());
+  inst = context->get_def_use_mgr()->GetDef(19);
+  EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+}
+
+TEST_F(VulkanBufferTest, ImageQueries) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability ImageBuffer
+OpCapability RuntimeDescriptorArray
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %1 "main"
+OpExecutionMode %1 LocalSize 1 1 1
+%2 = OpTypeVoid
+%3 = OpTypeFloat 32
+
+%4 = OpTypeImage %3 Buffer 0 0 0 1 Rgba32f
+%5 = OpTypeImage %3 Buffer 0 0 0 2 Rgba32f
+%6 = OpTypeImage %3 2D 0 0 0 1 Rgba32f
+%7 = OpTypeImage %3 2D 0 0 0 2 Rgba32f
+
+%8 = OpTypePointer UniformConstant %4
+%9 = OpTypePointer UniformConstant %5
+%10 = OpTypePointer UniformConstant %6
+%11 = OpTypePointer UniformConstant %7
+
+%12 = OpTypeInt 32 0
+%13 = OpConstant %12 1
+%14 = OpTypeArray %4 %13
+%15 = OpTypeArray %5 %13
+%16 = OpTypeArray %6 %13
+%17 = OpTypeArray %7 %13
+%18 = OpTypePointer UniformConstant %14
+%19 = OpTypePointer UniformConstant %15
+%20 = OpTypePointer UniformConstant %16
+%21 = OpTypePointer UniformConstant %17
+
+%22 = OpTypeRuntimeArray %4
+%23 = OpTypeRuntimeArray %5
+%24 = OpTypeRuntimeArray %6
+%25 = OpTypeRuntimeArray %7
+%26 = OpTypePointer UniformConstant %22
+%27 = OpTypePointer UniformConstant %23
+%28 = OpTypePointer UniformConstant %24
+%29 = OpTypePointer UniformConstant %25
+
+%50 = OpTypeFunction %4
+%1 = OpFunction %4 None %50
+%51 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text);
+  EXPECT_NE(context, nullptr);
+
+  // Bare pointers
+  Instruction* inst = context->get_def_use_mgr()->GetDef(8);
+  EXPECT_EQ(false, inst->IsVulkanStorageImage());
+  EXPECT_EQ(false, inst->IsVulkanSampledImage());
+  EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+  inst = context->get_def_use_mgr()->GetDef(9);
+  EXPECT_EQ(false, inst->IsVulkanStorageImage());
+  EXPECT_EQ(false, inst->IsVulkanSampledImage());
+  EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer());
+
+  inst = context->get_def_use_mgr()->GetDef(10);
+  EXPECT_EQ(false, inst->IsVulkanStorageImage());
+  EXPECT_EQ(true, inst->IsVulkanSampledImage());
+  EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+  inst = context->get_def_use_mgr()->GetDef(11);
+  EXPECT_EQ(true, inst->IsVulkanStorageImage());
+  EXPECT_EQ(false, inst->IsVulkanSampledImage());
+  EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+  // Array pointers
+  inst = context->get_def_use_mgr()->GetDef(18);
+  EXPECT_EQ(false, inst->IsVulkanStorageImage());
+  EXPECT_EQ(false, inst->IsVulkanSampledImage());
+  EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+  inst = context->get_def_use_mgr()->GetDef(19);
+  EXPECT_EQ(false, inst->IsVulkanStorageImage());
+  EXPECT_EQ(false, inst->IsVulkanSampledImage());
+  EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer());
+
+  inst = context->get_def_use_mgr()->GetDef(20);
+  EXPECT_EQ(false, inst->IsVulkanStorageImage());
+  EXPECT_EQ(true, inst->IsVulkanSampledImage());
+  EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+  inst = context->get_def_use_mgr()->GetDef(21);
+  EXPECT_EQ(true, inst->IsVulkanStorageImage());
+  EXPECT_EQ(false, inst->IsVulkanSampledImage());
+  EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+  // Runtime array pointers
+  inst = context->get_def_use_mgr()->GetDef(26);
+  EXPECT_EQ(false, inst->IsVulkanStorageImage());
+  EXPECT_EQ(false, inst->IsVulkanSampledImage());
+  EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+  inst = context->get_def_use_mgr()->GetDef(27);
+  EXPECT_EQ(false, inst->IsVulkanStorageImage());
+  EXPECT_EQ(false, inst->IsVulkanSampledImage());
+  EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer());
+
+  inst = context->get_def_use_mgr()->GetDef(28);
+  EXPECT_EQ(false, inst->IsVulkanStorageImage());
+  EXPECT_EQ(true, inst->IsVulkanSampledImage());
+  EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+  inst = context->get_def_use_mgr()->GetDef(29);
+  EXPECT_EQ(true, inst->IsVulkanStorageImage());
+  EXPECT_EQ(false, inst->IsVulkanSampledImage());
+  EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/ir_builder.cpp b/third_party/SPIRV-Tools/test/opt/ir_builder.cpp
index f800ca4..cb234e0 100644
--- a/third_party/SPIRV-Tools/test/opt/ir_builder.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ir_builder.cpp
@@ -408,7 +408,7 @@
 
 TEST_F(IRBuilderTest, AccelerationStructureNV) {
   const std::string text = R"(
-; CHECK: OpTypeAccelerationStructureNV
+; CHECK: OpTypeAccelerationStructureKHR
 OpCapability Shader
 OpCapability RayTracingNV
 OpExtension "SPV_NV_ray_tracing"
diff --git a/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp b/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
index c60e853..50e3a08 100644
--- a/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
@@ -234,34 +234,33 @@
 %44 = OpExtInst %void %1 DebugLocalVariable %16 %40 %34 6 16 %43 FlagIsLocal 0
 %45 = OpExtInst %void %1 DebugLocalVariable %17 %40 %34 7 16 %43 FlagIsLocal 1
 %46 = OpExtInst %void %1 DebugLocalVariable %18 %36 %34 8 3 %43 FlagIsLocal
-%47 = OpExtInst %void %1 DebugDeclare %44 %pos %42
-%48 = OpExtInst %void %1 DebugDeclare %45 %color %42
 OpLine %7 6 1
 %main = OpFunction %void None %31
-%49 = OpLabel
-%50 = OpExtInst %void %1 DebugScope %43
+%47 = OpLabel
+%60 = OpExtInst %void %1 DebugScope %43
 OpLine %7 8 13
 %vout = OpVariable %_ptr_Function_VS_OUTPUT Function
-%51 = OpExtInst %void %1 DebugDeclare %46 %vout %42
+%49 = OpExtInst %void %1 DebugDeclare %46 %vout %42
 OpLine %7 9 14
-%52 = OpLoad %v4float %pos
+%50 = OpLoad %v4float %pos
 OpLine %7 9 3
-%53 = OpAccessChain %_ptr_Function_v4float %vout %int_0
-%54 = OpExtInst %void %1 DebugValue %46 %53 %42 %int_0
-OpStore %53 %52
+%51 = OpAccessChain %_ptr_Function_v4float %vout %int_0
+%52 = OpExtInst %void %1 DebugValue %46 %51 %42 %int_0
+OpStore %51 %50
 OpLine %7 10 16
-%55 = OpLoad %v4float %color
+%53 = OpLoad %v4float %color
 OpLine %7 10 3
-%56 = OpAccessChain %_ptr_Function_v4float %vout %int_1
-%57 = OpExtInst %void %1 DebugValue %46 %56 %42 %int_1
-OpStore %56 %55
+%54 = OpAccessChain %_ptr_Function_v4float %vout %int_1
+%55 = OpExtInst %void %1 DebugValue %46 %54 %42 %int_1
+OpStore %54 %53
 OpLine %7 11 10
-%58 = OpLoad %VS_OUTPUT %vout
+%56 = OpLoad %VS_OUTPUT %vout
 OpLine %7 11 3
-%59 = OpCompositeExtract %v4float %58 0
-OpStore %gl_Position %59
-%60 = OpCompositeExtract %v4float %58 1
-OpStore %out_var_COLOR %60
+%57 = OpCompositeExtract %v4float %56 0
+OpStore %gl_Position %57
+%58 = OpCompositeExtract %v4float %56 1
+OpStore %out_var_COLOR %58
+%61 = OpExtInst %void %1 DebugNoScope
 OpReturn
 OpFunctionEnd
 )");
@@ -377,7 +376,7 @@
 OpLine %5 12 1
 %main = OpFunction %void None %36
 %bb_entry = OpLabel
-%52 = OpExtInst %void %1 DebugScope %47
+%70 = OpExtInst %void %1 DebugScope %47
 OpLine %5 13 16
 %param_var_arg1 = OpVariable %_ptr_Function_float Function
 %53 = OpLoad %float %pos
@@ -386,6 +385,7 @@
 %54 = OpFunctionCall %v4float %func1 %param_var_arg1
 OpLine %5 13 3
 OpStore %gl_Position %54
+%71 = OpExtInst %void %1 DebugNoScope
 OpReturn
 OpFunctionEnd
 OpLine %5 5 1
@@ -393,41 +393,45 @@
 OpLine %5 5 20
 %arg1 = OpFunctionParameter %_ptr_Function_float
 %bb_entry_0 = OpLabel
-%55 = OpExtInst %void %1 DebugScope %48
+%72 = OpExtInst %void %1 DebugScope %48
 OpLine %5 9 16
 %param_var_arg2 = OpVariable %_ptr_Function_float Function
 OpLine %5 6 7
-%56 = OpLoad %float %arg1
+%57 = OpLoad %float %arg1
 OpLine %5 6 12
-%57 = OpFOrdGreaterThan %bool %56 %float_1
+%58 = OpFOrdGreaterThan %bool %57 %float_1
 OpLine %5 6 17
+%73 = OpExtInst %void %1 DebugNoScope
 OpSelectionMerge %if_merge None
-OpBranchConditional %57 %if_true %if_merge
+OpBranchConditional %58 %if_true %if_merge
 %if_true = OpLabel
-%58 = OpExtInst %void %1 DebugScope %50
+%74 = OpExtInst %void %1 DebugScope %50
 OpLine %5 7 5
+%75 = OpExtInst %void %1 DebugNoScope
 OpReturnValue %32
 %if_merge = OpLabel
-%59 = OpExtInst %void %1 DebugScope %51
+%76 = OpExtInst %void %1 DebugScope %51
 OpLine %5 9 16
-%60 = OpLoad %float %arg1
-OpStore %param_var_arg2 %60
+%63 = OpLoad %float %arg1
+OpStore %param_var_arg2 %63
 OpLine %5 9 10
-%61 = OpFunctionCall %v4float %func2 %param_var_arg2
+%64 = OpFunctionCall %v4float %func2 %param_var_arg2
 OpLine %5 9 3
-OpReturnValue %61
+%77 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %64
 OpFunctionEnd
 OpLine %5 1 1
 %func2 = OpFunction %v4float None %38
 OpLine %5 1 20
 %arg2 = OpFunctionParameter %_ptr_Function_float
 %bb_entry_1 = OpLabel
-%62 = OpExtInst %void %1 DebugScope %49
+%78 = OpExtInst %void %1 DebugScope %49
 OpLine %5 2 17
-%63 = OpLoad %float %arg2
-%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0
+%67 = OpLoad %float %arg2
+%68 = OpCompositeConstruct %v4float %67 %float_0 %float_0 %float_0
 OpLine %5 2 3
-OpReturnValue %64
+%79 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %68
 OpFunctionEnd
 )");
 }
@@ -547,28 +551,31 @@
 OpLine %5 12 1
 %main = OpFunction %void None %28
 %bb_entry = OpLabel
-%51 = OpExtInst %void %1 DebugScope %44 %49
+%62 = OpExtInst %void %1 DebugScope %44 %49
 OpLine %5 6 7
 %52 = OpLoad %float %pos
 OpLine %5 6 12
 %53 = OpFOrdGreaterThan %bool %52 %float_1
 OpLine %5 6 17
+%63 = OpExtInst %void %1 DebugNoScope
 OpSelectionMerge %if_merge None
 OpBranchConditional %53 %if_true %if_merge
 %if_true = OpLabel
-%54 = OpExtInst %void %1 DebugScope %46 %49
+%64 = OpExtInst %void %1 DebugScope %46 %49
 OpLine %5 7 5
 OpStore %gl_Position %24
+%65 = OpExtInst %void %1 DebugNoScope
 OpReturn
 %if_merge = OpLabel
-%55 = OpExtInst %void %1 DebugScope %45 %50
+%66 = OpExtInst %void %1 DebugScope %45 %50
 OpLine %5 2 17
-%56 = OpLoad %float %pos
+%58 = OpLoad %float %pos
 OpLine %5 2 10
-%57 = OpCompositeConstruct %v4float %56 %float_0 %float_0 %float_0
-%58 = OpExtInst %void %1 DebugScope %43
+%59 = OpCompositeConstruct %v4float %58 %float_0 %float_0 %float_0
+%67 = OpExtInst %void %1 DebugScope %43
 OpLine %5 13 3
-OpStore %gl_Position %57
+OpStore %gl_Position %59
+%68 = OpExtInst %void %1 DebugNoScope
 OpReturn
 OpFunctionEnd
 )");
@@ -683,8 +690,8 @@
 %51 = OpExtInst %void %1 DebugLexicalBlock %40 9 3 %48
 OpLine %5 12 1
 %main = OpFunction %void None %36
-%52 = OpExtInst %void %1 DebugScope %47
 %bb_entry = OpLabel
+%70 = OpExtInst %void %1 DebugScope %47
 OpLine %5 13 16
 %param_var_arg1 = OpVariable %_ptr_Function_float Function
 %53 = OpLoad %float %pos
@@ -693,6 +700,7 @@
 %54 = OpFunctionCall %v4float %func1 %param_var_arg1
 OpLine %5 13 3
 OpStore %gl_Position %54
+%71 = OpExtInst %void %1 DebugNoScope
 OpReturn
 OpFunctionEnd
 OpLine %5 5 1
@@ -700,48 +708,244 @@
 OpLine %5 5 20
 %arg1 = OpFunctionParameter %_ptr_Function_float
 %bb_entry_0 = OpLabel
-%55 = OpExtInst %void %1 DebugScope %48
+%72 = OpExtInst %void %1 DebugScope %48
 OpLine %5 9 16
 %param_var_arg2 = OpVariable %_ptr_Function_float Function
 OpLine %5 6 7
-%56 = OpLoad %float %arg1
+%57 = OpLoad %float %arg1
 OpLine %5 6 12
-%57 = OpFOrdGreaterThan %bool %56 %float_1
+%58 = OpFOrdGreaterThan %bool %57 %float_1
 OpLine %5 6 17
+%73 = OpExtInst %void %1 DebugNoScope
 OpSelectionMerge %if_merge None
-OpBranchConditional %57 %if_true %if_merge
+OpBranchConditional %58 %if_true %if_merge
 %if_true = OpLabel
-%58 = OpExtInst %void %1 DebugScope %50
+%74 = OpExtInst %void %1 DebugScope %50
 OpLine %5 7 5
+%75 = OpExtInst %void %1 DebugNoScope
 OpReturnValue %32
 %if_merge = OpLabel
-%59 = OpExtInst %void %1 DebugScope %51
+%76 = OpExtInst %void %1 DebugScope %51
 OpLine %5 9 16
-%60 = OpLoad %float %arg1
-OpStore %param_var_arg2 %60
+%63 = OpLoad %float %arg1
+OpStore %param_var_arg2 %63
 OpLine %5 9 10
-%61 = OpFunctionCall %v4float %func2 %param_var_arg2
+%64 = OpFunctionCall %v4float %func2 %param_var_arg2
 OpLine %5 9 3
-OpReturnValue %61
+%77 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %64
 OpFunctionEnd
 OpLine %5 1 1
 %func2 = OpFunction %v4float None %38
 OpLine %5 1 20
 %arg2 = OpFunctionParameter %_ptr_Function_float
 %bb_entry_1 = OpLabel
-%62 = OpExtInst %void %1 DebugScope %49
+%78 = OpExtInst %void %1 DebugScope %49
 OpLine %5 2 17
-%63 = OpLoad %float %arg2
-%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0
+%67 = OpLoad %float %arg2
+%68 = OpCompositeConstruct %v4float %67 %float_0 %float_0 %float_0
 OpLine %5 2 3
-OpReturnValue %64
+%79 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %68
 OpFunctionEnd
 )";
 
   SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
   std::unique_ptr<IRContext> context =
       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
-  ASSERT_EQ(nullptr, context);
+  ASSERT_NE(nullptr, context);
+
+  std::vector<uint32_t> binary;
+  context->module()->ToBinary(&binary, /* skip_nop = */ false);
+
+  std::string disassembled_text;
+  EXPECT_TRUE(t.Disassemble(binary, &disassembled_text));
+  EXPECT_EQ(text, disassembled_text);
+}
+
+TEST(IrBuilder, DebugInfoInstInFunctionOutOfBlock2) {
+  // /* HLSL */
+  //
+  // struct VS_OUTPUT {
+  //   float4 pos : SV_POSITION;
+  //   float4 color : COLOR;
+  // };
+  //
+  // VS_OUTPUT main(float4 pos : POSITION,
+  //                float4 color : COLOR) {
+  //   VS_OUTPUT vout;
+  //   vout.pos = pos;
+  //   vout.color = color;
+  //   return vout;
+  // }
+  const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR
+%7 = OpString "vs.hlsl"
+OpSource HLSL 600 %7 "#line 1 \"vs.hlsl\"
+struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+               float4 color : COLOR) {
+  VS_OUTPUT vout;
+  vout.pos = pos;
+  vout.color = color;
+  return vout;
+}
+"
+%8 = OpString "#line 1 \"vs.hlsl\"
+struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+               float4 color : COLOR) {
+  VS_OUTPUT vout;
+  vout.pos = pos;
+  vout.color = color;
+  return vout;
+}
+"
+%9 = OpString "VS_OUTPUT"
+%10 = OpString "float"
+%11 = OpString "src.main"
+%12 = OpString "pos"
+%13 = OpString "color"
+%14 = OpString "vout"
+OpName %in_var_POSITION "in.var.POSITION"
+OpName %in_var_COLOR "in.var.COLOR"
+OpName %out_var_COLOR "out.var.COLOR"
+OpName %main "main"
+OpName %param_var_pos "param.var.pos"
+OpName %param_var_color "param.var.color"
+OpName %VS_OUTPUT "VS_OUTPUT"
+OpMemberName %VS_OUTPUT 0 "pos"
+OpMemberName %VS_OUTPUT 1 "color"
+OpName %src_main "src.main"
+OpName %pos "pos"
+OpName %color "color"
+OpName %bb_entry "bb.entry"
+OpName %vout "vout"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %in_var_POSITION Location 0
+OpDecorate %in_var_COLOR Location 1
+OpDecorate %out_var_COLOR Location 0
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%uint_256 = OpConstant %uint 256
+%uint_0 = OpConstant %uint 0
+%uint_128 = OpConstant %uint 128
+%36 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%VS_OUTPUT = OpTypeStruct %v4float %v4float
+%38 = OpTypeFunction %VS_OUTPUT %_ptr_Function_v4float %_ptr_Function_v4float
+%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT
+OpLine %7 6 29
+%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
+OpLine %7 7 31
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+OpLine %7 2 16
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+OpLine %7 3 18
+%out_var_COLOR = OpVariable %_ptr_Output_v4float Output
+%40 = OpExtInst %void %1 DebugExpression
+%41 = OpExtInst %void %1 DebugSource %7 %8
+%42 = OpExtInst %void %1 DebugCompilationUnit 1 4 %41 HLSL
+%43 = OpExtInst %void %1 DebugTypeComposite %9 Structure %41 1 1 %42 %9 %uint_256 FlagIsProtected|FlagIsPrivate %44 %45
+%46 = OpExtInst %void %1 DebugTypeBasic %10 %uint_32 Float
+%47 = OpExtInst %void %1 DebugTypeVector %46 4
+%48 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %47 %47
+%49 = OpExtInst %void %1 DebugFunction %11 %48 %41 6 1 %42 %11 FlagIsProtected|FlagIsPrivate 7 %src_main
+%50 = OpExtInst %void %1 DebugLocalVariable %12 %47 %41 6 23 %49 FlagIsLocal 0
+%51 = OpExtInst %void %1 DebugLocalVariable %13 %47 %41 7 23 %49 FlagIsLocal 1
+%52 = OpExtInst %void %1 DebugLexicalBlock %41 7 38 %49
+%53 = OpExtInst %void %1 DebugLocalVariable %14 %43 %41 8 13 %52 FlagIsLocal
+%44 = OpExtInst %void %1 DebugTypeMember %12 %47 %41 2 3 %43 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+%45 = OpExtInst %void %1 DebugTypeMember %13 %47 %41 3 3 %43 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+OpLine %7 6 1
+%main = OpFunction %void None %36
+%54 = OpLabel
+%74 = OpExtInst %void %1 DebugScope %42
+OpLine %7 6 23
+%param_var_pos = OpVariable %_ptr_Function_v4float Function
+OpLine %7 7 23
+%param_var_color = OpVariable %_ptr_Function_v4float Function
+OpLine %7 6 23
+%56 = OpLoad %v4float %in_var_POSITION
+OpStore %param_var_pos %56
+OpLine %7 7 23
+%57 = OpLoad %v4float %in_var_COLOR
+OpStore %param_var_color %57
+OpLine %7 6 1
+%58 = OpFunctionCall %VS_OUTPUT %src_main %param_var_pos %param_var_color
+OpLine %7 6 11
+%59 = OpCompositeExtract %v4float %58 0
+OpLine %7 2 16
+OpStore %gl_Position %59
+OpLine %7 6 11
+%60 = OpCompositeExtract %v4float %58 1
+OpLine %7 3 18
+OpStore %out_var_COLOR %60
+%75 = OpExtInst %void %1 DebugNoScope
+OpReturn
+OpFunctionEnd
+OpLine %7 6 1
+%src_main = OpFunction %VS_OUTPUT None %38
+%76 = OpExtInst %void %1 DebugScope %49
+OpLine %7 6 23
+%pos = OpFunctionParameter %_ptr_Function_v4float
+OpLine %7 7 23
+%color = OpFunctionParameter %_ptr_Function_v4float
+%63 = OpExtInst %void %1 DebugDeclare %50 %pos %40
+%64 = OpExtInst %void %1 DebugDeclare %51 %color %40
+%77 = OpExtInst %void %1 DebugNoScope
+%bb_entry = OpLabel
+%78 = OpExtInst %void %1 DebugScope %52
+OpLine %7 8 13
+%vout = OpVariable %_ptr_Function_VS_OUTPUT Function
+%67 = OpExtInst %void %1 DebugDeclare %53 %vout %40
+OpLine %7 9 14
+%68 = OpLoad %v4float %pos
+OpLine %7 9 3
+%69 = OpAccessChain %_ptr_Function_v4float %vout %int_0
+OpStore %69 %68
+OpLine %7 10 16
+%70 = OpLoad %v4float %color
+OpLine %7 10 3
+%71 = OpAccessChain %_ptr_Function_v4float %vout %int_1
+OpStore %71 %70
+OpLine %7 11 10
+%72 = OpLoad %VS_OUTPUT %vout
+OpLine %7 11 3
+%79 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %72
+OpFunctionEnd
+)";
+
+  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
+  ASSERT_NE(nullptr, context);
+
+  std::vector<uint32_t> binary;
+  context->module()->ToBinary(&binary, /* skip_nop = */ false);
+
+  std::string disassembled_text;
+  EXPECT_TRUE(t.Disassemble(binary, &disassembled_text));
+  EXPECT_EQ(text, disassembled_text);
 }
 
 TEST(IrBuilder, LocalGlobalVariables) {
diff --git a/third_party/SPIRV-Tools/test/opt/local_redundancy_elimination_test.cpp b/third_party/SPIRV-Tools/test/opt/local_redundancy_elimination_test.cpp
index bc4635e..291e1bc 100644
--- a/third_party/SPIRV-Tools/test/opt/local_redundancy_elimination_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_redundancy_elimination_test.cpp
@@ -154,6 +154,50 @@
   SinglePassRunAndMatch<LocalRedundancyEliminationPass>(text, false);
 }
 
+TEST_F(LocalRedundancyEliminationTest, StorageBufferIdentification) {
+  const std::string text = R"(
+; CHECK: [[gep:%\w+]] = OpAccessChain
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]]
+; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]]
+; CHECK: OpStore [[gep]] [[add]]
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]]
+; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]]
+; CHECK: OpStore [[gep]] [[add]]
+
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %block BufferBlock
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%block = OpTypeStruct %int
+%array = OpTypeArray %block %int_1
+%ptr_ssbo_array = OpTypePointer Uniform %array
+%ptr_ssbo_int = OpTypePointer Uniform %int
+%var = OpVariable %ptr_ssbo_array Uniform
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep1 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0
+%ld1 = OpLoad %int %gep1
+%add1 = OpIAdd %int %ld1 %int_1
+%gep2 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0
+OpStore %gep2 %add1
+%gep3 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0
+%ld3 = OpLoad %int %gep3
+%add3 = OpIAdd %int %ld3 %int_1
+%gep4 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0
+OpStore %gep4 %add3
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalRedundancyEliminationPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/type_manager_test.cpp b/third_party/SPIRV-Tools/test/opt/type_manager_test.cpp
index 743d0b6..fdae2ef 100644
--- a/third_party/SPIRV-Tools/test/opt/type_manager_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/type_manager_test.cpp
@@ -1063,7 +1063,7 @@
 ; CHECK: OpTypeForwardPointer [[uniform_ptr]] Uniform
 ; CHECK: OpTypePipeStorage
 ; CHECK: OpTypeNamedBarrier
-; CHECK: OpTypeAccelerationStructureNV
+; CHECK: OpTypeAccelerationStructureKHR
 ; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]]
 OpCapability Shader
 OpCapability Int64
diff --git a/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp b/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
index ae166d9..fa2b153 100644
--- a/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
@@ -120,7 +120,7 @@
       execution_model, memory_model);
 }
 
-std::string GenerateWebGPUShaderCode(
+std::string GenerateWebGPUComputeShaderCode(
     const std::string& body,
     const std::string& capabilities_and_extensions = "",
     const std::string& execution_model = "GLCompute") {
@@ -138,6 +138,24 @@
                                 "", execution_model, memory_model);
 }
 
+std::string GenerateWebGPUVertexShaderCode(
+    const std::string& body,
+    const std::string& capabilities_and_extensions = "",
+    const std::string& execution_model = "Vertex") {
+  const std::string vulkan_memory_capability = R"(
+OpCapability VulkanMemoryModelKHR
+)";
+  const std::string vulkan_memory_extension = R"(
+OpExtension "SPV_KHR_vulkan_memory_model"
+)";
+  const std::string memory_model = "OpMemoryModel Logical VulkanKHR";
+  return GenerateShaderCodeImpl(body,
+                                vulkan_memory_capability +
+                                    capabilities_and_extensions +
+                                    vulkan_memory_extension,
+                                "", execution_model, memory_model);
+}
+
 std::string GenerateKernelCode(
     const std::string& body,
     const std::string& capabilities_and_extensions = "") {
@@ -257,7 +275,7 @@
 OpControlBarrier %workgroup %workgroup %acquire_release_workgroup
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
@@ -266,7 +284,7 @@
 OpControlBarrier %workgroup %workgroup %workgroup
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("For WebGPU, AcquireRelease must be set for Memory "
@@ -278,7 +296,7 @@
 OpControlBarrier %workgroup %workgroup %acquire_release
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("For WebGPU, WorkgroupMemory must be set for Memory "
@@ -290,7 +308,7 @@
 OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(
       getDiagnosticString(),
@@ -303,7 +321,7 @@
 OpControlBarrier %workgroup %workgroup %release_uniform_workgroup
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("For WebGPU, AcquireRelease must be set for Memory "
@@ -421,7 +439,7 @@
 OpControlBarrier %device %workgroup %none
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("ControlBarrier: in WebGPU environment Execution Scope "
@@ -433,13 +451,27 @@
 OpControlBarrier %subgroup %workgroup %none
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("ControlBarrier: in WebGPU environment Execution Scope "
                         "is limited to Workgroup"));
 }
 
+TEST_F(ValidateBarriers,
+       OpControlBarrierWebGPUExecutionScopeWorkgroupNonComputeBad) {
+  const std::string body = R"(
+OpControlBarrier %workgroup %workgroup %acquire_release_workgroup
+)";
+
+  CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Workgroup Execution Scope is limited to GLCompute execution model"));
+}
+
 TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroup) {
   const std::string body = R"(
 OpControlBarrier %subgroup %subgroup %none
@@ -479,7 +511,7 @@
 OpControlBarrier %workgroup %subgroup %acquire_release_workgroup
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("ControlBarrier: in WebGPU environment Memory Scope is "
@@ -710,7 +742,7 @@
 OpMemoryBarrier %workgroup %image_memory
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
@@ -719,7 +751,7 @@
 OpMemoryBarrier %subgroup %image_memory
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("in WebGPU environment Memory Scope is limited to "
@@ -731,7 +763,7 @@
 OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("ImageMemory must be set for Memory Semantics of "
@@ -743,7 +775,7 @@
 OpMemoryBarrier %workgroup %uniform_workgroup
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("ImageMemory must be set for Memory Semantics of "
@@ -755,7 +787,7 @@
 OpMemoryBarrier %workgroup %acquire_uniform_workgroup
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("ImageMemory must be set for Memory Semantics of "
@@ -767,7 +799,7 @@
 OpMemoryBarrier %workgroup %release_uniform_workgroup
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("ImageMemory must be set for Memory Semantics of "
@@ -779,13 +811,26 @@
 OpMemoryBarrier %workgroup %uniform_image_memory
 )";
 
-  CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+  CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("only ImageMemory may be set for Memory Semantics of "
                         "OpMemoryBarrier"));
 }
 
+TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUWorkgroupNonComputeFailure) {
+  const std::string body = R"(
+OpMemoryBarrier %workgroup %image_memory
+)";
+
+  CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Workgroup Memory Scope is limited to GLCompute execution model"));
+}
+
 TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemoryScope) {
   const std::string body = R"(
 OpMemoryBarrier %f32_1 %acquire_release_uniform_workgroup
diff --git a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
index 4cf4029..0d09642 100644
--- a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
@@ -4296,6 +4296,213 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateCFG, PhiResultInvalidSampler) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%sampler = OpTypeSampler
+%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
+%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
+%undef_bool = OpUndef %bool
+%undef_sampler = OpUndef %sampler
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_sampler = OpLoad %sampler %sampler_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type cannot be OpTypeSampler"));
+}
+
+TEST_F(ValidateCFG, PhiResultInvalidImage) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
+%ptr_uc_image = OpTypePointer UniformConstant %image
+%image_var = OpVariable %ptr_uc_image UniformConstant
+%undef_bool = OpUndef %bool
+%undef_image = OpUndef %image
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_image = OpLoad %image %image_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %image %undef_image %entry %ld_image %loop
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type cannot be OpTypeImage"));
+}
+
+TEST_F(ValidateCFG, PhiResultInvalidSampledImage) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%sampler = OpTypeSampler
+%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
+%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
+%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
+%ptr_uc_image = OpTypePointer UniformConstant %image
+%image_var = OpVariable %ptr_uc_image UniformConstant
+%sampled_image = OpTypeSampledImage %image
+%undef_bool = OpUndef %bool
+%undef_sampled_image = OpUndef %sampled_image
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_image = OpLoad %image %image_var
+%ld_sampler = OpLoad %sampler %sampler_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
+%sample = OpSampledImage %sampled_image %ld_image %ld_sampler
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type cannot be OpTypeSampledImage"));
+}
+
+TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampler) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%sampler = OpTypeSampler
+%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
+%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
+%undef_bool = OpUndef %bool
+%undef_sampler = OpUndef %sampler
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_sampler = OpLoad %sampler %sampler_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->before_hlsl_legalization = true;
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, PhiResultValidPreLegalizationImage) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
+%ptr_uc_image = OpTypePointer UniformConstant %image
+%image_var = OpVariable %ptr_uc_image UniformConstant
+%undef_bool = OpUndef %bool
+%undef_image = OpUndef %image
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_image = OpLoad %image %image_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %image %undef_image %entry %ld_image %loop
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->before_hlsl_legalization = true;
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampledImage) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%sampler = OpTypeSampler
+%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
+%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
+%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
+%ptr_uc_image = OpTypePointer UniformConstant %image
+%image_var = OpVariable %ptr_uc_image UniformConstant
+%sampled_image = OpTypeSampledImage %image
+%undef_bool = OpUndef %bool
+%undef_sampled_image = OpUndef %sampled_image
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_image = OpLoad %image %image_var
+%ld_sampler = OpLoad %sampler %sampler_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
+%sample = OpSampledImage %sampled_image %ld_image %ld_sampler
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->before_hlsl_legalization = true;
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp b/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
index 01df796..aa73989 100644
--- a/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
@@ -36,6 +36,26 @@
 using ValidateOldDebugInfo = spvtest::ValidateBase<std::string>;
 using ValidateOpenCL100DebugInfo = spvtest::ValidateBase<std::string>;
 using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase<std::string>;
+using ValidateOpenCL100DebugInfoDebugTypedef =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeEnum =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeComposite =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeMember =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeInheritance =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugFunction =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugFunctionDeclaration =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugLexicalBlock =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugLocalVariable =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugDeclare =
+    spvtest::ValidateBase<std::pair<std::string, std::string>>;
 using ValidateGlslStd450SqrtLike = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450FMinLike = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450FClampLike = spvtest::ValidateBase<std::string>;
@@ -798,6 +818,37 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbgNone = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%void_info = OpExtInst %void %DbgExt DebugTypeBasic %void_name %u32_0 Unspecified
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void_info %void_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone
+)";
+
+  const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) {
   const std::string src = R"(
 %src = OpString "simple.hlsl"
@@ -919,6 +970,1097 @@
               HasSubstr("forward referenced IDs have not been defined"));
 }
 
+TEST_F(ValidateOpenCL100DebugInfo, DebugInstructionWrongResultType) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected result type must be a result id of "
+                        "OpTypeVoid"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Source must be a result id of "
+                        "DebugSource"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailFile) {
+  const std::string src = R"(
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand File must be a result id of "
+                        "OpString"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailSource) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Text must be a result id of "
+                        "OpString"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugSourceNoText) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+  float foo;
+  return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Name must be a result id of "
+                        "OpString"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+  float foo;
+  return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Size must be a result id of "
+                        "OpConstant"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+  float foo;
+  return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+  float foo;
+  return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Base Type must be a result id of "
+                        "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+  float foo;
+  return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+  float foo;
+  return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Base Type must be a result id of "
+                        "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Base Type is not a valid debug "
+                        "type"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Component Count must be a result id "
+                        "of OpConstant"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Component Count must be positive integer"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Component Count must be positive integer"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Base Type must be a result id of "
+                        "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Base Type must be a result id of "
+                        "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Base Type must be a result id of "
+                        "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugTypedef )";
+  ss << param.first;
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand " + param.second +
+                        " must be a result id of "));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)",
+                       "Name"),
+        std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)",
+                       "Base Type"),
+        std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)",
+                       "Source"),
+        std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)",
+                       "Parent"),
+    }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info
+%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info
+%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("expected operand Return Type is not a valid debug type"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("expected operand Parameter Types is not a valid debug type"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%none = OpExtInst %void %DbgExt DebugInfoNone
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name
+%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name
+%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )";
+  ss << param.first;
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(
+            R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
+            "Name"),
+        std::make_pair(
+            R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
+            "Underlying Types"),
+        std::make_pair(
+            R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
+            "Source"),
+        std::make_pair(
+            R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)",
+            "Parent"),
+        std::make_pair(
+            R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)",
+            "Size"),
+        std::make_pair(
+            R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)",
+            "Size"),
+        std::make_pair(
+            R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)",
+            "Value"),
+        std::make_pair(
+            R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)",
+            "Name"),
+    }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+};
+struct foo : VS_OUTPUT {
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+};
+struct foo : VS_OUTPUT {
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )";
+  ss << param.first;
+  ss << R"(
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand " + param.second + " must be "));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(
+            R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+            "Name"),
+        std::make_pair(
+            R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+            "Source"),
+        std::make_pair(
+            R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+            "Parent"),
+        std::make_pair(
+            R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+            "Linkage Name"),
+        std::make_pair(
+            R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+            "Size"),
+        std::make_pair(
+            R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)",
+            "Members"),
+        std::make_pair(
+            R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)",
+            "Members"),
+        std::make_pair(
+            R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)",
+            "Members"),
+    }));
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float pos : SV_POSITION;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )";
+  ss << param.first;
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  if (!param.second.empty()) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("expected operand " + param.second +
+                          " must be a result id of "));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(
+            R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
+            "Name"),
+        std::make_pair(
+            R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
+            ""),
+        std::make_pair(
+            R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
+            "Source"),
+        std::make_pair(
+            R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)",
+            "Parent"),
+        std::make_pair(
+            R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)",
+            "Offset"),
+        std::make_pair(
+            R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)",
+            "Size"),
+    }));
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {};
+struct foo : VS_OUTPUT {};
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%foo_name = OpString "foo"
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%child = OpExtInst %void %DbgExt DebugTypeInheritance )"
+     << param.first;
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)",
+                       "Child must be a result id of"),
+        std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)",
+                       "Parent must be a result id of"),
+        std::make_pair(
+            R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)",
+            "Child must be class or struct debug type"),
+        std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)",
+                       "Parent must be class or struct debug type"),
+        std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)",
+                       "Offset"),
+        std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)",
+                       "Size"),
+    }));
 TEST_P(ValidateGlslStd450SqrtLike, Success) {
   const std::string ext_inst_name = GetParam();
   std::ostringstream ss;
@@ -930,6 +2072,511 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic
+%main_info = OpExtInst %void %DbgExt DebugFunction )"
+     << param.first;
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(
+            R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
+            "Name"),
+        std::make_pair(
+            R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
+            "Type"),
+        std::make_pair(
+            R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
+            "Source"),
+        std::make_pair(
+            R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)",
+            "Parent"),
+        std::make_pair(
+            R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)",
+            "Linkage Name"),
+        std::make_pair(
+            R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)",
+            "Function"),
+        std::make_pair(
+            R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)",
+            "Declaration"),
+    }));
+
+TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )"
+     << param.first;
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail,
+    ValidateOpenCL100DebugInfoDebugFunctionDeclaration,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(
+            R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
+            "Name"),
+        std::make_pair(
+            R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
+            "Type"),
+        std::make_pair(
+            R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
+            "Source"),
+        std::make_pair(
+            R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)",
+            "Parent"),
+        std::make_pair(
+            R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)",
+            "Linkage Name"),
+    }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )"
+     << param.first;
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"),
+        std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"),
+        std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"),
+    }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+)";
+
+  const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+)";
+
+  const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugLocalVariable )"
+     << param.first;
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(
+            R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)",
+            "Name"),
+        std::make_pair(
+            R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)",
+            "Type"),
+        std::make_pair(
+            R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)",
+            "Source"),
+        std::make_pair(
+            R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)",
+            "Parent"),
+    }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+  const std::string body = R"(
+%foo = OpVariable %f32_ptr_function Function
+%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, body, extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) {
+  CompileSuccessfully(R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %in_var_COLOR
+          %4 = OpString "test.hlsl"
+               OpSource HLSL 620 %4 "#line 1 \"test.hlsl\"
+void main(float foo:COLOR) {}
+"
+         %11 = OpString "#line 1 \"test.hlsl\"
+void main(float foo:COLOR) {}
+"
+         %14 = OpString "float"
+         %17 = OpString "src.main"
+         %20 = OpString "foo"
+               OpName %in_var_COLOR "in.var.COLOR"
+               OpName %main "main"
+               OpName %param_var_foo "param.var.foo"
+               OpName %src_main "src.main"
+               OpName %foo "foo"
+               OpName %bb_entry "bb.entry"
+               OpDecorate %in_var_COLOR Location 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+       %void = OpTypeVoid
+         %23 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+         %29 = OpTypeFunction %void %_ptr_Function_float
+               OpLine %4 1 21
+%in_var_COLOR = OpVariable %_ptr_Input_float Input
+         %10 = OpExtInst %void %1 DebugExpression
+         %12 = OpExtInst %void %1 DebugSource %4 %11
+         %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL
+         %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float
+         %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15
+         %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main
+         %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0
+         %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18
+               OpLine %4 1 1
+       %main = OpFunction %void None %23
+         %24 = OpLabel
+               OpLine %4 1 17
+%param_var_foo = OpVariable %_ptr_Function_float Function
+         %27 = OpLoad %float %in_var_COLOR
+               OpLine %4 1 1
+         %28 = OpFunctionCall %void %src_main %param_var_foo
+               OpReturn
+               OpFunctionEnd
+   %src_main = OpFunction %void None %29
+               OpLine %4 1 17
+        %foo = OpFunctionParameter %_ptr_Function_float
+         %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10
+   %bb_entry = OpLabel
+               OpLine %4 1 29
+               OpReturn
+               OpFunctionEnd
+)");
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+  const auto& param = GetParam();
+
+  std::ostringstream ss;
+  ss << R"(
+%foo = OpVariable %f32_ptr_function Function
+%decl = OpExtInst %void %DbgExt DebugDeclare )"
+     << param.first;
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, ss.str(), extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare,
+    ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+        std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"),
+        std::make_pair(R"(%foo_info %void %null_expr)", "Variable"),
+        std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"),
+    }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) {
+  const std::string dbg_inst_header = R"(
+%op0 = OpExtInst %void %DbgExt DebugOperation Deref
+%op1 = OpExtInst %void %DbgExt DebugOperation Plus
+%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) {
+  const std::string dbg_inst_header = R"(
+%op = OpExtInst %void %DbgExt DebugOperation Deref
+%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
+                                                     "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "expected operand Operation must be a result id of DebugOperation"));
+}
+
 TEST_P(ValidateGlslStd450SqrtLike, IntResultType) {
   const std::string ext_inst_name = GetParam();
   const std::string body =
diff --git a/third_party/SPIRV-Tools/test/val/val_id_test.cpp b/third_party/SPIRV-Tools/test/val/val_id_test.cpp
index 019d91a..adea563 100644
--- a/third_party/SPIRV-Tools/test/val/val_id_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_id_test.cpp
@@ -4355,6 +4355,17 @@
                         "'23' as an operand of <id> '24'."));
 }
 
+TEST_F(ValidateIdWithMessage, OpCopyObjectSampledImageGood) {
+  std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
+%smpld_img  = OpSampledImage %sampled_image_type %image_inst %sampler_inst
+%smpld_img2 = OpCopyObject %sampled_image_type %smpld_img
+%image_inst2 = OpCopyObject %image_type %image_inst
+OpReturn
+OpFunctionEnd)";
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 // Valid: Get a float in a matrix using CompositeExtract.
 // Valid: Insert float into a matrix using CompositeInsert.
 TEST_F(ValidateIdWithMessage, CompositeExtractInsertGood) {
diff --git a/third_party/SPIRV-Tools/test/val/val_memory_test.cpp b/third_party/SPIRV-Tools/test/val/val_memory_test.cpp
index 22761cc..b32867b 100644
--- a/third_party/SPIRV-Tools/test/val/val_memory_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_memory_test.cpp
@@ -57,8 +57,9 @@
                 "Variables identified with the UniformConstant storage class "
                 "are used only as handles to refer to opaque resources. Such "
                 "variables must be typed as OpTypeImage, OpTypeSampler, "
-                "OpTypeSampledImage, OpTypeAccelerationStructureNV, or an "
-                "array of one of these types."));
+                "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
+                "OpTypeAccelerationStructureKHR, OpTypeRayQueryProvisionalKHR, "
+                "or an array of one of these types."));
 }
 
 TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceGood) {
@@ -110,8 +111,9 @@
                 "Variables identified with the UniformConstant storage class "
                 "are used only as handles to refer to opaque resources. Such "
                 "variables must be typed as OpTypeImage, OpTypeSampler, "
-                "OpTypeSampledImage, OpTypeAccelerationStructureNV, or an "
-                "array of one of these types."));
+                "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
+                "OpTypeAccelerationStructureKHR, OpTypeRayQueryProvisionalKHR, "
+                "or an array of one of these types."));
 }
 
 TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceArrayGood) {
diff --git a/third_party/SPIRV-Tools/utils/check_copyright.py b/third_party/SPIRV-Tools/utils/check_copyright.py
index 2d288a1..4467a32 100755
--- a/third_party/SPIRV-Tools/utils/check_copyright.py
+++ b/third_party/SPIRV-Tools/utils/check_copyright.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+# coding=utf-8
 # Copyright (c) 2016 Google Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,10 +32,13 @@
            'Google Inc.',
            'Google LLC',
            'Pierre Moreau',
-           'Samsung Inc']
+           'Samsung Inc',
+           'André Perez Maselco',
+           'Vasyl Teliman',
+           'Advanced Micro Devices, Inc.']
 CURRENT_YEAR='2020'
 
-YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019|2020)'
+YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)'
 COPYRIGHT_RE = re.compile(
         'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
 
@@ -167,7 +171,7 @@
         has_apache2 = False
         line_num = 0
         apache_expected_end = 0
-        with open(file) as contents:
+        with open(file, encoding='utf-8') as contents:
             for line in contents:
                 line_num += 1
                 if COPYRIGHT_RE.search(line):
diff --git a/third_party/SPIRV-Tools/utils/generate_language_headers.py b/third_party/SPIRV-Tools/utils/generate_language_headers.py
index 0296163..83fa99e 100755
--- a/third_party/SPIRV-Tools/utils/generate_language_headers.py
+++ b/third_party/SPIRV-Tools/utils/generate_language_headers.py
@@ -159,27 +159,25 @@
     import argparse
     parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar')
 
-    parser.add_argument('--extinst-name',
-                        type=str, required=True,
-                        help='The name to use in tokens')
     parser.add_argument('--extinst-grammar', metavar='<path>',
                         type=str, required=True,
                         help='input JSON grammar file for extended instruction set')
-    parser.add_argument('--extinst-output-base', metavar='<path>',
+    parser.add_argument('--extinst-output-path', metavar='<path>',
                         type=str, required=True,
-                        help='Basename of the language-specific output file.')
+                        help='Path of the language-specific output file.')
     args = parser.parse_args()
 
     with open(args.extinst_grammar) as json_file:
         grammar_json = json.loads(json_file.read())
-        grammar = ExtInstGrammar(name = args.extinst_name,
+        grammar_name = os.path.splitext(os.path.basename(args.extinst_output_path))[0]
+        grammar = ExtInstGrammar(name = grammar_name,
                                  copyright = grammar_json['copyright'],
                                  instructions = grammar_json['instructions'],
                                  operand_kinds = grammar_json['operand_kinds'],
                                  version = grammar_json['version'],
                                  revision = grammar_json['revision'])
-        make_path_to_file(args.extinst_output_base)
-        with open(args.extinst_output_base + '.h', 'w') as f:
+        make_path_to_file(args.extinst_output_path)
+        with open(args.extinst_output_path, 'w') as f:
             f.write(CGenerator().generate(grammar))
 
 
diff --git a/third_party/SPIRV-Tools/utils/generate_registry_tables.py b/third_party/SPIRV-Tools/utils/generate_registry_tables.py
index e662ba9..28152ef 100755
--- a/third_party/SPIRV-Tools/utils/generate_registry_tables.py
+++ b/third_party/SPIRV-Tools/utils/generate_registry_tables.py
@@ -14,11 +14,30 @@
 # limitations under the License.
 """Generates the vendor tool table from the SPIR-V XML registry."""
 
-import distutils.dir_util
+import errno
 import os.path
 import xml.etree.ElementTree
 
 
+def mkdir_p(directory):
+    """Make the directory, and all its ancestors as required.  Any of the
+    directories are allowed to already exist.
+    This is compatible with Python down to 3.0.
+    """
+
+    if directory == "":
+        # We're being asked to make the current directory.
+        return
+
+    try:
+        os.makedirs(directory)
+    except OSError as e:
+        if e.errno == errno.EEXIST and os.path.isdir(directory):
+            pass
+        else:
+            raise
+
+
 def generate_vendor_table(registry):
     """Returns a list of C style initializers for the registered vendors
     and their tools.
@@ -62,7 +81,7 @@
     with open(args.xml) as xml_in:
        registry = xml.etree.ElementTree.fromstring(xml_in.read())
 
-    distutils.dir_util.mkpath(os.path.dirname(args.generator_output))
+    mkdir_p(os.path.dirname(args.generator_output))
     with open(args.generator_output, 'w') as f:
       f.write(generate_vendor_table(registry))
 
diff --git a/third_party/SPIRV-Tools/utils/vscode/extension.js b/third_party/SPIRV-Tools/utils/vscode/extension.js
index f220172..e7fec28 100644
--- a/third_party/SPIRV-Tools/utils/vscode/extension.js
+++ b/third_party/SPIRV-Tools/utils/vscode/extension.js
@@ -63,4 +63,4 @@
 // this method is called when your extension is deactivated
 function deactivate() {
 }
-exports.deactivate = deactivate;
\ No newline at end of file
+exports.deactivate = deactivate;