Merge changes I3c4f10f7,I5b7ddc75

* changes:
  Update SPIR-V Tools to a61d07a72
  Squashed 'third_party/SPIRV-Tools/' changes from f7da52775..a61d07a72
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp b/third_party/SPIRV-Tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
index ad4cd0c..a2c1f2c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
@@ -176,12 +176,6 @@
             // We can thus infer "a = d"
             AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]);
           }
-          if (synonymous_.IsEquivalent(*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]});
-          }
         }
       }
       for (const auto& equation : GetEquations(rhs_dds[1])) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
index 4361283..40da497 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
@@ -107,8 +107,6 @@
 namespace {
 const uint32_t kIdBoundGap = 100;
 
-const uint32_t kTransformationLimit = 2000;
-
 }  // namespace
 
 Fuzzer::Fuzzer(spv_target_env target_env, MessageConsumer consumer,
@@ -367,21 +365,37 @@
   // that fuzzing stops if the number of repeated passes hits the limit on the
   // number of transformations that can be applied.
   assert(
-      num_repeated_passes_applied_ <= kTransformationLimit &&
+      num_repeated_passes_applied_ <=
+          fuzzer_context_->GetTransformationLimit() &&
       "The number of repeated passes applied must not exceed its upper limit.");
-  if (num_repeated_passes_applied_ == kTransformationLimit) {
+  if (ir_context_->module()->id_bound() >= fuzzer_context_->GetIdBoundLimit()) {
+    return false;
+  }
+  if (num_repeated_passes_applied_ ==
+      fuzzer_context_->GetTransformationLimit()) {
     // Stop because fuzzing has got stuck.
     return false;
   }
   auto transformations_applied_so_far =
       static_cast<uint32_t>(transformation_sequence_out_.transformation_size());
-  if (transformations_applied_so_far >= kTransformationLimit) {
+  if (transformations_applied_so_far >=
+      fuzzer_context_->GetTransformationLimit()) {
     // Stop because we have reached the transformation limit.
     return false;
   }
+  // If we have applied T transformations so far, and the limit on the number of
+  // transformations to apply is L (where T < L), the chance that we will
+  // continue fuzzing is:
+  //
+  //     1 - T/(2*L)
+  //
+  // That is, the chance of continuing decreases as more transformations are
+  // applied.  Using 2*L instead of L increases the number of transformations
+  // that are applied on average.
   auto chance_of_continuing = static_cast<uint32_t>(
       100.0 * (1.0 - (static_cast<double>(transformations_applied_so_far) /
-                      static_cast<double>(kTransformationLimit))));
+                      (2.0 * static_cast<double>(
+                                 fuzzer_context_->GetTransformationLimit())))));
   if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) {
     // We have probabilistically decided to stop.
     return false;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
index 65904dc..47bf4e2 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
@@ -20,14 +20,19 @@
 namespace fuzz {
 
 namespace {
+
+// Limits to help control the overall fuzzing process and rein in individual
+// fuzzer passes.
+const uint32_t kIdBoundLimit = 50000;
+const uint32_t kTransformationLimit = 2000;
+
 // Default <minimum, maximum> pairs of probabilities for applying various
 // transformations. All values are percentages. Keep them in alphabetical order.
-
 const std::pair<uint32_t, uint32_t>
-    kChanceOfAcceptingRepeatedPassRecommendation = {70, 100};
+    kChanceOfAcceptingRepeatedPassRecommendation = {50, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingAccessChain = {5, 50};
-const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherPassToPassLoop = {85,
-                                                                            95};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherPassToPassLoop = {50,
+                                                                            90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
                                                                          90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
@@ -392,5 +397,11 @@
   return static_cast<protobufs::TransformationAddSynonym::SynonymType>(result);
 }
 
+uint32_t FuzzerContext::GetIdBoundLimit() const { return kIdBoundLimit; }
+
+uint32_t FuzzerContext::GetTransformationLimit() const {
+  return kTransformationLimit;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
index 9193dfc..8c51041 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
@@ -104,240 +104,271 @@
   // Returns a vector of |count| fresh ids.
   std::vector<uint32_t> GetFreshIds(const uint32_t count);
 
+  // A suggested limit on the id bound for the module being fuzzed.  This is
+  // useful for deciding when to stop the overall fuzzing process.  Furthermore,
+  // fuzzer passes that run the risk of spiralling out of control can
+  // periodically check this limit and terminate early if it has been reached.
+  uint32_t GetIdBoundLimit() const;
+
+  // A suggested limit on the number of transformations that should be applied.
+  // Also useful to control the overall fuzzing process and rein in individual
+  // fuzzer passes.
+  uint32_t GetTransformationLimit() const;
+
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
-  uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() {
+  uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() const {
     return chance_of_accepting_repeated_pass_recommendation_;
   }
-  uint32_t GetChanceOfAddingAccessChain() {
+  uint32_t GetChanceOfAddingAccessChain() const {
     return chance_of_adding_access_chain_;
   }
-  uint32_t GetChanceOfAddingAnotherPassToPassLoop() {
+  uint32_t GetChanceOfAddingAnotherPassToPassLoop() const {
     return chance_of_adding_another_pass_to_pass_loop_;
   }
-  uint32_t GetChanceOfAddingAnotherStructField() {
+  uint32_t GetChanceOfAddingAnotherStructField() const {
     return chance_of_adding_another_struct_field_;
   }
-  uint32_t GetChanceOfAddingArrayOrStructType() {
+  uint32_t GetChanceOfAddingArrayOrStructType() const {
     return chance_of_adding_array_or_struct_type_;
   }
-  uint32_t GetChanceOfAddingBitInstructionSynonym() {
+  uint32_t GetChanceOfAddingBitInstructionSynonym() const {
     return chance_of_adding_bit_instruction_synonym_;
   }
-  uint32_t GetChanceOfAddingBothBranchesWhenReplacingOpSelect() {
+  uint32_t GetChanceOfAddingBothBranchesWhenReplacingOpSelect() const {
     return chance_of_adding_both_branches_when_replacing_opselect_;
   }
-  uint32_t GetChanceOfAddingCompositeExtract() {
+  uint32_t GetChanceOfAddingCompositeExtract() const {
     return chance_of_adding_composite_extract_;
   }
-  uint32_t GetChanceOfAddingCompositeInsert() {
+  uint32_t GetChanceOfAddingCompositeInsert() const {
     return chance_of_adding_composite_insert_;
   }
-  uint32_t GetChanceOfAddingCopyMemory() {
+  uint32_t GetChanceOfAddingCopyMemory() const {
     return chance_of_adding_copy_memory_;
   }
-  uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; }
-  uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
-  uint32_t GetChanceOfAddingDeadContinue() {
+  uint32_t GetChanceOfAddingDeadBlock() const {
+    return chance_of_adding_dead_block_;
+  }
+  uint32_t GetChanceOfAddingDeadBreak() const {
+    return chance_of_adding_dead_break_;
+  }
+  uint32_t GetChanceOfAddingDeadContinue() const {
     return chance_of_adding_dead_continue_;
   }
-  uint32_t GetChanceOfAddingEquationInstruction() {
+  uint32_t GetChanceOfAddingEquationInstruction() const {
     return chance_of_adding_equation_instruction_;
   }
-  uint32_t GetChanceOfAddingGlobalVariable() {
+  uint32_t GetChanceOfAddingGlobalVariable() const {
     return chance_of_adding_global_variable_;
   }
-  uint32_t GetChanceOfAddingImageSampleUnusedComponents() {
+  uint32_t GetChanceOfAddingImageSampleUnusedComponents() const {
     return chance_of_adding_image_sample_unused_components_;
   }
-  uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; }
-  uint32_t GetChanceOfAddingLocalVariable() {
+  uint32_t GetChanceOfAddingLoad() const { return chance_of_adding_load_; }
+  uint32_t GetChanceOfAddingLocalVariable() const {
     return chance_of_adding_local_variable_;
   }
-  uint32_t GetChanceOfAddingLoopPreheader() {
+  uint32_t GetChanceOfAddingLoopPreheader() const {
     return chance_of_adding_loop_preheader_;
   }
-  uint32_t GetChanceOfAddingMatrixType() {
+  uint32_t GetChanceOfAddingMatrixType() const {
     return chance_of_adding_matrix_type_;
   }
-  uint32_t GetChanceOfAddingNoContractionDecoration() {
+  uint32_t GetChanceOfAddingNoContractionDecoration() const {
     return chance_of_adding_no_contraction_decoration_;
   }
-  uint32_t GetChanceOfAddingOpPhiSynonym() {
+  uint32_t GetChanceOfAddingOpPhiSynonym() const {
     return chance_of_adding_opphi_synonym_;
   }
-  uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
-  uint32_t GetChanceOfAddingRelaxedDecoration() {
+  uint32_t GetChanceOfAddingParameters() const {
+    return chance_of_adding_parameters;
+  }
+  uint32_t GetChanceOfAddingRelaxedDecoration() const {
     return chance_of_adding_relaxed_decoration_;
   }
-  uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
-  uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
-  uint32_t GetChanceOfAddingTrueBranchWhenReplacingOpSelect() {
+  uint32_t GetChanceOfAddingStore() const { return chance_of_adding_store_; }
+  uint32_t GetChanceOfAddingSynonyms() const {
+    return chance_of_adding_synonyms_;
+  }
+  uint32_t GetChanceOfAddingTrueBranchWhenReplacingOpSelect() const {
     return chance_of_adding_true_branch_when_replacing_opselect_;
   }
-  uint32_t GetChanceOfAddingVectorShuffle() {
+  uint32_t GetChanceOfAddingVectorShuffle() const {
     return chance_of_adding_vector_shuffle_;
   }
-  uint32_t GetChanceOfAddingVectorType() {
+  uint32_t GetChanceOfAddingVectorType() const {
     return chance_of_adding_vector_type_;
   }
-  uint32_t GetChanceOfAdjustingBranchWeights() {
+  uint32_t GetChanceOfAdjustingBranchWeights() const {
     return chance_of_adjusting_branch_weights_;
   }
-  uint32_t GetChanceOfAdjustingFunctionControl() {
+  uint32_t GetChanceOfAdjustingFunctionControl() const {
     return chance_of_adjusting_function_control_;
   }
-  uint32_t GetChanceOfAdjustingLoopControl() {
+  uint32_t GetChanceOfAdjustingLoopControl() const {
     return chance_of_adjusting_loop_control_;
   }
-  uint32_t GetChanceOfAdjustingMemoryOperandsMask() {
+  uint32_t GetChanceOfAdjustingMemoryOperandsMask() const {
     return chance_of_adjusting_memory_operands_mask_;
   }
-  uint32_t GetChanceOfAdjustingSelectionControl() {
+  uint32_t GetChanceOfAdjustingSelectionControl() const {
     return chance_of_adjusting_selection_control_;
   }
-  uint32_t GetChanceOfCallingFunction() { return chance_of_calling_function_; }
-  uint32_t GetChanceOfChoosingStructTypeVsArrayType() {
+  uint32_t GetChanceOfCallingFunction() const {
+    return chance_of_calling_function_;
+  }
+  uint32_t GetChanceOfChoosingStructTypeVsArrayType() const {
     return chance_of_choosing_struct_type_vs_array_type_;
   }
-  uint32_t GetChanceOfChoosingWorkgroupStorageClass() {
+  uint32_t GetChanceOfChoosingWorkgroupStorageClass() const {
     return chance_of_choosing_workgroup_storage_class_;
   }
-  uint32_t GetChanceOfConstructingComposite() {
+  uint32_t GetChanceOfConstructingComposite() const {
     return chance_of_constructing_composite_;
   }
-  uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
-  uint32_t GetChanceOfCreatingIntSynonymsUsingLoops() {
+  uint32_t GetChanceOfCopyingObject() const {
+    return chance_of_copying_object_;
+  }
+  uint32_t GetChanceOfCreatingIntSynonymsUsingLoops() const {
     return chance_of_creating_int_synonyms_using_loops_;
   }
-  uint32_t GetChanceOfDonatingAdditionalModule() {
+  uint32_t GetChanceOfDonatingAdditionalModule() const {
     return chance_of_donating_additional_module_;
   }
-  uint32_t GetChanceOfDuplicatingRegionWithSelection() {
+  uint32_t GetChanceOfDuplicatingRegionWithSelection() const {
     return chance_of_duplicating_region_with_selection_;
   }
-  uint32_t GetChanceOfExpandingVectorReduction() {
+  uint32_t GetChanceOfExpandingVectorReduction() const {
     return chance_of_expanding_vector_reduction_;
   }
-  uint32_t GetChanceOfFlatteningConditionalBranch() {
+  uint32_t GetChanceOfFlatteningConditionalBranch() const {
     return chance_of_flattening_conditional_branch_;
   }
-  uint32_t GetChanceOfGoingDeeperToExtractComposite() {
+  uint32_t GetChanceOfGoingDeeperToExtractComposite() const {
     return chance_of_going_deeper_to_extract_composite_;
   }
-  uint32_t GetChanceOfGoingDeeperToInsertInComposite() {
+  uint32_t GetChanceOfGoingDeeperToInsertInComposite() const {
     return chance_of_going_deeper_to_insert_in_composite_;
   }
-  uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
+  uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() const {
     return chance_of_going_deeper_when_making_access_chain_;
   }
-  uint32_t GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym() {
+  uint32_t GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym() const {
     return chance_of_having_two_blocks_in_loop_to_create_int_synonym_;
   }
-  uint32_t GetChanceOfInliningFunction() {
+  uint32_t GetChanceOfInliningFunction() const {
     return chance_of_inlining_function_;
   }
-  uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() {
+  uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() const {
     return chance_of_interchanging_signedness_of_integer_operands_;
   }
-  uint32_t GetChanceOfInterchangingZeroLikeConstants() {
+  uint32_t GetChanceOfInterchangingZeroLikeConstants() const {
     return chance_of_interchanging_zero_like_constants_;
   }
-  uint32_t GetChanceOfInvertingComparisonOperators() {
+  uint32_t GetChanceOfInvertingComparisonOperators() const {
     return chance_of_inverting_comparison_operators_;
   }
-  uint32_t ChanceOfMakingDonorLivesafe() {
+  uint32_t ChanceOfMakingDonorLivesafe() const {
     return chance_of_making_donor_livesafe_;
   }
-  uint32_t GetChanceOfMakingVectorOperationDynamic() {
+  uint32_t GetChanceOfMakingVectorOperationDynamic() const {
     return chance_of_making_vector_operation_dynamic_;
   }
-  uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; }
-  uint32_t GetChanceOfMergingFunctionReturns() {
+  uint32_t GetChanceOfMergingBlocks() const {
+    return chance_of_merging_blocks_;
+  }
+  uint32_t GetChanceOfMergingFunctionReturns() const {
     return chance_of_merging_function_returns_;
   }
-  uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
-  uint32_t GetChanceOfMutatingPointer() { return chance_of_mutating_pointer_; }
-  uint32_t GetChanceOfObfuscatingConstant() {
+  uint32_t GetChanceOfMovingBlockDown() const {
+    return chance_of_moving_block_down_;
+  }
+  uint32_t GetChanceOfMutatingPointer() const {
+    return chance_of_mutating_pointer_;
+  }
+  uint32_t GetChanceOfObfuscatingConstant() const {
     return chance_of_obfuscating_constant_;
   }
-  uint32_t GetChanceOfOutliningFunction() {
+  uint32_t GetChanceOfOutliningFunction() const {
     return chance_of_outlining_function_;
   }
-  uint32_t GetChanceOfPermutingInstructions() {
+  uint32_t GetChanceOfPermutingInstructions() const {
     return chance_of_permuting_instructions_;
   }
-  uint32_t GetChanceOfPermutingParameters() {
+  uint32_t GetChanceOfPermutingParameters() const {
     return chance_of_permuting_parameters_;
   }
-  uint32_t GetChanceOfPermutingPhiOperands() {
+  uint32_t GetChanceOfPermutingPhiOperands() const {
     return chance_of_permuting_phi_operands_;
   }
-  uint32_t GetChanceOfPropagatingInstructionsDown() {
+  uint32_t GetChanceOfPropagatingInstructionsDown() const {
     return chance_of_propagating_instructions_down_;
   }
-  uint32_t GetChanceOfPropagatingInstructionsUp() {
+  uint32_t GetChanceOfPropagatingInstructionsUp() const {
     return chance_of_propagating_instructions_up_;
   }
-  uint32_t GetChanceOfPushingIdThroughVariable() {
+  uint32_t GetChanceOfPushingIdThroughVariable() const {
     return chance_of_pushing_id_through_variable_;
   }
-  uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() {
+  uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() const {
     return chance_of_replacing_add_sub_mul_with_carrying_extended_;
   }
-  uint32_t GetChanceOfReplacingBranchFromDeadBlockWithExit() {
+  uint32_t GetChanceOfReplacingBranchFromDeadBlockWithExit() const {
     return chance_of_replacing_branch_from_dead_block_with_exit_;
   }
-  uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
+  uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() const {
     return chance_of_replacing_copy_memory_with_load_store_;
   }
-  uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() {
+  uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() const {
     return chance_of_replacing_copyobject_with_store_load_;
   }
-  uint32_t GetChanceOfReplacingIdWithSynonym() {
+  uint32_t GetChanceOfReplacingIdWithSynonym() const {
     return chance_of_replacing_id_with_synonym_;
   }
-  uint32_t GetChanceOfReplacingIrrelevantId() {
+  uint32_t GetChanceOfReplacingIrrelevantId() const {
     return chance_of_replacing_irrelevant_id_;
   }
-  uint32_t GetChanceOfReplacingLinearAlgebraInstructions() {
+  uint32_t GetChanceOfReplacingLinearAlgebraInstructions() const {
     return chance_of_replacing_linear_algebra_instructions_;
   }
-  uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
+  uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() const {
     return chance_of_replacing_load_store_with_copy_memory_;
   }
-  uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() {
+  uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() const {
     return chance_of_replacing_opphi_id_from_dead_predecessor_;
   }
-  uint32_t GetChanceOfReplacingOpselectWithConditionalBranch() {
+  uint32_t GetChanceOfReplacingOpselectWithConditionalBranch() const {
     return chance_of_replacing_opselect_with_conditional_branch_;
   }
-  uint32_t GetChanceOfReplacingParametersWithGlobals() {
+  uint32_t GetChanceOfReplacingParametersWithGlobals() const {
     return chance_of_replacing_parameters_with_globals_;
   }
-  uint32_t GetChanceOfReplacingParametersWithStruct() {
+  uint32_t GetChanceOfReplacingParametersWithStruct() const {
     return chance_of_replacing_parameters_with_struct_;
   }
-  uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
-  uint32_t GetChanceOfSwappingConditionalBranchOperands() {
+  uint32_t GetChanceOfSplittingBlock() const {
+    return chance_of_splitting_block_;
+  }
+  uint32_t GetChanceOfSwappingConditionalBranchOperands() const {
     return chance_of_swapping_conditional_branch_operands_;
   }
-  uint32_t GetChanceOfTogglingAccessChainInstruction() {
+  uint32_t GetChanceOfTogglingAccessChainInstruction() const {
     return chance_of_toggling_access_chain_instruction_;
   }
-  uint32_t GetChanceOfWrappingRegionInSelection() {
+  uint32_t GetChanceOfWrappingRegionInSelection() const {
     return chance_of_wrapping_region_in_selection_;
   }
 
   // Other functions to control transformations. Keep them in alphabetical
   // order.
-  uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() {
+  uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() const {
     return max_equivalence_class_size_for_data_synonym_fact_closure_;
   }
-  uint32_t GetMaximumNumberOfFunctionParameters() {
+  uint32_t GetMaximumNumberOfFunctionParameters() const {
     return max_number_of_function_parameters_;
   }
-  uint32_t GetMaximumNumberOfParametersReplacedWithStruct() {
+  uint32_t GetMaximumNumberOfParametersReplacedWithStruct() const {
     return max_number_of_parameters_replaced_with_struct_;
   }
   std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
index bd2dd3d..7b9ac4e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
@@ -35,6 +35,13 @@
   for (auto& function : *GetIRContext()->module()) {
     for (auto& block : function) {
       for (auto& instruction : block) {
+        // This fuzzer pass can add a *lot* of ids.  We bail out early if we hit
+        // the recommended id limit.
+        if (GetIRContext()->module()->id_bound() >=
+            GetFuzzerContext()->GetIdBoundLimit()) {
+          return;
+        }
+
         // Randomly decides whether the transformation will be applied.
         if (!GetFuzzerContext()->ChoosePercentage(
                 GetFuzzerContext()->GetChanceOfAddingBitInstructionSynonym())) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp
index 3b40d5f..132a49d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp
@@ -91,35 +91,29 @@
                   available_composites)];
           composite_id = inst->result_id();
 
-          const auto* type =
-              GetIRContext()->get_type_mgr()->GetType(inst->type_id());
-          assert(type && "Composite instruction has invalid type id");
-
+          auto type_id = inst->type_id();
           do {
             uint32_t number_of_members = 0;
 
-            if (const auto* array_type = type->AsArray()) {
-              const auto* type_inst =
-                  GetIRContext()->get_def_use_mgr()->GetDef(inst->type_id());
-              assert(type_inst && "Type instruction must exist");
+            const auto* type_inst =
+                GetIRContext()->get_def_use_mgr()->GetDef(type_id);
+            assert(type_inst && "Composite instruction has invalid type id");
 
-              number_of_members =
-                  fuzzerutil::GetArraySize(*type_inst, GetIRContext());
-              type = array_type->element_type();
-            } else if (const auto* vector_type = type->AsVector()) {
-              number_of_members = vector_type->element_count();
-              type = vector_type->element_type();
-            } else if (const auto* matrix_type = type->AsMatrix()) {
-              number_of_members = matrix_type->element_count();
-              type = matrix_type->element_type();
-            } else if (const auto* struct_type = type->AsStruct()) {
-              number_of_members =
-                  static_cast<uint32_t>(struct_type->element_types().size());
-              // The next value of |type| will be assigned when we know the
-              // index of the OpTypeStruct's operand.
-            } else {
-              assert(false && "|inst| is not a composite");
-              return;
+            switch (type_inst->opcode()) {
+              case SpvOpTypeArray:
+                number_of_members =
+                    fuzzerutil::GetArraySize(*type_inst, GetIRContext());
+                break;
+              case SpvOpTypeVector:
+              case SpvOpTypeMatrix:
+                number_of_members = type_inst->GetSingleWordInOperand(1);
+                break;
+              case SpvOpTypeStruct:
+                number_of_members = type_inst->NumInOperands();
+                break;
+              default:
+                assert(false && "|type_inst| is not a composite");
+                return;
             }
 
             if (number_of_members == 0) {
@@ -130,10 +124,21 @@
                 GetFuzzerContext()->GetRandomCompositeExtractIndex(
                     number_of_members));
 
-            if (const auto* struct_type = type->AsStruct()) {
-              type = struct_type->element_types()[indices.back()];
+            switch (type_inst->opcode()) {
+              case SpvOpTypeArray:
+              case SpvOpTypeVector:
+              case SpvOpTypeMatrix:
+                type_id = type_inst->GetSingleWordInOperand(0);
+                break;
+              case SpvOpTypeStruct:
+                type_id = type_inst->GetSingleWordInOperand(indices.back());
+                break;
+              default:
+                assert(false && "|type_inst| is not a composite");
+                return;
             }
-          } while (fuzzerutil::IsCompositeType(type) &&
+          } while (fuzzerutil::IsCompositeType(
+                       GetIRContext()->get_type_mgr()->GetType(type_id)) &&
                    GetFuzzerContext()->ChoosePercentage(
                        GetFuzzerContext()
                            ->GetChanceOfGoingDeeperToExtractComposite()));
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
index c7c2933..1e21aa5 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
@@ -71,7 +71,8 @@
       // Do not consider this header if the conditional cannot be flattened.
       if (!TransformationFlattenConditionalBranch::
               GetProblematicInstructionsIfConditionalCanBeFlattened(
-                  GetIRContext(), header, &instructions_that_need_ids)) {
+                  GetIRContext(), header, *GetTransformationContext(),
+                  &instructions_that_need_ids)) {
         continue;
       }
 
@@ -214,7 +215,7 @@
           }
         }
 
-        wrappers_info.emplace_back(wrapper_info);
+        wrappers_info.push_back(std::move(wrapper_info));
       }
 
       // Apply the transformation, evenly choosing whether to lay out the true
diff --git a/third_party/SPIRV-Tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp b/third_party/SPIRV-Tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp
index 920d13f..6d77f25 100644
--- a/third_party/SPIRV-Tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp
@@ -53,6 +53,8 @@
     result = recommended_passes_.front();
     recommended_passes_.pop_front();
   }
+  assert(result != nullptr && "A pass must have been chosen.");
+  last_pass_choice_ = result;
   return result;
 }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp
index 9fdc37e..6cdfdfb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp
@@ -46,7 +46,8 @@
   // |instruction| must be defined and must be a supported bit instruction.
   if (!instruction || (instruction->opcode() != SpvOpBitwiseOr &&
                        instruction->opcode() != SpvOpBitwiseXor &&
-                       instruction->opcode() != SpvOpBitwiseAnd)) {
+                       instruction->opcode() != SpvOpBitwiseAnd &&
+                       instruction->opcode() != SpvOpNot)) {
     return false;
   }
 
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.cpp
index bad4972..dec933c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.cpp
@@ -82,65 +82,6 @@
     }
   }
 
-  if (OpSelectArgumentsAreRestricted(ir_context)) {
-    // OpPhi instructions at the convergence block for the selection are handled
-    // by turning them into OpSelect instructions.  As the SPIR-V version in use
-    // has restrictions on the arguments that OpSelect can take, we must check
-    // that any OpPhi instructions are compatible with these restrictions.
-    uint32_t convergence_block_id =
-        FindConvergenceBlock(ir_context, *header_block);
-    // Consider every OpPhi instruction at the convergence block.
-    if (!ir_context->cfg()
-             ->block(convergence_block_id)
-             ->WhileEachPhiInst([this,
-                                 ir_context](opt::Instruction* inst) -> bool {
-               // Decide whether the OpPhi can be handled based on its result
-               // type.
-               opt::Instruction* phi_result_type =
-                   ir_context->get_def_use_mgr()->GetDef(inst->type_id());
-               switch (phi_result_type->opcode()) {
-                 case SpvOpTypeBool:
-                 case SpvOpTypeInt:
-                 case SpvOpTypeFloat:
-                 case SpvOpTypePointer:
-                   // Fine: OpSelect can work directly on scalar and pointer
-                   // types.
-                   return true;
-                 case SpvOpTypeVector: {
-                   // In its restricted form, OpSelect can only select between
-                   // vectors if the condition of the select is a boolean
-                   // boolean vector.  We thus require the appropriate boolean
-                   // vector type to be present.
-                   uint32_t bool_type_id =
-                       fuzzerutil::MaybeGetBoolType(ir_context);
-                   uint32_t dimension =
-                       phi_result_type->GetSingleWordInOperand(1);
-                   if (fuzzerutil::MaybeGetVectorType(ir_context, bool_type_id,
-                                                      dimension) == 0) {
-                     // The required boolean vector type is not present.
-                     return false;
-                   }
-                   // The transformation needs to be equipped with a fresh id
-                   // in which to store the vectorized version of the selection
-                   // construct's condition.
-                   switch (dimension) {
-                     case 2:
-                       return message_.fresh_id_for_bvec2_selector() != 0;
-                     case 3:
-                       return message_.fresh_id_for_bvec3_selector() != 0;
-                     default:
-                       assert(dimension == 4 && "Invalid vector dimension.");
-                       return message_.fresh_id_for_bvec4_selector() != 0;
-                   }
-                 }
-                 default:
-                   return false;
-               }
-             })) {
-      return false;
-    }
-  }
-
   // Use a set to keep track of the instructions that require fresh ids.
   std::set<opt::Instruction*> instructions_that_need_ids;
 
@@ -148,7 +89,8 @@
   // if so, add all the problematic instructions that need to be enclosed inside
   // conditionals to |instructions_that_need_ids|.
   if (!GetProblematicInstructionsIfConditionalCanBeFlattened(
-          ir_context, header_block, &instructions_that_need_ids)) {
+          ir_context, header_block, transformation_context,
+          &instructions_that_need_ids)) {
     return false;
   }
 
@@ -205,6 +147,69 @@
     }
   }
 
+  if (OpSelectArgumentsAreRestricted(ir_context)) {
+    // OpPhi instructions at the convergence block for the selection are handled
+    // by turning them into OpSelect instructions.  As the SPIR-V version in use
+    // has restrictions on the arguments that OpSelect can take, we must check
+    // that any OpPhi instructions are compatible with these restrictions.
+    uint32_t convergence_block_id =
+        FindConvergenceBlock(ir_context, *header_block);
+    // Consider every OpPhi instruction at the convergence block.
+    if (!ir_context->cfg()
+             ->block(convergence_block_id)
+             ->WhileEachPhiInst([this,
+                                 ir_context](opt::Instruction* inst) -> bool {
+               // Decide whether the OpPhi can be handled based on its result
+               // type.
+               opt::Instruction* phi_result_type =
+                   ir_context->get_def_use_mgr()->GetDef(inst->type_id());
+               switch (phi_result_type->opcode()) {
+                 case SpvOpTypeBool:
+                 case SpvOpTypeInt:
+                 case SpvOpTypeFloat:
+                 case SpvOpTypePointer:
+                   // Fine: OpSelect can work directly on scalar and pointer
+                   // types.
+                   return true;
+                 case SpvOpTypeVector: {
+                   // In its restricted form, OpSelect can only select between
+                   // vectors if the condition of the select is a boolean
+                   // boolean vector.  We thus require the appropriate boolean
+                   // vector type to be present.
+                   uint32_t bool_type_id =
+                       fuzzerutil::MaybeGetBoolType(ir_context);
+                   if (!bool_type_id) {
+                     return false;
+                   }
+
+                   uint32_t dimension =
+                       phi_result_type->GetSingleWordInOperand(1);
+                   if (fuzzerutil::MaybeGetVectorType(ir_context, bool_type_id,
+                                                      dimension) == 0) {
+                     // The required boolean vector type is not present.
+                     return false;
+                   }
+                   // The transformation needs to be equipped with a fresh id
+                   // in which to store the vectorized version of the selection
+                   // construct's condition.
+                   switch (dimension) {
+                     case 2:
+                       return message_.fresh_id_for_bvec2_selector() != 0;
+                     case 3:
+                       return message_.fresh_id_for_bvec3_selector() != 0;
+                     default:
+                       assert(dimension == 4 && "Invalid vector dimension.");
+                       return message_.fresh_id_for_bvec4_selector() != 0;
+                   }
+                 }
+                 default:
+                   return false;
+               }
+             })) {
+      return false;
+    }
+  }
+
   // All checks were passed.
   return true;
 }
@@ -428,6 +433,7 @@
 bool TransformationFlattenConditionalBranch::
     GetProblematicInstructionsIfConditionalCanBeFlattened(
         opt::IRContext* ir_context, opt::BasicBlock* header,
+        const TransformationContext& transformation_context,
         std::set<opt::Instruction*>* instructions_that_need_ids) {
   uint32_t merge_block_id = header->MergeBlockIdIfAny();
   assert(merge_block_id &&
@@ -441,6 +447,11 @@
   auto postdominator_analysis =
       ir_context->GetPostDominatorAnalysis(enclosing_function);
 
+  // |header| must be reachable.
+  if (!dominator_analysis->IsReachable(header)) {
+    return false;
+  }
+
   // Check that the header and the merge block describe a single-entry,
   // single-exit region.
   if (!dominator_analysis->Dominates(header->id(), merge_block_id) ||
@@ -454,13 +465,22 @@
   //  - they branch unconditionally to another block
   //  Add any side-effecting instruction, requiring fresh ids, to
   //  |instructions_that_need_ids|
-  std::list<uint32_t> to_check;
+  std::queue<uint32_t> to_check;
   header->ForEachSuccessorLabel(
-      [&to_check](uint32_t label) { to_check.push_back(label); });
+      [&to_check](uint32_t label) { to_check.push(label); });
 
+  auto* structured_cfg = ir_context->GetStructuredCFGAnalysis();
   while (!to_check.empty()) {
     uint32_t block_id = to_check.front();
-    to_check.pop_front();
+    to_check.pop();
+
+    if (structured_cfg->ContainingConstruct(block_id) != header->id() &&
+        block_id != merge_block_id) {
+      // This block can be reached from the |header| but doesn't belong to its
+      // selection construct. This might be a continue target of some loop -
+      // we can't flatten the |header|.
+      return false;
+    }
 
     // If the block post-dominates the header, this is where flow converges, and
     // we don't need to check this branch any further, because the
@@ -470,6 +490,15 @@
       continue;
     }
 
+    if (!transformation_context.GetFactManager()->BlockIsDead(header->id()) &&
+        transformation_context.GetFactManager()->BlockIsDead(block_id)) {
+      // The |header| is not dead but the |block_id| is. Since |block_id|
+      // doesn't postdominate the |header|, CFG hasn't converged yet. Thus, we
+      // don't flatten the construct to prevent |block_id| from becoming
+      // executable.
+      return false;
+    }
+
     auto block = ir_context->cfg()->block(block_id);
 
     // The block must not have a merge instruction, because inner constructs are
@@ -518,7 +547,7 @@
 
     // Add the successor of this block to the list of blocks that need to be
     // checked.
-    to_check.push_back(block->terminator()->GetSingleWordInOperand(0));
+    to_check.push(block->terminator()->GetSingleWordInOperand(0));
   }
 
   // All the blocks are compatible with the transformation and this is indeed a
@@ -564,7 +593,7 @@
     opt::Instruction* instruction,
     const protobufs::SideEffectWrapperInfo& wrapper_info, uint32_t condition_id,
     bool exec_if_cond_true, std::vector<uint32_t>* dead_blocks,
-    std::vector<uint32_t>* irrelevant_ids) const {
+    std::vector<uint32_t>* irrelevant_ids) {
   // Get the next instruction (it will be useful for splitting).
   auto next_instruction = instruction->NextNode();
 
@@ -810,7 +839,7 @@
 void TransformationFlattenConditionalBranch::AddBooleanVectorConstructorToBlock(
     uint32_t fresh_id, uint32_t dimension,
     const opt::Operand& branch_condition_operand, opt::IRContext* ir_context,
-    opt::BasicBlock* block) const {
+    opt::BasicBlock* block) {
   opt::Instruction::OperandList in_operands;
   for (uint32_t i = 0; i < dimension; i++) {
     in_operands.emplace_back(branch_condition_operand);
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.h b/third_party/SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.h
index 2d5e8d7..e8cb414 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.h
@@ -72,6 +72,7 @@
   // instructions are OpSelectionMerge and OpBranchConditional.
   static bool GetProblematicInstructionsIfConditionalCanBeFlattened(
       opt::IRContext* ir_context, opt::BasicBlock* header,
+      const TransformationContext& transformation_context,
       std::set<opt::Instruction*>* instructions_that_need_ids);
 
   // Returns true iff the given instruction needs a placeholder to be enclosed
@@ -117,14 +118,14 @@
   // |dead_blocks| and |irrelevant_ids| are used to record the ids of blocks
   // and instructions for which dead block and irrelevant id facts should
   // ultimately be created.
-  opt::BasicBlock* EncloseInstructionInConditional(
+  static opt::BasicBlock* EncloseInstructionInConditional(
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context,
       opt::BasicBlock* block, opt::Instruction* instruction,
       const protobufs::SideEffectWrapperInfo& wrapper_info,
       uint32_t condition_id, bool exec_if_cond_true,
       std::vector<uint32_t>* dead_blocks,
-      std::vector<uint32_t>* irrelevant_ids) const;
+      std::vector<uint32_t>* irrelevant_ids);
 
   // Turns every OpPhi instruction of |convergence_block| -- the convergence
   // block for |header_block| (both in |ir_context|) into an OpSelect
@@ -137,10 +138,10 @@
   // |ir_context|, with result id given by |fresh_id|.  The instruction will
   // make a |dimension|-dimensional boolean vector with
   // |branch_condition_operand| at every component.
-  void AddBooleanVectorConstructorToBlock(
+  static void AddBooleanVectorConstructorToBlock(
       uint32_t fresh_id, uint32_t dimension,
       const opt::Operand& branch_condition_operand, opt::IRContext* ir_context,
-      opt::BasicBlock* block) const;
+      opt::BasicBlock* block);
 
   // Returns true if the given instruction either has no side effects or it can
   // be handled by being enclosed in a conditional.
diff --git a/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp b/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
index 48285bd..87ef655 100644
--- a/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
+++ b/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
@@ -384,7 +384,8 @@
   return dbg_decl_itr != var_id_to_dbg_decl_.end();
 }
 
-void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
+bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
+  bool modified = false;
   auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
   if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
     // We intentionally copy the list of DebugDeclare instructions because
@@ -394,9 +395,11 @@
 
     for (auto* dbg_decl : copy_dbg_decls) {
       context()->KillInst(dbg_decl);
+      modified = true;
     }
     var_id_to_dbg_decl_.erase(dbg_decl_itr);
   }
+  return modified;
 }
 
 uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
@@ -514,16 +517,18 @@
   return added_dbg_value;
 }
 
-void DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
+bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
     Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
     Instruction* insert_pos,
     std::unordered_set<Instruction*>* invisible_decls) {
-  auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
-  if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
+  assert(scope_and_line != nullptr);
 
+  auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+  if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return false;
+
+  bool modified = false;
   for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
-    if (scope_and_line &&
-        !IsDeclareVisibleToInstr(dbg_decl_or_val, scope_and_line)) {
+    if (!IsDeclareVisibleToInstr(dbg_decl_or_val, scope_and_line)) {
       if (invisible_decls) invisible_decls->insert(dbg_decl_or_val);
       continue;
     }
@@ -547,10 +552,11 @@
                                    kDebugValueOperandLocalVariableIndex),
                                value_id, 0, index_id, insert_before);
     assert(added_dbg_value != nullptr);
-    added_dbg_value->UpdateDebugInfoFrom(scope_and_line ? scope_and_line
-                                                        : dbg_decl_or_val);
+    added_dbg_value->UpdateDebugInfoFrom(scope_and_line);
     AnalyzeDebugInst(added_dbg_value);
+    modified = true;
   }
+  return modified;
 }
 
 bool DebugInfoManager::AddDebugValueForDecl(Instruction* dbg_decl,
diff --git a/third_party/SPIRV-Tools/source/opt/debug_info_manager.h b/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
index edb11b7..630be4f 100644
--- a/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
+++ b/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
@@ -138,14 +138,15 @@
   bool IsVariableDebugDeclared(uint32_t variable_id);
 
   // Kills all debug declaration instructions with Deref whose 'Local Variable'
-  // operand is |variable_id|.
-  void KillDebugDeclares(uint32_t variable_id);
+  // operand is |variable_id|. Returns whether it kills an instruction or not.
+  bool KillDebugDeclares(uint32_t variable_id);
 
   // Generates a DebugValue instruction with value |value_id| for every local
   // variable that is in the scope of |scope_and_line| and whose memory is
   // |variable_id| and inserts it after the instruction |insert_pos|.
-  // |invisible_decls| returns DebugDeclares invisible to |scope_and_line|.
-  void AddDebugValueIfVarDeclIsVisible(
+  // Returns whether a DebugValue is added or not. |invisible_decls| returns
+  // DebugDeclares invisible to |scope_and_line|.
+  bool AddDebugValueIfVarDeclIsVisible(
       Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
       Instruction* insert_pos,
       std::unordered_set<Instruction*>* invisible_decls);
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 f1b8177..99c0fb2 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
@@ -148,16 +148,41 @@
         context()->get_type_mgr()->GetType(var_inst->type_id());
     const analysis::Type* store_type = var_type->AsPointer()->pointee_type();
     if (!(store_type->AsStruct() || store_type->AsArray())) {
-      context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
-          nullptr, var_id, store_inst->GetSingleWordInOperand(1), store_inst,
-          nullptr);
-      context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
+      modified |= RewriteDebugDeclares(store_inst, var_id);
     }
   }
 
   return modified;
 }
 
+bool LocalSingleStoreElimPass::RewriteDebugDeclares(Instruction* store_inst,
+                                                    uint32_t var_id) {
+  std::unordered_set<Instruction*> invisible_decls;
+  uint32_t value_id = store_inst->GetSingleWordInOperand(1);
+  bool modified =
+      context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
+          store_inst, var_id, value_id, store_inst, &invisible_decls);
+
+  // For cases like the argument passing for an inlined function, the value
+  // assignment is out of DebugDeclare's scope, but we have to preserve the
+  // value assignment information using DebugValue. Generally, we need
+  // ssa-rewrite analysis to decide a proper value assignment but at this point
+  // we confirm that |var_id| has a single store. We can safely add DebugValue.
+  if (!invisible_decls.empty()) {
+    BasicBlock* store_block = context()->get_instr_block(store_inst);
+    DominatorAnalysis* dominator_analysis =
+        context()->GetDominatorAnalysis(store_block->GetParent());
+    for (auto* decl : invisible_decls) {
+      if (dominator_analysis->Dominates(store_inst, decl)) {
+        context()->get_debug_info_mgr()->AddDebugValueForDecl(decl, value_id);
+        modified = true;
+      }
+    }
+  }
+  modified |= context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
+  return modified;
+}
+
 Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
     Instruction* var_inst, const std::vector<Instruction*>& users) const {
   // Make sure there is exactly 1 store.
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h
index d66a441..3aa0f02 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h
@@ -94,6 +94,10 @@
   bool RewriteLoads(Instruction* store_inst,
                     const std::vector<Instruction*>& uses, bool* all_rewritten);
 
+  // Replaces DebugDeclares of |var_id| with DebugValues using the value
+  // assignment of |store_inst|.
+  bool RewriteDebugDeclares(Instruction* store_inst, uint32_t var_id);
+
   // Extensions supported by this pass.
   std::unordered_set<std::string> extensions_allowlist_;
 };
diff --git a/third_party/SPIRV-Tools/source/opt/optimizer.cpp b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
index bc14411..1ded2ee 100644
--- a/third_party/SPIRV-Tools/source/opt/optimizer.cpp
+++ b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
@@ -161,6 +161,7 @@
       .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateMergeReturnPass())
       .RegisterPass(CreateInlineExhaustivePass())
+      .RegisterPass(CreateEliminateDeadFunctionsPass())
       .RegisterPass(CreateAggressiveDCEPass())
       .RegisterPass(CreatePrivateToLocalPass())
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
diff --git a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp
index 5a56887..3ff0361 100644
--- a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp
@@ -47,6 +47,7 @@
 #include "source/opcode.h"
 #include "source/opt/cfg.h"
 #include "source/opt/mem_pass.h"
+#include "source/opt/types.h"
 #include "source/util/make_unique.h"
 
 // Debug logging (0: Off, 1-N: Verbosity level).  Replace this with the
@@ -326,32 +327,94 @@
 }
 
 bool SSARewriter::ProcessLoad(Instruction* inst, BasicBlock* bb) {
+  // Get the pointer that we are using to load from.
   uint32_t var_id = 0;
   (void)pass_->GetPtr(inst, &var_id);
-  if (pass_->IsTargetVar(var_id)) {
-    // Get the immediate reaching definition for |var_id|.
-    uint32_t val_id = GetReachingDef(var_id, bb);
+
+  // Get the immediate reaching definition for |var_id|.
+  //
+  // In the presence of variable pointers, the reaching definition may be
+  // another pointer.  For example, the following fragment:
+  //
+  //  %2 = OpVariable %_ptr_Input_float Input
+  // %11 = OpVariable %_ptr_Function__ptr_Input_float Function
+  //       OpStore %11 %2
+  // %12 = OpLoad %_ptr_Input_float %11
+  // %13 = OpLoad %float %12
+  //
+  // corresponds to the pseudo-code:
+  //
+  // layout(location = 0) in flat float *%2
+  // float %13;
+  // float *%12;
+  // float **%11;
+  // *%11 = %2;
+  // %12 = *%11;
+  // %13 = *%12;
+  //
+  // which ultimately, should correspond to:
+  //
+  // %13 = *%2;
+  //
+  // During rewriting, the pointer %12 is found to be replaceable by %2 (i.e.,
+  // load_replacement_[12] is 2). However, when processing the load
+  // %13 = *%12, the type of %12's reaching definition is another float
+  // pointer (%2), instead of a float value.
+  //
+  // When this happens, we need to continue looking up the reaching definition
+  // chain until we get to a float value or a non-target var (i.e. a variable
+  // that cannot be SSA replaced, like %2 in this case since it is a function
+  // argument).
+  analysis::DefUseManager* def_use_mgr = pass_->context()->get_def_use_mgr();
+  analysis::TypeManager* type_mgr = pass_->context()->get_type_mgr();
+  analysis::Type* load_type = type_mgr->GetType(inst->type_id());
+  uint32_t val_id = 0;
+  bool found_reaching_def = false;
+  while (!found_reaching_def) {
+    if (!pass_->IsTargetVar(var_id)) {
+      // If the variable we are loading from is not an SSA target (globals,
+      // function parameters), do nothing.
+      return true;
+    }
+
+    val_id = GetReachingDef(var_id, bb);
     if (val_id == 0) {
       return false;
     }
 
-    // Schedule a replacement for the result of this load instruction with
-    // |val_id|. After all the rewriting decisions are made, every use of
-    // this load will be replaced with |val_id|.
-    const uint32_t load_id = inst->result_id();
-    assert(load_replacement_.count(load_id) == 0);
-    load_replacement_[load_id] = val_id;
-    PhiCandidate* defining_phi = GetPhiCandidate(val_id);
-    if (defining_phi) {
-      defining_phi->AddUser(load_id);
+    // If the reaching definition is a pointer type different than the type of
+    // the instruction we are analyzing, then it must be a reference to another
+    // pointer (otherwise, this would be invalid SPIRV).  We continue
+    // de-referencing it by making |val_id| be |var_id|.
+    //
+    // NOTE: if there is no reaching definition instruction, it means |val_id|
+    // is an undef.
+    Instruction* reaching_def_inst = def_use_mgr->GetDef(val_id);
+    if (reaching_def_inst &&
+        !type_mgr->GetType(reaching_def_inst->type_id())->IsSame(load_type)) {
+      var_id = val_id;
+    } else {
+      found_reaching_def = true;
     }
+  }
+
+  // Schedule a replacement for the result of this load instruction with
+  // |val_id|. After all the rewriting decisions are made, every use of
+  // this load will be replaced with |val_id|.
+  uint32_t load_id = inst->result_id();
+  assert(load_replacement_.count(load_id) == 0);
+  load_replacement_[load_id] = val_id;
+  PhiCandidate* defining_phi = GetPhiCandidate(val_id);
+  if (defining_phi) {
+    defining_phi->AddUser(load_id);
+  }
 
 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
-    std::cerr << "\tFound load: "
-              << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
-              << " (replacement for %" << load_id << " is %" << val_id << ")\n";
+  std::cerr << "\tFound load: "
+            << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
+            << " (replacement for %" << load_id << " is %" << val_id << ")\n";
 #endif
-  }
+
   return true;
 }
 
@@ -390,8 +453,8 @@
     }
   }
 
-  // Seal |bb|. This means that all the stores in it have been scanned and it's
-  // ready to feed them into its successors.
+  // Seal |bb|. This means that all the stores in it have been scanned and
+  // it's ready to feed them into its successors.
   SealBlock(bb);
 
 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
@@ -504,8 +567,8 @@
   }
 
   // Scan uses for all inserted Phi instructions. Do this separately from the
-  // registration of the Phi instruction itself to avoid trying to analyze uses
-  // of Phi instructions that have not been registered yet.
+  // registration of the Phi instruction itself to avoid trying to analyze
+  // uses of Phi instructions that have not been registered yet.
   for (Instruction* phi_inst : generated_phis) {
     pass_->get_def_use_mgr()->AnalyzeInstUse(&*phi_inst);
   }
@@ -562,7 +625,8 @@
   // This candidate is now completed.
   phi_candidate->MarkComplete();
 
-  // If |phi_candidate| is not trivial, add it to the list of Phis to generate.
+  // If |phi_candidate| is not trivial, add it to the list of Phis to
+  // generate.
   if (TryRemoveTrivialPhi(phi_candidate) == phi_candidate->result_id()) {
     // If we could not remove |phi_candidate|, it means that it is complete
     // and not trivial. Add it to the list of Phis to generate.
diff --git a/third_party/SPIRV-Tools/source/val/validate_builtins.cpp b/third_party/SPIRV-Tools/source/val/validate_builtins.cpp
index 1d85f88..2c5604c 100644
--- a/third_party/SPIRV-Tools/source/val/validate_builtins.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_builtins.cpp
@@ -2603,17 +2603,19 @@
       assert(function_id_ == 0);
       for (const auto em :
            {SpvExecutionModelVertex, SpvExecutionModelTessellationEvaluation,
-            SpvExecutionModelGeometry}) {
+            SpvExecutionModelGeometry, SpvExecutionModelMeshNV}) {
         id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
-            std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
-                      this,
-                      "Vulkan spec doesn't allow BuiltIn Layer and "
-                      "ViewportIndex to be "
-                      "used for variables with Input storage class if "
-                      "execution model is Vertex, TessellationEvaluation, or "
-                      "Geometry.",
-                      em, decoration, built_in_inst, referenced_from_inst,
-                      std::placeholders::_1));
+            std::bind(
+                &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
+                std::string(
+                    _.VkErrorID((operand == SpvBuiltInLayer) ? 4274 : 4406) +
+                    "Vulkan spec doesn't allow BuiltIn Layer and "
+                    "ViewportIndex to be "
+                    "used for variables with Input storage class if "
+                    "execution model is Vertex, TessellationEvaluation, "
+                    "Geometry, or MeshNV."),
+                em, decoration, built_in_inst, referenced_from_inst,
+                std::placeholders::_1));
       }
     }
 
@@ -2621,11 +2623,12 @@
       assert(function_id_ == 0);
       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
-          "Vulkan spec doesn't allow BuiltIn Layer and "
-          "ViewportIndex to be "
-          "used for variables with Output storage class if "
-          "execution model is "
-          "Fragment.",
+          std::string(_.VkErrorID((operand == SpvBuiltInLayer) ? 4275 : 4407) +
+                      "Vulkan spec doesn't allow BuiltIn Layer and "
+                      "ViewportIndex to be "
+                      "used for variables with Output storage class if "
+                      "execution model is "
+                      "Fragment."),
           SpvExecutionModelFragment, decoration, built_in_inst,
           referenced_from_inst, std::placeholders::_1));
     }
diff --git a/third_party/SPIRV-Tools/source/val/validation_state.cpp b/third_party/SPIRV-Tools/source/val/validation_state.cpp
index e190b3c..cb69dda 100644
--- a/third_party/SPIRV-Tools/source/val/validation_state.cpp
+++ b/third_party/SPIRV-Tools/source/val/validation_state.cpp
@@ -1377,6 +1377,10 @@
       return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04265);
     case 4272:
       return VUID_WRAP(VUID-Layer-Layer-04272);
+    case 4274:
+      return VUID_WRAP(VUID-Layer-Layer-04274);
+    case 4275:
+      return VUID_WRAP(VUID-Layer-Layer-04275);
     case 4276:
       return VUID_WRAP(VUID-Layer-Layer-04276);
     case 4281:
@@ -1469,6 +1473,10 @@
       return VUID_WRAP(VUID-ViewIndex-ViewIndex-04403);
     case 4404:
       return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04404);
+    case 4406:
+      return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04406);
+    case 4407:
+      return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04407);
     case 4408:
       return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04408);
     case 4422:
@@ -1483,6 +1491,18 @@
       return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04426);
     case 4427:
       return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04427);
+    case 4484:
+      return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04484);
+    case 4485:
+      return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04485);
+    case 4486:
+      return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04486);
+    case 4490:
+      return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04490);
+    case 4491:
+      return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04491);
+    case 4492:
+      return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04492);
     default:
       return "";  // unknown id
   };
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
index 9760ed3..fa8f7bf 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
@@ -30,12 +30,12 @@
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %37 "main"
 
-; Types
+          ; Types
           %2 = OpTypeInt 32 0
           %3 = OpTypeVoid
           %4 = OpTypeFunction %3
 
-; Constants
+          ; Constants
           %5 = OpConstant %2 0
           %6 = OpConstant %2 1
           %7 = OpConstant %2 2
@@ -69,10 +69,22 @@
          %35 = OpConstant %2 30
          %36 = OpConstant %2 31
 
-; main function
+         ; main function
          %37 = OpFunction %3 None %4
          %38 = OpLabel
+
+         ; Supported bit instructions
          %39 = OpBitwiseOr %2 %5 %6
+         %40 = OpBitwiseXor %2 %7 %8
+         %41 = OpBitwiseAnd %2 %9 %10
+         %42 = OpNot %2 %11
+
+         ; Not yet supported bit instructions
+         %43 = OpShiftRightLogical %2 %12 %13
+         %44 = OpShiftRightArithmetic %2 %14 %15
+         %45 = OpShiftLeftLogical %2 %16 %17
+         %46 = OpBitReverse %2 %18
+         %47 = OpBitCount %2 %19
                OpReturn
                OpFunctionEnd
   )";
@@ -86,78 +98,94 @@
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
+
   // Tests undefined bit instruction.
   auto transformation = TransformationAddBitInstructionSynonym(
-      40, {41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,
-           54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,
-           67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
-           80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,
-           93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105,
-           106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
-           119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
-           132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
-           145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
-           158, 159, 160, 161, 162, 163, 164, 165, 166, 167});
+      48, {49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,
+           62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,
+           75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,  87,
+           88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100,
+           101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+           114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+           127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+           140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+           153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
+           166, 167, 168, 169, 170, 171, 172, 173, 174, 175});
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests false bit instruction.
   transformation = TransformationAddBitInstructionSynonym(
-      38, {40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,
-           53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,
-           66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,
-           79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,
-           92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
-           105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
-           118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
-           131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
-           144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
-           157, 158, 159, 160, 161, 162, 163, 164, 165, 166});
+      38, {48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,
+           61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,
+           74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,
+           87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,
+           100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+           113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+           126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+           139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+           152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+           165, 166, 167, 168, 169, 170, 171, 172, 173, 174});
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests the number of fresh ids being different than the necessary.
   transformation = TransformationAddBitInstructionSynonym(
       39,
-      {40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,
-       54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,
-       68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,
-       82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
-       96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
-       110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
-       124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
-       138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
-       152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165});
+      {48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,
+       62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,
+       76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
+       90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103,
+       104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+       118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+       132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
+       146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+       160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173});
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
   // Tests non-fresh ids.
   transformation = TransformationAddBitInstructionSynonym(
-      39, {38,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
-           52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,
-           65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
-           78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,
-           91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103,
-           104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
-           117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
-           130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
-           143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
-           156, 157, 158, 159, 160, 161, 162, 163, 164, 165});
+      40, {47,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,
+           61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,
+           74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,
+           87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,
+           100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+           113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+           126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+           139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+           152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+           165, 166, 167, 168, 169, 170, 171, 172, 173, 174});
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
 
-  // Tests applicable transformation.
+  // Tests unsupported transformation.
   transformation = TransformationAddBitInstructionSynonym(
-      39, {40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,
-           53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,
-           66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,
-           79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,
-           92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
-           105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
-           118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
-           131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
-           144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
-           157, 158, 159, 160, 161, 162, 163, 164, 165, 166});
+      43, {48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,
+           61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,
+           74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,
+           87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,
+           100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+           113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+           126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+           139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+           152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+           165, 166, 167, 168, 169, 170, 171, 172, 173, 174});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Tests supported transformation.
+  transformation = TransformationAddBitInstructionSynonym(
+      41, {48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,
+           61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,
+           74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,
+           87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,
+           100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+           113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+           126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+           139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+           152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+           165, 166, 167, 168, 169, 170, 171, 172, 173, 174});
   ASSERT_TRUE(
       transformation.IsApplicable(context.get(), transformation_context));
 }
@@ -238,6 +266,8 @@
            131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
            144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
            157, 158, 159, 160, 161, 162, 163, 164, 165, 166});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {})));
@@ -534,6 +564,8 @@
            96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
            110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
            124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
       MakeDataDescriptor(134, {}), MakeDataDescriptor(39, {})));
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_flatten_conditional_branch_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_flatten_conditional_branch_test.cpp
index 0aaf20e..e0697d4 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_flatten_conditional_branch_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_flatten_conditional_branch_test.cpp
@@ -2041,6 +2041,100 @@
   ASSERT_TRUE(IsEqual(env, expected, context.get()));
 }
 
+TEST(TransformationFlattenConditionalBranchTest, ContainsDeadBlocksTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpSelectionMerge %9 None
+               OpBranchConditional %7 %8 %9
+          %8 = OpLabel
+         %10 = OpCopyObject %6 %7
+               OpBranch %9
+          %9 = OpLabel
+         %11 = OpPhi %6 %10 %8 %7 %5
+         %12 = OpPhi %6 %7 %5 %10 %8
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  TransformationFlattenConditionalBranch transformation(5, true, 0, 0, 0, {});
+  ASSERT_TRUE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  transformation_context.GetFactManager()->AddFactBlockIsDead(8);
+
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationFlattenConditionalBranchTest, ContainsContinueBlockTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 320
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantFalse %6
+          %4 = OpFunction %2 None %3
+         %12 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+               OpLoopMerge %15 %14 None
+               OpBranchConditional %7 %5 %15
+          %5 = OpLabel
+               OpSelectionMerge %11 None
+               OpBranchConditional %7 %9 %10
+          %9 = OpLabel
+               OpBranch %11
+         %10 = OpLabel
+               OpBranch %14
+         %11 = OpLabel
+               OpBranch %14
+         %14 = OpLabel
+               OpBranch %13
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  spvtools::ValidatorOptions validator_options;
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {})
+                   .IsApplicable(context.get(), transformation_context));
+}
+
 }  // namespace
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp
index ebc89a6..d015dfb 100644
--- a/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp
@@ -1211,6 +1211,237 @@
   SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
 }
 
+TEST_F(LocalSingleStoreElimTest, UseStoreLineInfoForDebugValueLine) {
+  // When the store is in the scope of OpenCL.DebugInfo.100 DebugDeclare,
+  // the OpLine of the added OpenCL.DebugInfo.100 DebugValue must be the
+  // same with the OpLine of the store.
+  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 "simple.hlsl"
+          %8 = OpString "float"
+          %9 = OpString "VS_OUTPUT"
+         %10 = OpString "color"
+         %11 = OpString "pos"
+         %12 = OpString "main"
+         %13 = OpString ""
+         %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 %VS_OUTPUT "VS_OUTPUT"
+               OpMemberName %VS_OUTPUT 0 "pos"
+               OpMemberName %VS_OUTPUT 1 "color"
+               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_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+         %36 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+  %VS_OUTPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT
+%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%out_var_COLOR = OpVariable %_ptr_Output_v4float Output
+         %85 = OpExtInst %void %1 DebugOperation Deref
+         %81 = OpExtInst %void %1 DebugInfoNone
+         %52 = OpExtInst %void %1 DebugExpression
+         %40 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float
+         %41 = OpExtInst %void %1 DebugTypeVector %40 4
+         %42 = OpExtInst %void %1 DebugSource %7
+         %43 = OpExtInst %void %1 DebugCompilationUnit 1 4 %42 HLSL
+         %44 = OpExtInst %void %1 DebugTypeComposite %9 Structure %42 1 8 %43 %9 %uint_256 FlagIsProtected|FlagIsPrivate %45 %46
+         %46 = OpExtInst %void %1 DebugTypeMember %10 %41 %42 3 10 %44 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+         %45 = OpExtInst %void %1 DebugTypeMember %11 %41 %42 2 10 %44 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %47 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %44 %41 %41
+         %48 = OpExtInst %void %1 DebugFunction %12 %47 %42 6 1 %43 %13 FlagIsProtected|FlagIsPrivate 7 %81
+         %49 = OpExtInst %void %1 DebugLexicalBlock %42 7 38 %48
+         %50 = OpExtInst %void %1 DebugLocalVariable %14 %44 %42 8 13 %49 FlagIsLocal
+         %84 = OpExtInst %void %1 DebugExpression %85
+       %main = OpFunction %void None %36
+         %54 = OpLabel
+         %91 = OpExtInst %void %1 DebugScope %49
+               OpLine %7 7 23
+         %83 = OpVariable %_ptr_Function_v4float Function
+               OpLine %7 8 13
+         %87 = OpExtInst %void %1 DebugValue %50 %83 %84 %int_1
+               OpLine %7 7 23
+         %82 = OpVariable %_ptr_Function_v4float Function
+               OpLine %7 8 13
+         %86 = OpExtInst %void %1 DebugValue %50 %82 %84 %int_0
+               OpNoLine
+         %92 = OpExtInst %void %1 DebugNoScope
+         %55 = OpLoad %v4float %in_var_POSITION
+         %56 = OpLoad %v4float %in_var_COLOR
+;CHECK:   [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION
+;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR
+
+         %94 = OpExtInst %void %1 DebugScope %49
+               OpLine %7 9 3
+               OpStore %82 %55
+;CHECK: OpLine [[file:%\w+]] 9 3
+;CHECK: OpStore {{%\w+}} [[pos]]
+;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[vout:%\w+]] [[pos]] [[empty_expr:%\w+]] %int_0
+;CHECK: OpLine [[file]] 10 3
+;CHECK: OpStore {{%\w+}} [[color]]
+;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[vout]] [[color]] [[empty_expr]] %int_1
+
+               OpLine %7 10 3
+               OpStore %83 %56
+               OpLine %7 11 10
+         %90 = OpCompositeConstruct %VS_OUTPUT %55 %56
+               OpNoLine
+         %95 = OpExtInst %void %1 DebugNoScope
+         %58 = OpCompositeExtract %v4float %90 0
+               OpStore %gl_Position %58
+         %59 = OpCompositeExtract %v4float %90 1
+               OpStore %out_var_COLOR %59
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
+TEST_F(LocalSingleStoreElimTest, AddDebugValueforStoreOutOfDebugDeclareScope) {
+  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 "simple.hlsl"
+          %8 = OpString "float"
+          %9 = OpString "VS_OUTPUT"
+         %10 = OpString "color"
+         %11 = OpString "pos"
+         %12 = OpString "main"
+         %13 = OpString ""
+         %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 %VS_OUTPUT "VS_OUTPUT"
+               OpMemberName %VS_OUTPUT 0 "pos"
+               OpMemberName %VS_OUTPUT 1 "color"
+               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_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+         %36 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+  %VS_OUTPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT
+%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%out_var_COLOR = OpVariable %_ptr_Output_v4float Output
+         %85 = OpExtInst %void %1 DebugOperation Deref
+         %81 = OpExtInst %void %1 DebugInfoNone
+         %52 = OpExtInst %void %1 DebugExpression
+         %40 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float
+         %41 = OpExtInst %void %1 DebugTypeVector %40 4
+         %42 = OpExtInst %void %1 DebugSource %7
+         %43 = OpExtInst %void %1 DebugCompilationUnit 1 4 %42 HLSL
+         %44 = OpExtInst %void %1 DebugTypeComposite %9 Structure %42 1 8 %43 %9 %uint_256 FlagIsProtected|FlagIsPrivate %45 %46
+         %46 = OpExtInst %void %1 DebugTypeMember %10 %41 %42 3 10 %44 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+         %45 = OpExtInst %void %1 DebugTypeMember %11 %41 %42 2 10 %44 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %47 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %44 %41 %41
+         %48 = OpExtInst %void %1 DebugFunction %12 %47 %42 6 1 %43 %13 FlagIsProtected|FlagIsPrivate 7 %81
+         %49 = OpExtInst %void %1 DebugLexicalBlock %42 7 38 %48
+         %50 = OpExtInst %void %1 DebugLocalVariable %14 %44 %42 8 13 %49 FlagIsLocal
+         %51 = OpExtInst %void %1 DebugLocalVariable %10 %41 %42 7 23 %48 FlagIsLocal 2
+         %53 = OpExtInst %void %1 DebugLocalVariable %11 %41 %42 6 23 %48 FlagIsLocal 1
+;CHECK: [[dbg_color:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable {{%\w+}} {{%\w+}} {{%\w+}} 7 23 {{%\w+}} FlagIsLocal 2
+;CHECK: [[dbg_pos:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable {{%\w+}} {{%\w+}} {{%\w+}} 6 23 {{%\w+}} FlagIsLocal 1
+
+         %84 = OpExtInst %void %1 DebugExpression %85
+       %main = OpFunction %void None %36
+         %54 = OpLabel
+         %91 = OpExtInst %void %1 DebugScope %49
+               OpLine %7 7 23
+         %83 = OpVariable %_ptr_Function_v4float Function
+               OpLine %7 8 13
+         %87 = OpExtInst %void %1 DebugValue %50 %83 %84 %int_1
+               OpLine %7 7 23
+         %82 = OpVariable %_ptr_Function_v4float Function
+               OpLine %7 8 13
+         %86 = OpExtInst %void %1 DebugValue %50 %82 %84 %int_0
+               OpNoLine
+         %92 = OpExtInst %void %1 DebugNoScope
+%param_var_pos = OpVariable %_ptr_Function_v4float Function
+%param_var_color = OpVariable %_ptr_Function_v4float Function
+         %55 = OpLoad %v4float %in_var_POSITION
+               OpStore %param_var_pos %55
+         %56 = OpLoad %v4float %in_var_COLOR
+;CHECK:      DebugNoScope
+;CHECK-NOT:  OpLine
+;CHECK:      [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION
+;CHECK:      [[color:%\w+]] = OpLoad %v4float %in_var_COLOR
+
+               OpStore %param_var_color %56
+         %93 = OpExtInst %void %1 DebugScope %48
+               OpLine %7 6 23
+         %73 = OpExtInst %void %1 DebugDeclare %53 %param_var_pos %52
+               OpLine %7 7 23
+         %74 = OpExtInst %void %1 DebugDeclare %51 %param_var_color %52
+;CHECK:      OpLine [[file:%\w+]] 6 23
+;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_pos]] [[pos]] [[empty_expr:%\w+]]
+;CHECK:      OpLine [[file]] 7 23
+;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_color]] [[color]] [[empty_expr]]
+
+         %94 = OpExtInst %void %1 DebugScope %49
+               OpLine %7 9 3
+               OpStore %82 %55
+               OpLine %7 10 3
+               OpStore %83 %56
+               OpLine %7 11 10
+         %90 = OpCompositeConstruct %VS_OUTPUT %55 %56
+               OpNoLine
+         %95 = OpExtInst %void %1 DebugNoScope
+         %58 = OpCompositeExtract %v4float %90 0
+               OpStore %gl_Position %58
+         %59 = OpCompositeExtract %v4float %90 1
+               OpStore %out_var_COLOR %59
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other types
diff --git a/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
index 2ecd238..ff42193 100644
--- a/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
@@ -3891,6 +3891,44 @@
   SinglePassRunAndMatch<SSARewritePass>(text, true);
 }
 
+// Check support for pointer variables. When pointer variables are used, the
+// computation of reaching definitions may need to follow pointer chains.
+// See https://github.com/KhronosGroup/SPIRV-Tools/issues/3873 for details.
+TEST_F(LocalSSAElimTest, PointerVariables) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability VariablePointers
+               OpExtension "SPV_KHR_variable_pointers"
+               OpMemoryModel Logical Simple
+               OpEntryPoint Fragment %1 "main" %2 %3
+               OpExecutionMode %1 OriginUpperLeft
+      %float = OpTypeFloat 32
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_float = OpTypePointer Output %float
+%_ptr_Function__ptr_Input_float = OpTypePointer Function %_ptr_Input_float
+          %2 = OpVariable %_ptr_Input_float Input
+          %3 = OpVariable %_ptr_Output_float Output
+          %1 = OpFunction %void None %6
+         %10 = OpLabel
+         %11 = OpVariable %_ptr_Function__ptr_Input_float Function
+               OpStore %11 %2
+
+; CHECK-NOT: %12 = OpLoad %_ptr_Input_float %11
+         %12 = OpLoad %_ptr_Input_float %11
+
+; CHECK: %13 = OpLoad %float %2
+         %13 = OpLoad %float %12
+
+               OpStore %3 %13
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    No optimization in the presence of
diff --git a/third_party/SPIRV-Tools/test/tools/opt/flags.py b/third_party/SPIRV-Tools/test/tools/opt/flags.py
index f7dc64c..f8117d9 100644
--- a/third_party/SPIRV-Tools/test/tools/opt/flags.py
+++ b/third_party/SPIRV-Tools/test/tools/opt/flags.py
@@ -149,6 +149,7 @@
       'eliminate-dead-branches',
       'merge-return',
       'inline-entry-points-exhaustive',
+      'eliminate-dead-functions',
       'eliminate-dead-code-aggressive',
       'private-to-local',
       'eliminate-local-single-block',
diff --git a/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp b/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
index 6bfcca3..74d5c3a 100644
--- a/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
@@ -168,6 +168,17 @@
   std::string token;
   std::string vuids = std::string(vuid_set);
   size_t position;
+
+  // Catch case were someone accidentally left spaces by trimming string
+  // clang-format off
+  vuids.erase(std::find_if(vuids.rbegin(), vuids.rend(), [](unsigned char c) {
+    return (c != ' ');
+  }).base(), vuids.end());
+  vuids.erase(vuids.begin(), std::find_if(vuids.begin(), vuids.end(), [](unsigned char c) {
+    return (c != ' ');
+  }));
+  // clang-format on
+
   do {
     position = vuids.find(delimiter);
     if (position != std::string::npos) {
@@ -1263,7 +1274,8 @@
     ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
     Combine(
         Values("Layer", "ViewportIndex"), Values("Fragment"), Values("Output"),
-        Values("%u32"), Values(nullptr),
+        Values("%u32"),
+        Values("VUID-Layer-Layer-04275 VUID-ViewportIndex-ViewportIndex-04407"),
         Values(TestResult(SPV_ERROR_INVALID_DATA,
                           "Output storage class if execution model is Fragment",
                           "which is called with execution model Fragment"))));
@@ -1274,10 +1286,11 @@
     Combine(
         Values("Layer", "ViewportIndex"),
         Values("Vertex", "TessellationEvaluation", "Geometry"), Values("Input"),
-        Values("%u32"), Values(nullptr),
+        Values("%u32"),
+        Values("VUID-Layer-Layer-04274 VUID-ViewportIndex-ViewportIndex-04406"),
         Values(TestResult(SPV_ERROR_INVALID_DATA,
                           "Input storage class if execution model is Vertex, "
-                          "TessellationEvaluation, or Geometry",
+                          "TessellationEvaluation, Geometry, or MeshNV",
                           "which is called with execution model"))));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -3836,7 +3849,7 @@
         Values("PrimitiveShadingRateKHR"), Values("Vertex"), Values("Output"),
         Values("%f32"), Values("OpCapability FragmentShadingRateKHR\n"),
         Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"),
-        Values("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04485 "),
+        Values("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04486 "),
         Values(TestResult(
             SPV_ERROR_INVALID_DATA,
             "According to the Vulkan spec BuiltIn PrimitiveShadingRateKHR "