Selaa lähdekoodia

Updated spirv-tools.

Бранимир Караџић 6 vuotta sitten
vanhempi
sitoutus
2d6c2cebeb
35 muutettua tiedostoa jossa 775 lisäystä ja 269 poistoa
  1. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  2. 8 5
      3rdparty/spirv-tools/include/spirv-tools/libspirv.h
  3. 9 5
      3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp
  4. 3 0
      3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp
  5. 12 0
      3rdparty/spirv-tools/source/opt/basic_block.cpp
  6. 6 0
      3rdparty/spirv-tools/source/opt/basic_block.h
  7. 7 0
      3rdparty/spirv-tools/source/opt/optimizer.cpp
  8. 14 0
      3rdparty/spirv-tools/source/opt/pass_manager.cpp
  9. 28 1
      3rdparty/spirv-tools/source/opt/pass_manager.h
  10. 3 1
      3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp
  11. 36 2
      3rdparty/spirv-tools/source/reduce/reducer.cpp
  12. 8 1
      3rdparty/spirv-tools/source/reduce/reducer.h
  13. 14 16
      3rdparty/spirv-tools/source/reduce/reduction_pass.cpp
  14. 18 7
      3rdparty/spirv-tools/source/reduce/reduction_pass.h
  15. 9 14
      3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
  16. 13 6
      3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp
  17. 5 0
      3rdparty/spirv-tools/source/spirv_reducer_options.cpp
  18. 6 2
      3rdparty/spirv-tools/source/spirv_reducer_options.h
  19. 32 0
      3rdparty/spirv-tools/source/val/validate_cfg.cpp
  20. 40 5
      3rdparty/spirv-tools/source/val/validate_image.cpp
  21. 1 1
      3rdparty/spirv-tools/test/opt/block_merge_test.cpp
  22. 10 5
      3rdparty/spirv-tools/test/opt/inline_test.cpp
  23. 20 0
      3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp
  24. 24 0
      3rdparty/spirv-tools/test/reduce/reduce_test_util.cpp
  25. 4 0
      3rdparty/spirv-tools/test/reduce/reduce_test_util.h
  26. 2 0
      3rdparty/spirv-tools/test/reduce/reducer_test.cpp
  27. 11 142
      3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp
  28. 185 0
      3rdparty/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp
  29. 12 4
      3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp
  30. 137 2
      3rdparty/spirv-tools/test/val/val_cfg_test.cpp
  31. 8 6
      3rdparty/spirv-tools/test/val/val_id_test.cpp
  32. 70 7
      3rdparty/spirv-tools/test/val/val_image_test.cpp
  33. 8 6
      3rdparty/spirv-tools/test/val/val_validation_state_test.cpp
  34. 4 0
      3rdparty/spirv-tools/tools/opt/opt.cpp
  35. 7 30
      3rdparty/spirv-tools/tools/reduce/reduce.cpp

+ 1 - 1
3rdparty/spirv-tools/include/generated/build-version.inc

@@ -1 +1 @@
-"v2019.2", "SPIRV-Tools v2019.2 027e592038bb74c2ffe6b07f9cc4e2ab1b3fbb84"
+"v2019.2", "SPIRV-Tools v2019.2 2ddefa50b7e00747527827ee9aeddb9741bd6c2e"

+ 8 - 5
3rdparty/spirv-tools/include/spirv-tools/libspirv.h

@@ -557,14 +557,17 @@ SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate();
 // Destroys the given reducer options object.
 // Destroys the given reducer options object.
 SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options);
 SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options);
 
 
-// Records the maximum number of reduction steps that should run before the
-// reducer gives up.
+// Sets the maximum number of reduction steps that should run before the reducer
+// gives up.
 SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit(
 SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit(
     spv_reducer_options options, uint32_t step_limit);
     spv_reducer_options options, uint32_t step_limit);
 
 
-// Sets seed for random number generation.
-SPIRV_TOOLS_EXPORT void spvReducerOptionsSetSeed(spv_reducer_options options,
-                                                 uint32_t seed);
+// Sets the fail-on-validation-error option; if true, the reducer will return
+// kStateInvalid if a reduction step yields a state that fails SPIR-V
+// validation. Otherwise, an invalid state is treated as uninteresting and the
+// reduction backtracks and continues.
+SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError(
+    spv_reducer_options options, bool fail_on_validation_error);
 
 
 // Encodes the given SPIR-V assembly text to its binary representation. The
 // Encodes the given SPIR-V assembly text to its binary representation. The
 // length parameter specifies the number of bytes for text. Encoded binary will
 // length parameter specifies the number of bytes for text. Encoded binary will

+ 9 - 5
3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp

@@ -151,16 +151,20 @@ class ReducerOptions {
   ~ReducerOptions() { spvReducerOptionsDestroy(options_); }
   ~ReducerOptions() { spvReducerOptionsDestroy(options_); }
 
 
   // Allow implicit conversion to the underlying object.
   // Allow implicit conversion to the underlying object.
-  operator spv_reducer_options() const { return options_; }
+  operator spv_reducer_options() const {  // NOLINT(google-explicit-constructor)
+    return options_;
+  }
 
 
-  // Records the maximum number of reduction steps that should
-  // run before the reducer gives up.
+  // See spvReducerOptionsSetStepLimit.
   void set_step_limit(uint32_t step_limit) {
   void set_step_limit(uint32_t step_limit) {
     spvReducerOptionsSetStepLimit(options_, step_limit);
     spvReducerOptionsSetStepLimit(options_, step_limit);
   }
   }
 
 
-  // Sets a seed to be used for random number generation.
-  void set_seed(uint32_t seed) { spvReducerOptionsSetSeed(options_, seed); }
+  // See spvReducerOptionsSetFailOnValidationError.
+  void set_fail_on_validation_error(bool fail_on_validation_error) {
+    spvReducerOptionsSetFailOnValidationError(options_,
+                                              fail_on_validation_error);
+  }
 
 
  private:
  private:
   spv_reducer_options options_;
   spv_reducer_options options_;

+ 3 - 0
3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp

@@ -199,6 +199,9 @@ class Optimizer {
   // |out| output stream.
   // |out| output stream.
   Optimizer& SetTimeReport(std::ostream* out);
   Optimizer& SetTimeReport(std::ostream* out);
 
 
+  // Sets the option to validate the module after each pass.
+  Optimizer& SetValidateAfterAll(bool validate);
+
  private:
  private:
   struct Impl;                  // Opaque struct for holding internal data.
   struct Impl;                  // Opaque struct for holding internal data.
   std::unique_ptr<Impl> impl_;  // Unique pointer to internal data.
   std::unique_ptr<Impl> impl_;  // Unique pointer to internal data.

+ 12 - 0
3rdparty/spirv-tools/source/opt/basic_block.cpp

@@ -184,6 +184,12 @@ uint32_t BasicBlock::MergeBlockIdIfAny() const {
   return mbid;
   return mbid;
 }
 }
 
 
+uint32_t BasicBlock::MergeBlockId() const {
+  uint32_t mbid = MergeBlockIdIfAny();
+  assert(mbid && "Expected block to have a corresponding merge block");
+  return mbid;
+}
+
 uint32_t BasicBlock::ContinueBlockIdIfAny() const {
 uint32_t BasicBlock::ContinueBlockIdIfAny() const {
   auto merge_ii = cend();
   auto merge_ii = cend();
   --merge_ii;
   --merge_ii;
@@ -197,6 +203,12 @@ uint32_t BasicBlock::ContinueBlockIdIfAny() const {
   return cbid;
   return cbid;
 }
 }
 
 
+uint32_t BasicBlock::ContinueBlockId() const {
+  uint32_t cbid = ContinueBlockIdIfAny();
+  assert(cbid && "Expected block to have a corresponding continue target");
+  return cbid;
+}
+
 std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
 std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
   str << block.PrettyPrint();
   str << block.PrettyPrint();
   return str;
   return str;

+ 6 - 0
3rdparty/spirv-tools/source/opt/basic_block.h

@@ -183,10 +183,16 @@ class BasicBlock {
   // block, if any.  If none, returns zero.
   // block, if any.  If none, returns zero.
   uint32_t MergeBlockIdIfAny() const;
   uint32_t MergeBlockIdIfAny() const;
 
 
+  // Returns MergeBlockIdIfAny() and asserts that it is non-zero.
+  uint32_t MergeBlockId() const;
+
   // Returns the ID of the continue block declared by a merge instruction in
   // Returns the ID of the continue block declared by a merge instruction in
   // this block, if any.  If none, returns zero.
   // this block, if any.  If none, returns zero.
   uint32_t ContinueBlockIdIfAny() const;
   uint32_t ContinueBlockIdIfAny() const;
 
 
+  // Returns ContinueBlockIdIfAny() and asserts that it is non-zero.
+  uint32_t ContinueBlockId() const;
+
   // Returns the terminator instruction.  Assumes the terminator exists.
   // Returns the terminator instruction.  Assumes the terminator exists.
   Instruction* terminator() { return &*tail(); }
   Instruction* terminator() { return &*tail(); }
   const Instruction* terminator() const { return &*ctail(); }
   const Instruction* terminator() const { return &*ctail(); }

+ 7 - 0
3rdparty/spirv-tools/source/opt/optimizer.cpp

@@ -507,6 +507,8 @@ bool Optimizer::Run(const uint32_t* original_binary,
 
 
   context->set_max_id_bound(opt_options->max_id_bound_);
   context->set_max_id_bound(opt_options->max_id_bound_);
 
 
+  impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
+  impl_->pass_manager.SetTargetEnv(impl_->target_env);
   auto status = impl_->pass_manager.Run(context.get());
   auto status = impl_->pass_manager.Run(context.get());
   if (status == opt::Pass::Status::SuccessWithChange ||
   if (status == opt::Pass::Status::SuccessWithChange ||
       (status == opt::Pass::Status::SuccessWithoutChange &&
       (status == opt::Pass::Status::SuccessWithoutChange &&
@@ -529,6 +531,11 @@ Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
   return *this;
   return *this;
 }
 }
 
 
+Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
+  impl_->pass_manager.SetValidateAfterAll(validate);
+  return *this;
+}
+
 Optimizer::PassToken CreateNullPass() {
 Optimizer::PassToken CreateNullPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
 }
 }

+ 14 - 0
3rdparty/spirv-tools/source/opt/pass_manager.cpp

@@ -51,6 +51,20 @@ Pass::Status PassManager::Run(IRContext* context) {
     if (one_status == Pass::Status::Failure) return one_status;
     if (one_status == Pass::Status::Failure) return one_status;
     if (one_status == Pass::Status::SuccessWithChange) status = one_status;
     if (one_status == Pass::Status::SuccessWithChange) status = one_status;
 
 
+    if (validate_after_all_) {
+      spvtools::SpirvTools tools(target_env_);
+      tools.SetMessageConsumer(consumer());
+      std::vector<uint32_t> binary;
+      context->module()->ToBinary(&binary, true);
+      if (!tools.Validate(binary.data(), binary.size(), val_options_)) {
+        std::string msg = "Validation failed after pass ";
+        msg += pass->name();
+        spv_position_t null_pos{0, 0, 0};
+        consumer()(SPV_MSG_INTERNAL_ERROR, "", null_pos, msg.c_str());
+        return Pass::Status::Failure;
+      }
+    }
+
     // Reset the pass to free any memory used by the pass.
     // Reset the pass to free any memory used by the pass.
     pass.reset(nullptr);
     pass.reset(nullptr);
   }
   }

+ 28 - 1
3rdparty/spirv-tools/source/opt/pass_manager.h

@@ -43,7 +43,10 @@ class PassManager {
   PassManager()
   PassManager()
       : consumer_(nullptr),
       : consumer_(nullptr),
         print_all_stream_(nullptr),
         print_all_stream_(nullptr),
-        time_report_stream_(nullptr) {}
+        time_report_stream_(nullptr),
+        target_env_(SPV_ENV_UNIVERSAL_1_2),
+        val_options_(nullptr),
+        validate_after_all_(false) {}
 
 
   // Sets the message consumer to the given |consumer|.
   // Sets the message consumer to the given |consumer|.
   void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
   void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
@@ -89,6 +92,24 @@ class PassManager {
     return *this;
     return *this;
   }
   }
 
 
+  // Sets the target environment for validation.
+  PassManager& SetTargetEnv(spv_target_env env) {
+    target_env_ = env;
+    return *this;
+  }
+
+  // Sets the validation options.
+  PassManager& SetValidatorOptions(spv_validator_options options) {
+    val_options_ = options;
+    return *this;
+  }
+
+  // Sets the option to validate after each pass.
+  PassManager& SetValidateAfterAll(bool validate) {
+    validate_after_all_ = validate;
+    return *this;
+  }
+
  private:
  private:
   // Consumer for messages.
   // Consumer for messages.
   MessageConsumer consumer_;
   MessageConsumer consumer_;
@@ -100,6 +121,12 @@ class PassManager {
   // The output stream to write the resource utilization of each pass. If this
   // The output stream to write the resource utilization of each pass. If this
   // is null, no output is generated.
   // is null, no output is generated.
   std::ostream* time_report_stream_;
   std::ostream* time_report_stream_;
+  // The target environment.
+  spv_target_env target_env_;
+  // The validator options (used when validating each pass).
+  spv_validator_options val_options_;
+  // Controls whether validation occurs after every pass.
+  bool validate_after_all_;
 };
 };
 
 
 inline void PassManager::AddPass(std::unique_ptr<Pass> pass) {
 inline void PassManager::AddPass(std::unique_ptr<Pass> pass) {

+ 3 - 1
3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp

@@ -19,7 +19,7 @@
 namespace {
 namespace {
 const uint32_t kMergeNodeIndex = 0;
 const uint32_t kMergeNodeIndex = 0;
 const uint32_t kContinueNodeIndex = 1;
 const uint32_t kContinueNodeIndex = 1;
-}
+}  // namespace
 
 
 namespace spvtools {
 namespace spvtools {
 namespace opt {
 namespace opt {
@@ -37,6 +37,8 @@ StructuredCFGAnalysis::StructuredCFGAnalysis(IRContext* ctx) : context_(ctx) {
 }
 }
 
 
 void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
 void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
+  if (func->begin() == func->end()) return;
+
   std::list<BasicBlock*> order;
   std::list<BasicBlock*> order;
   context_->cfg()->ComputeStructuredOrder(func, &*func->begin(), &order);
   context_->cfg()->ComputeStructuredOrder(func, &*func->begin(), &order);
 
 

+ 36 - 2
3rdparty/spirv-tools/source/reduce/reducer.cpp

@@ -15,10 +15,17 @@
 #include <cassert>
 #include <cassert>
 #include <sstream>
 #include <sstream>
 
 
+#include "source/reduce/merge_blocks_reduction_opportunity_finder.h"
+#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
+#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h"
+#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
+#include "source/reduce/remove_function_reduction_opportunity_finder.h"
+#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
 #include "source/spirv_reducer_options.h"
 #include "source/spirv_reducer_options.h"
 
 
 #include "reducer.h"
 #include "reducer.h"
-#include "reduction_pass.h"
 
 
 namespace spvtools {
 namespace spvtools {
 namespace reduce {
 namespace reduce {
@@ -105,13 +112,15 @@ Reducer::ReductionResultStatus Reducer::Run(
       do {
       do {
         auto maybe_result = pass->TryApplyReduction(current_binary);
         auto maybe_result = pass->TryApplyReduction(current_binary);
         if (maybe_result.empty()) {
         if (maybe_result.empty()) {
-          // This pass did not have any impact, so move on to the next pass.
+          // For this round, the pass has no more opportunities (chunks) to
+          // apply, so move on to the next pass.
           impl_->consumer(
           impl_->consumer(
               SPV_MSG_INFO, nullptr, {},
               SPV_MSG_INFO, nullptr, {},
               ("Pass " + pass->GetName() + " did not make a reduction step.")
               ("Pass " + pass->GetName() + " did not make a reduction step.")
                   .c_str());
                   .c_str());
           break;
           break;
         }
         }
+        bool interesting = false;
         std::stringstream stringstream;
         std::stringstream stringstream;
         reductions_applied++;
         reductions_applied++;
         stringstream << "Pass " << pass->GetName() << " made reduction step "
         stringstream << "Pass " << pass->GetName() << " made reduction step "
@@ -125,6 +134,9 @@ Reducer::ReductionResultStatus Reducer::Run(
           // invalid binary from being regarded as interesting.
           // invalid binary from being regarded as interesting.
           impl_->consumer(SPV_MSG_INFO, nullptr, {},
           impl_->consumer(SPV_MSG_INFO, nullptr, {},
                           "Reduction step produced an invalid binary.");
                           "Reduction step produced an invalid binary.");
+          if (options->fail_on_validation_error) {
+            return Reducer::ReductionResultStatus::kStateInvalid;
+          }
         } else if (impl_->interestingness_function(maybe_result,
         } else if (impl_->interestingness_function(maybe_result,
                                                    reductions_applied)) {
                                                    reductions_applied)) {
           // Success!  The binary produced by this reduction step is
           // Success!  The binary produced by this reduction step is
@@ -133,8 +145,11 @@ Reducer::ReductionResultStatus Reducer::Run(
           impl_->consumer(SPV_MSG_INFO, nullptr, {},
           impl_->consumer(SPV_MSG_INFO, nullptr, {},
                           "Reduction step succeeded.");
                           "Reduction step succeeded.");
           current_binary = std::move(maybe_result);
           current_binary = std::move(maybe_result);
+          interesting = true;
           another_round_worthwhile = true;
           another_round_worthwhile = true;
         }
         }
+        // We must call this before the next call to TryApplyReduction.
+        pass->NotifyInteresting(interesting);
         // Bail out if the reduction step limit has been reached.
         // Bail out if the reduction step limit has been reached.
       } while (!impl_->ReachedStepLimit(reductions_applied, options));
       } while (!impl_->ReachedStepLimit(reductions_applied, options));
     }
     }
@@ -153,6 +168,25 @@ Reducer::ReductionResultStatus Reducer::Run(
   return Reducer::ReductionResultStatus::kComplete;
   return Reducer::ReductionResultStatus::kComplete;
 }
 }
 
 
+void Reducer::AddDefaultReductionPasses() {
+  AddReductionPass(spvtools::MakeUnique<
+                   RemoveOpNameInstructionReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
+  AddReductionPass(spvtools::MakeUnique<
+                   RemoveUnreferencedInstructionReductionOpportunityFinder>());
+  AddReductionPass(spvtools::MakeUnique<
+                   StructuredLoopToSelectionReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<RemoveFunctionReductionOpportunityFinder>());
+}
+
 void Reducer::AddReductionPass(
 void Reducer::AddReductionPass(
     std::unique_ptr<ReductionOpportunityFinder>&& finder) {
     std::unique_ptr<ReductionOpportunityFinder>&& finder) {
   impl_->passes.push_back(spvtools::MakeUnique<ReductionPass>(
   impl_->passes.push_back(spvtools::MakeUnique<ReductionPass>(

+ 8 - 1
3rdparty/spirv-tools/source/reduce/reducer.h

@@ -34,7 +34,11 @@ class Reducer {
     kInitialStateNotInteresting,
     kInitialStateNotInteresting,
     kReachedStepLimit,
     kReachedStepLimit,
     kComplete,
     kComplete,
-    kInitialStateInvalid
+    kInitialStateInvalid,
+
+    // Returned when the fail-on-validation-error option is set and a
+    // reduction step yields a state that fails validation.
+    kStateInvalid,
   };
   };
 
 
   // The type for a function that will take a binary and return true if and
   // The type for a function that will take a binary and return true if and
@@ -76,6 +80,9 @@ class Reducer {
   void SetInterestingnessFunction(
   void SetInterestingnessFunction(
       InterestingnessFunction interestingness_function);
       InterestingnessFunction interestingness_function);
 
 
+  // Adds all default reduction passes.
+  void AddDefaultReductionPasses();
+
   // Adds a reduction pass based on the given finder to the sequence of passes
   // Adds a reduction pass based on the given finder to the sequence of passes
   // that will be iterated over.
   // that will be iterated over.
   void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder);
   void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder);

+ 14 - 16
3rdparty/spirv-tools/source/reduce/reduction_pass.cpp

@@ -36,20 +36,19 @@ std::vector<uint32_t> ReductionPass::TryApplyReduction(
   std::vector<std::unique_ptr<ReductionOpportunity>> opportunities =
   std::vector<std::unique_ptr<ReductionOpportunity>> opportunities =
       finder_->GetAvailableOpportunities(context.get());
       finder_->GetAvailableOpportunities(context.get());
 
 
-  if (!is_initialized_) {
-    is_initialized_ = true;
-    index_ = 0;
-    granularity_ = (uint32_t)opportunities.size();
-  }
-
-  if (opportunities.empty()) {
-    granularity_ = 1;
-    return std::vector<uint32_t>();
+  // There is no point in having a granularity larger than the number of
+  // opportunities, so reduce the granularity in this case.
+  if (granularity_ > opportunities.size()) {
+    granularity_ = std::max((uint32_t)1, (uint32_t)opportunities.size());
   }
   }
 
 
   assert(granularity_ > 0);
   assert(granularity_ > 0);
 
 
   if (index_ >= opportunities.size()) {
   if (index_ >= opportunities.size()) {
+    // We have reached the end of the available opportunities and, therefore,
+    // the end of the round for this pass, so reset the index and decrease the
+    // granularity for the next round. Return an empty vector to signal the end
+    // of the round.
     index_ = 0;
     index_ = 0;
     granularity_ = std::max((uint32_t)1, granularity_ / 2);
     granularity_ = std::max((uint32_t)1, granularity_ / 2);
     return std::vector<uint32_t>();
     return std::vector<uint32_t>();
@@ -61,8 +60,6 @@ std::vector<uint32_t> ReductionPass::TryApplyReduction(
     opportunities[i]->TryToApply();
     opportunities[i]->TryToApply();
   }
   }
 
 
-  index_ += granularity_;
-
   std::vector<uint32_t> result;
   std::vector<uint32_t> result;
   context->module()->ToBinary(&result, false);
   context->module()->ToBinary(&result, false);
   return result;
   return result;
@@ -73,16 +70,17 @@ void ReductionPass::SetMessageConsumer(MessageConsumer consumer) {
 }
 }
 
 
 bool ReductionPass::ReachedMinimumGranularity() const {
 bool ReductionPass::ReachedMinimumGranularity() const {
-  if (!is_initialized_) {
-    // Conceptually we can think that if the pass has not yet been initialized,
-    // it is operating at unbounded granularity.
-    return false;
-  }
   assert(granularity_ != 0);
   assert(granularity_ != 0);
   return granularity_ == 1;
   return granularity_ == 1;
 }
 }
 
 
 std::string ReductionPass::GetName() const { return finder_->GetName(); }
 std::string ReductionPass::GetName() const { return finder_->GetName(); }
 
 
+void ReductionPass::NotifyInteresting(bool interesting) {
+  if (!interesting) {
+    index_ += granularity_;
+  }
+}
+
 }  // namespace reduce
 }  // namespace reduce
 }  // namespace spvtools
 }  // namespace spvtools

+ 18 - 7
3rdparty/spirv-tools/source/reduce/reduction_pass.h

@@ -15,10 +15,11 @@
 #ifndef SOURCE_REDUCE_REDUCTION_PASS_H_
 #ifndef SOURCE_REDUCE_REDUCTION_PASS_H_
 #define SOURCE_REDUCE_REDUCTION_PASS_H_
 #define SOURCE_REDUCE_REDUCTION_PASS_H_
 
 
-#include "spirv-tools/libspirv.hpp"
+#include <limits>
 
 
-#include "reduction_opportunity_finder.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/ir_context.h"
+#include "source/reduce/reduction_opportunity_finder.h"
+#include "spirv-tools/libspirv.hpp"
 
 
 namespace spvtools {
 namespace spvtools {
 namespace reduce {
 namespace reduce {
@@ -33,17 +34,28 @@ namespace reduce {
 class ReductionPass {
 class ReductionPass {
  public:
  public:
   // Constructs a reduction pass with a given target environment, |target_env|,
   // Constructs a reduction pass with a given target environment, |target_env|,
-  // and a given finder of reduction opportunities, |finder|.  Initially the
-  // pass is uninitialized.
+  // and a given finder of reduction opportunities, |finder|.
   explicit ReductionPass(const spv_target_env target_env,
   explicit ReductionPass(const spv_target_env target_env,
                          std::unique_ptr<ReductionOpportunityFinder> finder)
                          std::unique_ptr<ReductionOpportunityFinder> finder)
       : target_env_(target_env),
       : target_env_(target_env),
         finder_(std::move(finder)),
         finder_(std::move(finder)),
-        is_initialized_(false) {}
+        index_(0),
+        granularity_(std::numeric_limits<uint32_t>::max()) {}
 
 
-  // Applies the reduction pass to the given binary.
+  // Applies the reduction pass to the given binary by applying a "chunk" of
+  // reduction opportunities. Returns the new binary if a chunk was applied; in
+  // this case, before the next call the caller must invoke
+  // NotifyInteresting(...) to indicate whether the new binary is interesting.
+  // Returns an empty vector if there are no more chunks left to apply; in this
+  // case, the index will be reset and the granularity lowered for the next
+  // round.
   std::vector<uint32_t> TryApplyReduction(const std::vector<uint32_t>& binary);
   std::vector<uint32_t> TryApplyReduction(const std::vector<uint32_t>& binary);
 
 
+  // Notifies the reduction pass whether the binary returned from
+  // TryApplyReduction is interesting, so that the next call to
+  // TryApplyReduction will avoid applying the same chunk of opportunities.
+  void NotifyInteresting(bool interesting);
+
   // Sets a consumer to which relevant messages will be directed.
   // Sets a consumer to which relevant messages will be directed.
   void SetMessageConsumer(MessageConsumer consumer);
   void SetMessageConsumer(MessageConsumer consumer);
 
 
@@ -59,7 +71,6 @@ class ReductionPass {
   const spv_target_env target_env_;
   const spv_target_env target_env_;
   const std::unique_ptr<ReductionOpportunityFinder> finder_;
   const std::unique_ptr<ReductionOpportunityFinder> finder_;
   MessageConsumer consumer_;
   MessageConsumer consumer_;
-  bool is_initialized_;
   uint32_t index_;
   uint32_t index_;
   uint32_t granularity_;
   uint32_t granularity_;
 };
 };

+ 9 - 14
3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp

@@ -23,7 +23,6 @@ namespace reduce {
 
 
 namespace {
 namespace {
 const uint32_t kMergeNodeIndex = 0;
 const uint32_t kMergeNodeIndex = 0;
-const uint32_t kContinueNodeIndex = 1;
 }  // namespace
 }  // namespace
 
 
 bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() {
 bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() {
@@ -43,15 +42,11 @@ void StructuredLoopToSelectionReductionOpportunity::Apply() {
 
 
   // (1) Redirect edges that point to the loop's continue target to their
   // (1) Redirect edges that point to the loop's continue target to their
   // closest merge block.
   // closest merge block.
-  RedirectToClosestMergeBlock(
-      loop_construct_header_->GetLoopMergeInst()->GetSingleWordOperand(
-          kContinueNodeIndex));
+  RedirectToClosestMergeBlock(loop_construct_header_->ContinueBlockId());
 
 
   // (2) Redirect edges that point to the loop's merge block to their closest
   // (2) Redirect edges that point to the loop's merge block to their closest
   // merge block (which might be that of an enclosing selection, for instance).
   // merge block (which might be that of an enclosing selection, for instance).
-  RedirectToClosestMergeBlock(
-      loop_construct_header_->GetLoopMergeInst()->GetSingleWordOperand(
-          kMergeNodeIndex));
+  RedirectToClosestMergeBlock(loop_construct_header_->MergeBlockId());
 
 
   // (3) Turn the loop construct header into a selection.
   // (3) Turn the loop construct header into a selection.
   ChangeLoopToSelection();
   ChangeLoopToSelection();
@@ -127,12 +122,8 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectEdge(
 
 
   // original_target_id must either be the merge target or continue construct
   // original_target_id must either be the merge target or continue construct
   // for the loop being operated on.
   // for the loop being operated on.
-  assert(original_target_id ==
-             loop_construct_header_->GetMergeInst()->GetSingleWordOperand(
-                 kMergeNodeIndex) ||
-         original_target_id ==
-             loop_construct_header_->GetMergeInst()->GetSingleWordOperand(
-                 kContinueNodeIndex));
+  assert(original_target_id == loop_construct_header_->MergeBlockId() ||
+         original_target_id == loop_construct_header_->ContinueBlockId());
 
 
   auto terminator = context_->cfg()->block(source_id)->terminator();
   auto terminator = context_->cfg()->block(source_id)->terminator();
 
 
@@ -221,7 +212,7 @@ void StructuredLoopToSelectionReductionOpportunity::ChangeLoopToSelection() {
     const analysis::Bool* bool_type =
     const analysis::Bool* bool_type =
         context_->get_type_mgr()->GetRegisteredType(&temp)->AsBool();
         context_->get_type_mgr()->GetRegisteredType(&temp)->AsBool();
     auto const_mgr = context_->get_constant_mgr();
     auto const_mgr = context_->get_constant_mgr();
-    auto true_const = const_mgr->GetConstant(bool_type, {true});
+    auto true_const = const_mgr->GetConstant(bool_type, {1});
     auto true_const_result_id =
     auto true_const_result_id =
         const_mgr->GetDefiningInstruction(true_const)->result_id();
         const_mgr->GetDefiningInstruction(true_const)->result_id();
     auto original_branch_id = terminator->GetSingleWordOperand(0);
     auto original_branch_id = terminator->GetSingleWordOperand(0);
@@ -250,6 +241,10 @@ void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() {
       context_->get_def_use_mgr()->ForEachUse(&def, [this, &block, &def](
       context_->get_def_use_mgr()->ForEachUse(&def, [this, &block, &def](
                                                         Instruction* use,
                                                         Instruction* use,
                                                         uint32_t index) {
                                                         uint32_t index) {
+        // Ignore uses outside of blocks, such as in OpDecorate.
+        if (context_->get_instr_block(use) == nullptr) {
+          return;
+        }
         // If a use is not appropriately dominated by its definition,
         // If a use is not appropriately dominated by its definition,
         // replace the use with an OpUndef, unless the definition is an
         // replace the use with an OpUndef, unless the definition is an
         // access chain, in which case replace it with some (possibly fresh)
         // access chain, in which case replace it with some (possibly fresh)

+ 13 - 6
3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp

@@ -33,10 +33,9 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
   std::set<uint32_t> merge_block_ids;
   std::set<uint32_t> merge_block_ids;
   for (auto& function : *context->module()) {
   for (auto& function : *context->module()) {
     for (auto& block : function) {
     for (auto& block : function) {
-      auto merge_inst = block.GetMergeInst();
-      if (merge_inst) {
-        merge_block_ids.insert(
-            merge_inst->GetSingleWordOperand(kMergeNodeIndex));
+      auto merge_block_id = block.MergeBlockIdIfAny();
+      if (merge_block_id) {
+        merge_block_ids.insert(merge_block_id);
       }
       }
     }
     }
   }
   }
@@ -50,11 +49,19 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
         continue;
         continue;
       }
       }
 
 
+      uint32_t continue_block_id =
+          loop_merge_inst->GetSingleWordOperand(kContinueNodeIndex);
+
       // Check whether the loop construct's continue target is the merge block
       // Check whether the loop construct's continue target is the merge block
       // of some structured control flow construct.  If it is, we cautiously do
       // of some structured control flow construct.  If it is, we cautiously do
       // not consider applying a transformation.
       // not consider applying a transformation.
-      if (merge_block_ids.find(loop_merge_inst->GetSingleWordOperand(
-              kContinueNodeIndex)) != merge_block_ids.end()) {
+      if (merge_block_ids.find(continue_block_id) != merge_block_ids.end()) {
+        continue;
+      }
+
+      // Check whether the loop header block is also the continue target. If it
+      // is, we cautiously do not consider applying a transformation.
+      if (block.id() == continue_block_id) {
         continue;
         continue;
       }
       }
 
 

+ 5 - 0
3rdparty/spirv-tools/source/spirv_reducer_options.cpp

@@ -29,3 +29,8 @@ SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit(
     spv_reducer_options options, uint32_t step_limit) {
     spv_reducer_options options, uint32_t step_limit) {
   options->step_limit = step_limit;
   options->step_limit = step_limit;
 }
 }
+
+SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError(
+    spv_reducer_options options, bool fail_on_validation_error) {
+  options->fail_on_validation_error = fail_on_validation_error;
+}

+ 6 - 2
3rdparty/spirv-tools/source/spirv_reducer_options.h

@@ -26,10 +26,14 @@ const uint32_t kDefaultStepLimit = 250;
 // Manages command line options passed to the SPIR-V Reducer. New struct
 // Manages command line options passed to the SPIR-V Reducer. New struct
 // members may be added for any new option.
 // members may be added for any new option.
 struct spv_reducer_options_t {
 struct spv_reducer_options_t {
-  spv_reducer_options_t() : step_limit(kDefaultStepLimit) {}
+  spv_reducer_options_t()
+      : step_limit(kDefaultStepLimit), fail_on_validation_error(false) {}
 
 
-  // The number of steps the reducer will run for before giving up.
+  // See spvReducerOptionsSetStepLimit.
   uint32_t step_limit;
   uint32_t step_limit;
+
+  // See spvReducerOptionsSetFailOnValidationError.
+  bool fail_on_validation_error;
 };
 };
 
 
 #endif  // SOURCE_SPIRV_REDUCER_OPTIONS_H_
 #endif  // SOURCE_SPIRV_REDUCER_OPTIONS_H_

+ 32 - 0
3rdparty/spirv-tools/source/val/validate_cfg.cpp

@@ -725,6 +725,36 @@ spv_result_t PerformWebGPUCfgChecks(ValidationState_t& _, Function* function) {
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 
+// Checks that there are no OpUnreachable instructions reachable from the entry
+// block of |function|.
+spv_result_t ValidateUnreachableBlocks(ValidationState_t& _,
+                                       Function& function) {
+  auto* entry_block = function.first_block();
+  std::vector<BasicBlock*> stack;
+  std::unordered_set<BasicBlock*> seen;
+  if (entry_block) stack.push_back(entry_block);
+
+  while (!stack.empty()) {
+    auto* block = stack.back();
+    stack.pop_back();
+
+    if (!seen.insert(block).second) continue;
+
+    auto* terminator = block->terminator();
+    if (terminator->opcode() == SpvOpUnreachable) {
+      return _.diag(SPV_ERROR_INVALID_CFG, block->label())
+             << "Statically reachable blocks cannot be terminated by "
+                "OpUnreachable";
+    }
+
+    for (auto* succ : *block->successors()) {
+      stack.push_back(succ);
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t PerformCfgChecks(ValidationState_t& _) {
 spv_result_t PerformCfgChecks(ValidationState_t& _) {
   for (auto& function : _.functions()) {
   for (auto& function : _.functions()) {
     // Check all referenced blocks are defined within a function
     // Check all referenced blocks are defined within a function
@@ -827,6 +857,8 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
       }
       }
     }
     }
 
 
+    if (auto error = ValidateUnreachableBlocks(_, function)) return error;
+
     /// Structured control flow checks are only required for shader capabilities
     /// Structured control flow checks are only required for shader capabilities
     if (_.HasCapability(SpvCapabilityShader)) {
     if (_.HasCapability(SpvCapabilityShader)) {
       if (auto error = StructuredControlFlowChecks(_, &function, back_edges))
       if (auto error = StructuredControlFlowChecks(_, &function, back_edges))

+ 40 - 5
3rdparty/spirv-tools/source/val/validate_image.cpp

@@ -748,6 +748,33 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 
+bool IsAllowedSampledImageOperand(SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpSampledImage:
+    case SpvOpImageSampleImplicitLod:
+    case SpvOpImageSampleExplicitLod:
+    case SpvOpImageSampleDrefImplicitLod:
+    case SpvOpImageSampleDrefExplicitLod:
+    case SpvOpImageSampleProjImplicitLod:
+    case SpvOpImageSampleProjExplicitLod:
+    case SpvOpImageSampleProjDrefImplicitLod:
+    case SpvOpImageSampleProjDrefExplicitLod:
+    case SpvOpImageGather:
+    case SpvOpImageDrefGather:
+    case SpvOpImage:
+    case SpvOpImageQueryLod:
+    case SpvOpImageSparseSampleImplicitLod:
+    case SpvOpImageSparseSampleExplicitLod:
+    case SpvOpImageSparseSampleDrefImplicitLod:
+    case SpvOpImageSparseSampleDrefExplicitLod:
+    case SpvOpImageSparseGather:
+    case SpvOpImageSparseDrefGather:
+      return true;
+    default:
+      return false;
+  }
+}
+
 spv_result_t ValidateSampledImage(ValidationState_t& _,
 spv_result_t ValidateSampledImage(ValidationState_t& _,
                                   const Instruction* inst) {
                                   const Instruction* inst) {
   if (_.GetIdOpcode(inst->type_id()) != SpvOpTypeSampledImage) {
   if (_.GetIdOpcode(inst->type_id()) != SpvOpTypeSampledImage) {
@@ -815,11 +842,7 @@ spv_result_t ValidateSampledImage(ValidationState_t& _,
                   "block. The consumer instruction <id> is '"
                   "block. The consumer instruction <id> is '"
                << _.getIdName(consumer_instr->id()) << "'.";
                << _.getIdName(consumer_instr->id()) << "'.";
       }
       }
-      // TODO: The following check is incomplete. We should also check that the
-      // Sampled Image is not used by instructions that should not take
-      // SampledImage as an argument. We could find the list of valid
-      // instructions by scanning for "Sampled Image" in the operand description
-      // field in the grammar file.
+
       if (consumer_opcode == SpvOpPhi || consumer_opcode == SpvOpSelect) {
       if (consumer_opcode == SpvOpPhi || consumer_opcode == SpvOpSelect) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "Result <id> from OpSampledImage instruction must not appear "
                << "Result <id> from OpSampledImage instruction must not appear "
@@ -830,6 +853,18 @@ spv_result_t ValidateSampledImage(ValidationState_t& _,
                << "' as an operand of <id> '"
                << "' as an operand of <id> '"
                << _.getIdName(consumer_instr->id()) << "'.";
                << _.getIdName(consumer_instr->id()) << "'.";
       }
       }
+
+      if (!IsAllowedSampledImageOperand(consumer_opcode)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Result <id> from OpSampledImage instruction must not appear "
+                  "as operand for Op"
+               << spvOpcodeString(static_cast<SpvOp>(consumer_opcode))
+               << ", since it is not specificed as taking an "
+               << "OpTypeSampledImage."
+               << " Found result <id> '" << _.getIdName(inst->id())
+               << "' as an operand of <id> '"
+               << _.getIdName(consumer_instr->id()) << "'.";
+      }
     }
     }
   }
   }
   return SPV_SUCCESS;
   return SPV_SUCCESS;

+ 1 - 1
3rdparty/spirv-tools/test/opt/block_merge_test.cpp

@@ -590,7 +590,7 @@ OpUnreachable
 OpFunctionEnd
 OpFunctionEnd
 )";
 )";
 
 
-  SinglePassRunAndMatch<BlockMergePass>(text, true);
+  SinglePassRunAndMatch<BlockMergePass>(text, false);
 }
 }
 
 
 TEST_F(BlockMergeTest, DontMergeReturn) {
 TEST_F(BlockMergeTest, DontMergeReturn) {

+ 10 - 5
3rdparty/spirv-tools/test/opt/inline_test.cpp

@@ -2421,7 +2421,8 @@ OpBranchConditional %true %44 %43
 OpStore %38 %float_1
 OpStore %38 %float_1
 OpBranch %40
 OpBranch %40
 %43 = OpLabel
 %43 = OpLabel
-OpUnreachable
+OpStore %38 %float_1
+OpBranch %40
 %41 = OpLabel
 %41 = OpLabel
 OpBranchConditional %false %39 %40
 OpBranchConditional %false %39 %40
 %40 = OpLabel
 %40 = OpLabel
@@ -2446,7 +2447,7 @@ OpBranchConditional %true %36 %35
 %36 = OpLabel
 %36 = OpLabel
 OpReturnValue %float_1
 OpReturnValue %float_1
 %35 = OpLabel
 %35 = OpLabel
-OpUnreachable
+OpReturnValue %float_1
 OpFunctionEnd
 OpFunctionEnd
 )";
 )";
 
 
@@ -3058,6 +3059,7 @@ OpDecorate %2 DescriptorSet 439418829
 %4 = OpTypeFunction %void
 %4 = OpTypeFunction %void
 %float = OpTypeFloat 32
 %float = OpTypeFloat 32
 %_struct_6 = OpTypeStruct %float %float
 %_struct_6 = OpTypeStruct %float %float
+%15 = OpConstantNull %_struct_6
 %7 = OpTypeFunction %_struct_6
 %7 = OpTypeFunction %_struct_6
 %1 = OpFunction %void Pure|Const %4
 %1 = OpFunction %void Pure|Const %4
 %8 = OpLabel
 %8 = OpLabel
@@ -3067,10 +3069,11 @@ OpFunctionEnd
 %9 = OpFunction %_struct_6 None %7
 %9 = OpFunction %_struct_6 None %7
 %10 = OpLabel
 %10 = OpLabel
 %11 = OpFunctionCall %_struct_6 %9
 %11 = OpFunctionCall %_struct_6 %9
-OpUnreachable
+OpReturnValue %15
 OpFunctionEnd
 OpFunctionEnd
 )";
 )";
 
 
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
   SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
 }
 }
 
 
@@ -3086,6 +3089,7 @@ OpDecorate %2 DescriptorSet 439418829
 %4 = OpTypeFunction %void
 %4 = OpTypeFunction %void
 %float = OpTypeFloat 32
 %float = OpTypeFloat 32
 %_struct_6 = OpTypeStruct %float %float
 %_struct_6 = OpTypeStruct %float %float
+%15 = OpConstantNull %_struct_6
 %7 = OpTypeFunction %_struct_6
 %7 = OpTypeFunction %_struct_6
 %1 = OpFunction %void Pure|Const %4
 %1 = OpFunction %void Pure|Const %4
 %8 = OpLabel
 %8 = OpLabel
@@ -3095,15 +3099,16 @@ OpFunctionEnd
 %9 = OpFunction %_struct_6 None %7
 %9 = OpFunction %_struct_6 None %7
 %10 = OpLabel
 %10 = OpLabel
 %11 = OpFunctionCall %_struct_6 %12
 %11 = OpFunctionCall %_struct_6 %12
-OpUnreachable
+OpReturnValue %15
 OpFunctionEnd
 OpFunctionEnd
 %12 = OpFunction %_struct_6 None %7
 %12 = OpFunction %_struct_6 None %7
 %13 = OpLabel
 %13 = OpLabel
 %14 = OpFunctionCall %_struct_6 %9
 %14 = OpFunctionCall %_struct_6 %9
-OpUnreachable
+OpReturnValue %15
 OpFunctionEnd
 OpFunctionEnd
 )";
 )";
 
 
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
   SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
 }
 }
 
 

+ 20 - 0
3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp

@@ -461,6 +461,26 @@ OpFunctionEnd
   }
   }
 }
 }
 
 
+TEST_F(StructCFGAnalysisTest, EmptyFunctionTest) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %func LinkageAttributes "x" Import
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+OpFunctionEnd
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  // #2451: This segfaulted on empty functions.
+  StructuredCFGAnalysis analysis(context.get());
+}
+
 }  // namespace
 }  // namespace
 }  // namespace opt
 }  // namespace opt
 }  // namespace spvtools
 }  // namespace spvtools

+ 24 - 0
3rdparty/spirv-tools/test/reduce/reduce_test_util.cpp

@@ -14,6 +14,8 @@
 
 
 #include "reduce_test_util.h"
 #include "reduce_test_util.h"
 
 
+#include <iostream>
+
 namespace spvtools {
 namespace spvtools {
 namespace reduce {
 namespace reduce {
 
 
@@ -68,5 +70,27 @@ void NopDiagnostic(spv_message_level_t /*level*/, const char* /*source*/,
                    const spv_position_t& /*position*/,
                    const spv_position_t& /*position*/,
                    const char* /*message*/) {}
                    const char* /*message*/) {}
 
 
+void CLIMessageConsumer(spv_message_level_t level, const char*,
+                        const spv_position_t& position, const char* message) {
+  switch (level) {
+    case SPV_MSG_FATAL:
+    case SPV_MSG_INTERNAL_ERROR:
+    case SPV_MSG_ERROR:
+      std::cerr << "error: line " << position.index << ": " << message
+                << std::endl;
+      break;
+    case SPV_MSG_WARNING:
+      std::cout << "warning: line " << position.index << ": " << message
+                << std::endl;
+      break;
+    case SPV_MSG_INFO:
+      std::cout << "info: line " << position.index << ": " << message
+                << std::endl;
+      break;
+    default:
+      break;
+  }
+}
+
 }  // namespace reduce
 }  // namespace reduce
 }  // namespace spvtools
 }  // namespace spvtools

+ 4 - 0
3rdparty/spirv-tools/test/reduce/reduce_test_util.h

@@ -59,6 +59,10 @@ const uint32_t kReduceDisassembleOption =
 void NopDiagnostic(spv_message_level_t /*level*/, const char* /*source*/,
 void NopDiagnostic(spv_message_level_t /*level*/, const char* /*source*/,
                    const spv_position_t& /*position*/, const char* /*message*/);
                    const spv_position_t& /*position*/, const char* /*message*/);
 
 
+// Prints reducer messages (for debugging).
+void CLIMessageConsumer(spv_message_level_t level, const char*,
+                        const spv_position_t& position, const char* message);
+
 }  // namespace reduce
 }  // namespace reduce
 }  // namespace spvtools
 }  // namespace spvtools
 
 

+ 2 - 0
3rdparty/spirv-tools/test/reduce/reducer_test.cpp

@@ -229,6 +229,7 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
   std::vector<uint32_t> binary_out;
   std::vector<uint32_t> binary_out;
   spvtools::ReducerOptions reducer_options;
   spvtools::ReducerOptions reducer_options;
   reducer_options.set_step_limit(500);
   reducer_options.set_step_limit(500);
+  reducer_options.set_fail_on_validation_error(true);
   spvtools::ValidatorOptions validator_options;
   spvtools::ValidatorOptions validator_options;
 
 
   Reducer::ReductionResultStatus status = reducer.Run(
   Reducer::ReductionResultStatus status = reducer.Run(
@@ -304,6 +305,7 @@ TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) {
   std::vector<uint32_t> binary_out;
   std::vector<uint32_t> binary_out;
   spvtools::ReducerOptions reducer_options;
   spvtools::ReducerOptions reducer_options;
   reducer_options.set_step_limit(500);
   reducer_options.set_step_limit(500);
+  reducer_options.set_fail_on_validation_error(true);
   spvtools::ValidatorOptions validator_options;
   spvtools::ValidatorOptions validator_options;
 
 
   Reducer::ReductionResultStatus status = reducer.Run(
   Reducer::ReductionResultStatus status = reducer.Run(

+ 11 - 142
3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp

@@ -65,12 +65,16 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
                OpStore %14 %15
                OpStore %14 %15
   )" + epilogue;
   )" + epilogue;
 
 
-  const std::string expected = prologue + R"(
+  const std::string expected_after_2 = prologue + R"(
                OpStore %12 %13
                OpStore %12 %13
          %15 = OpLoad %6 %8
          %15 = OpLoad %6 %8
                OpStore %14 %15
                OpStore %14 %15
   )" + epilogue;
   )" + epilogue;
 
 
+  const std::string expected_after_4 = prologue + R"(
+         %15 = OpLoad %6 %8
+  )" + epilogue;
+
   const auto env = SPV_ENV_UNIVERSAL_1_3;
   const auto env = SPV_ENV_UNIVERSAL_1_3;
   const auto consumer = nullptr;
   const auto consumer = nullptr;
   const auto context =
   const auto context =
@@ -83,149 +87,14 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
   ASSERT_TRUE(ops[1]->PreconditionHolds());
   ASSERT_TRUE(ops[1]->PreconditionHolds());
   ops[1]->TryToApply();
   ops[1]->TryToApply();
 
 
-  CheckEqual(env, expected, context.get());
-}
-
-TEST(RemoveUnreferencedInstructionReductionPassTest, ApplyReduction) {
-  const std::string prologue = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %8 "a"
-               OpName %10 "b"
-               OpName %12 "c"
-               OpName %14 "d"
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeInt 32 1
-          %7 = OpTypePointer Function %6
-          %9 = OpConstant %6 10
-         %11 = OpConstant %6 20
-         %13 = OpConstant %6 30
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-          %8 = OpVariable %7 Function
-         %10 = OpVariable %7 Function
-         %12 = OpVariable %7 Function
-         %14 = OpVariable %7 Function
-  )";
-
-  const std::string epilogue = R"(
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const std::string original = prologue + R"(
-               OpStore %8 %9
-               OpStore %10 %11
-               OpStore %12 %13
-         %15 = OpLoad %6 %8
-               OpStore %14 %15
-  )" + epilogue;
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-
-  std::vector<uint32_t> binary;
-  SpirvTools t(env);
-  ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
-
-  ReductionPass pass(
-      env, spvtools::MakeUnique<
-               RemoveUnreferencedInstructionReductionOpportunityFinder>());
-
-  {
-    // Attempt 1 should remove everything removable.
-    const std::string expected_reduced = prologue + R"(
-         %15 = OpLoad %6 %8
-    )" + epilogue;
-    auto reduced_binary = pass.TryApplyReduction(binary);
-    CheckEqual(env, expected_reduced, reduced_binary);
-  }
-
-  // Attempt 2 should fail as pass with granularity 4 got to end.
-  ASSERT_EQ(0, pass.TryApplyReduction(binary).size());
-
-  {
-    // Attempt 3 should remove first two removable statements.
-    const std::string expected_reduced = prologue + R"(
-               OpStore %12 %13
-         %15 = OpLoad %6 %8
-               OpStore %14 %15
-    )" + epilogue;
-    auto reduced_binary = pass.TryApplyReduction(binary);
-    CheckEqual(env, expected_reduced, reduced_binary);
-  }
-
-  {
-    // Attempt 4 should remove last two removable statements.
-    const std::string expected_reduced = prologue + R"(
-               OpStore %8 %9
-               OpStore %10 %11
-         %15 = OpLoad %6 %8
-    )" + epilogue;
-    auto reduced_binary = pass.TryApplyReduction(binary);
-    CheckEqual(env, expected_reduced, reduced_binary);
-  }
-
-  // Attempt 5 should fail as pass with granularity 2 got to end.
-  ASSERT_EQ(0, pass.TryApplyReduction(binary).size());
-
-  {
-    // Attempt 6 should remove first removable statement.
-    const std::string expected_reduced = prologue + R"(
-               OpStore %10 %11
-               OpStore %12 %13
-         %15 = OpLoad %6 %8
-               OpStore %14 %15
-    )" + epilogue;
-    auto reduced_binary = pass.TryApplyReduction(binary);
-    CheckEqual(env, expected_reduced, reduced_binary);
-  }
-
-  {
-    // Attempt 7 should remove second removable statement.
-    const std::string expected_reduced = prologue + R"(
-               OpStore %8 %9
-               OpStore %12 %13
-         %15 = OpLoad %6 %8
-               OpStore %14 %15
-    )" + epilogue;
-    auto reduced_binary = pass.TryApplyReduction(binary);
-    CheckEqual(env, expected_reduced, reduced_binary);
-  }
-
-  {
-    // Attempt 8 should remove third removable statement.
-    const std::string expected_reduced = prologue + R"(
-               OpStore %8 %9
-               OpStore %10 %11
-         %15 = OpLoad %6 %8
-               OpStore %14 %15
-    )" + epilogue;
-    auto reduced_binary = pass.TryApplyReduction(binary);
-    CheckEqual(env, expected_reduced, reduced_binary);
-  }
-
-  {
-    // Attempt 9 should remove fourth removable statement.
-    const std::string expected_reduced = prologue + R"(
-               OpStore %8 %9
-               OpStore %10 %11
-               OpStore %12 %13
-         %15 = OpLoad %6 %8
-    )" + epilogue;
-    auto reduced_binary = pass.TryApplyReduction(binary);
-    CheckEqual(env, expected_reduced, reduced_binary);
-  }
+  CheckEqual(env, expected_after_2, context.get());
 
 
-  // Attempt 10 should fail as pass with granularity 1 got to end.
-  ASSERT_EQ(0, pass.TryApplyReduction(binary).size());
+  ASSERT_TRUE(ops[2]->PreconditionHolds());
+  ops[2]->TryToApply();
+  ASSERT_TRUE(ops[3]->PreconditionHolds());
+  ops[3]->TryToApply();
 
 
-  ASSERT_TRUE(pass.ReachedMinimumGranularity());
+  CheckEqual(env, expected_after_4, context.get());
 }
 }
 
 
 }  // namespace
 }  // namespace

+ 185 - 0
3rdparty/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp

@@ -3436,6 +3436,191 @@ TEST(StructuredLoopToSelectionReductionPassTest, LongAccessChains) {
   // CheckEqual(env, expected, context.get());
   // CheckEqual(env, expected, context.get());
 }
 }
 
 
+TEST(StructuredLoopToSelectionReductionPassTest, LoopyShaderWithOpDecorate) {
+  // A shader containing a function that contains a loop and some definitions
+  // that are "used" in OpDecorate instructions (outside the function). These
+  // "uses" were causing segfaults because we try to calculate their dominance
+  // information, which doesn't make sense.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "_GLF_color"
+               OpName %14 "buf0"
+               OpMemberName %14 0 "a"
+               OpName %16 ""
+               OpDecorate %9 RelaxedPrecision
+               OpDecorate %9 Location 0
+               OpMemberDecorate %14 0 RelaxedPrecision
+               OpMemberDecorate %14 0 Offset 0
+               OpDecorate %14 Block
+               OpDecorate %16 DescriptorSet 0
+               OpDecorate %16 Binding 0
+               OpDecorate %21 RelaxedPrecision
+               OpDecorate %35 RelaxedPrecision
+               OpDecorate %36 RelaxedPrecision
+               OpDecorate %39 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpConstant %6 1
+         %11 = OpConstantComposite %7 %10 %10 %10 %10
+         %14 = OpTypeStruct %6
+         %15 = OpTypePointer Uniform %14
+         %16 = OpVariable %15 Uniform
+         %17 = OpTypeInt 32 1
+         %18 = OpConstant %17 0
+         %19 = OpTypePointer Uniform %6
+         %28 = OpConstant %6 2
+         %29 = OpTypeBool
+         %31 = OpTypeInt 32 0
+         %32 = OpConstant %31 0
+         %33 = OpTypePointer Output %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpStore %9 %11
+         %20 = OpAccessChain %19 %16 %18
+         %21 = OpLoad %6 %20
+               OpBranch %22
+         %22 = OpLabel
+         %40 = OpPhi %6 %21 %5 %39 %23
+         %30 = OpFOrdLessThan %29 %40 %28
+               OpLoopMerge %24 %23 None
+               OpBranchConditional %30 %23 %24
+         %23 = OpLabel
+         %34 = OpAccessChain %33 %9 %32
+         %35 = OpLoad %6 %34
+         %36 = OpFAdd %6 %35 %10
+               OpStore %34 %36
+         %39 = OpFAdd %6 %40 %10
+               OpBranch %22
+         %24 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
+  const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
+                       .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(1, ops.size());
+
+  ASSERT_TRUE(ops[0]->PreconditionHolds());
+  ops[0]->TryToApply();
+  CheckValid(env, context.get());
+
+  std::string after_op_0 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "_GLF_color"
+               OpName %14 "buf0"
+               OpMemberName %14 0 "a"
+               OpName %16 ""
+               OpDecorate %9 RelaxedPrecision
+               OpDecorate %9 Location 0
+               OpMemberDecorate %14 0 RelaxedPrecision
+               OpMemberDecorate %14 0 Offset 0
+               OpDecorate %14 Block
+               OpDecorate %16 DescriptorSet 0
+               OpDecorate %16 Binding 0
+               OpDecorate %21 RelaxedPrecision
+               OpDecorate %35 RelaxedPrecision
+               OpDecorate %36 RelaxedPrecision
+               OpDecorate %39 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpConstant %6 1
+         %11 = OpConstantComposite %7 %10 %10 %10 %10
+         %14 = OpTypeStruct %6
+         %15 = OpTypePointer Uniform %14
+         %16 = OpVariable %15 Uniform
+         %17 = OpTypeInt 32 1
+         %18 = OpConstant %17 0
+         %19 = OpTypePointer Uniform %6
+         %28 = OpConstant %6 2
+         %29 = OpTypeBool
+         %31 = OpTypeInt 32 0
+         %32 = OpConstant %31 0
+         %33 = OpTypePointer Output %6
+         %41 = OpUndef %6                          ; Added
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpStore %9 %11
+         %20 = OpAccessChain %19 %16 %18
+         %21 = OpLoad %6 %20
+               OpBranch %22
+         %22 = OpLabel
+         %40 = OpPhi %6 %21 %5 %41 %23             ; Changed
+         %30 = OpFOrdLessThan %29 %40 %28
+               OpSelectionMerge %24 None           ; Changed
+               OpBranchConditional %30 %24 %24
+         %23 = OpLabel
+         %34 = OpAccessChain %33 %9 %32
+         %35 = OpLoad %6 %34
+         %36 = OpFAdd %6 %35 %10
+               OpStore %34 %36
+         %39 = OpFAdd %6 %41 %10                   ; Changed
+               OpBranch %22
+         %24 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  CheckEqual(env, after_op_0, context.get());
+}
+
+TEST(StructuredLoopToSelectionReductionPassTest,
+     LoopWithCombinedHeaderAndContinue) {
+  // A shader containing a loop where the header is also the continue target.
+  // For now, we don't simplify such loops.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+         %30 = OpConstantFalse %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %10
+         %10 = OpLabel                       ; loop header and continue target
+               OpLoopMerge %12 %10 None
+               OpBranchConditional %30 %10 %12
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
+  const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
+                       .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(0, ops.size());
+}
+
 }  // namespace
 }  // namespace
 }  // namespace reduce
 }  // namespace reduce
 }  // namespace spvtools
 }  // namespace spvtools

+ 12 - 4
3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp

@@ -113,7 +113,9 @@ class OpVariableDuplicatorReductionOpportunityFinder
 
 
 TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) {
 TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) {
   // A module whose global values are all referenced, so that any application of
   // A module whose global values are all referenced, so that any application of
-  // MakeModuleInvalidPass will make the module invalid.
+  // MakeModuleInvalidPass will make the module invalid. Check that the reducer
+  // makes no progress, as every step will be invalid and treated as
+  // uninteresting.
   std::string original = R"(
   std::string original = R"(
                OpCapability Shader
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
           %1 = OpExtInstImport "GLSL.std.450"
@@ -219,6 +221,8 @@ TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) {
   std::vector<uint32_t> binary_out;
   std::vector<uint32_t> binary_out;
   spvtools::ReducerOptions reducer_options;
   spvtools::ReducerOptions reducer_options;
   reducer_options.set_step_limit(500);
   reducer_options.set_step_limit(500);
+  // Don't fail on a validation error; just treat it as uninteresting.
+  reducer_options.set_fail_on_validation_error(false);
   spvtools::ValidatorOptions validator_options;
   spvtools::ValidatorOptions validator_options;
 
 
   Reducer::ReductionResultStatus status = reducer.Run(
   Reducer::ReductionResultStatus status = reducer.Run(
@@ -428,6 +432,8 @@ TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) {
   std::vector<uint32_t> binary_out;
   std::vector<uint32_t> binary_out;
   spvtools::ReducerOptions reducer_options;
   spvtools::ReducerOptions reducer_options;
   reducer_options.set_step_limit(500);
   reducer_options.set_step_limit(500);
+  // Don't fail on a validation error; just treat it as uninteresting.
+  reducer_options.set_fail_on_validation_error(false);
   spvtools::ValidatorOptions validator_options;
   spvtools::ValidatorOptions validator_options;
 
 
   Reducer::ReductionResultStatus status = reducer.Run(
   Reducer::ReductionResultStatus status = reducer.Run(
@@ -518,6 +524,7 @@ TEST(ValidationDuringReductionTest, CheckValidationOptions) {
   spvtools::ValidatorOptions validator_options;
   spvtools::ValidatorOptions validator_options;
 
 
   reducer_options.set_step_limit(3);
   reducer_options.set_step_limit(3);
+  reducer_options.set_fail_on_validation_error(true);
 
 
   // Reduction should fail because the initial state is invalid without the
   // Reduction should fail because the initial state is invalid without the
   // "skip-block-layout" validator option. Note that the interestingness test
   // "skip-block-layout" validator option. Note that the interestingness test
@@ -553,8 +560,9 @@ TEST(ValidationDuringReductionTest, CheckValidationOptions) {
   validator_options.SetUniversalLimit(spv_validator_limit_max_local_variables,
   validator_options.SetUniversalLimit(spv_validator_limit_max_local_variables,
                                       2);
                                       2);
 
 
-  // Reduction should "complete"; after one step, a local variable is added and
-  // the module becomes "invalid" given the validator limits.
+  // Reduction should now fail due to reaching an invalid state; after one step,
+  // a local variable is added and the module becomes "invalid" given the
+  // validator limits.
   {
   {
     Reducer reducer(env);
     Reducer reducer(env);
     setupReducerForCheckValidationOptions(&reducer);
     setupReducerForCheckValidationOptions(&reducer);
@@ -563,7 +571,7 @@ TEST(ValidationDuringReductionTest, CheckValidationOptions) {
         reducer.Run(std::vector<uint32_t>(binary_in), &binary_out,
         reducer.Run(std::vector<uint32_t>(binary_in), &binary_out,
                     reducer_options, validator_options);
                     reducer_options, validator_options);
 
 
-    ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
+    ASSERT_EQ(status, Reducer::ReductionResultStatus::kStateInvalid);
   }
   }
 }
 }
 
 

+ 137 - 2
3rdparty/spirv-tools/test/val/val_cfg_test.cpp

@@ -1203,7 +1203,11 @@ std::string GetUnreachableMergeWithBranchUse(SpvCapability cap,
 TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) {
 TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) {
   CompileSuccessfully(
   CompileSuccessfully(
       GetUnreachableMergeWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0));
       GetUnreachableMergeWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0));
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Statically reachable blocks cannot be terminated by OpUnreachable"));
 }
 }
 
 
 TEST_F(ValidateCFG, WebGPUUnreachableMergeWithBranchUse) {
 TEST_F(ValidateCFG, WebGPUUnreachableMergeWithBranchUse) {
@@ -1938,7 +1942,10 @@ OpDecorate %id BuiltIn GlobalInvocationId
   str += "OpFunctionEnd";
   str += "OpFunctionEnd";
 
 
   CompileSuccessfully(str);
   CompileSuccessfully(str);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Statically reachable blocks cannot be terminated by "
+                        "OpUnreachable"));
 }
 }
 
 
 TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) {
 TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) {
@@ -2852,6 +2859,134 @@ TEST_F(ValidateCFG, BranchTargetMustBeLabel) {
                         "be the ID of an OpLabel instruction"));
                         "be the ID of an OpLabel instruction"));
 }
 }
 
 
+TEST_F(ValidateCFG, ReachableOpUnreachableOneBlock) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Statically reachable blocks cannot be terminated by OpUnreachable"));
+}
+
+TEST_F(ValidateCFG, ReachableOpUnreachableOpBranch) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %block
+%block = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Statically reachable blocks cannot be terminated by OpUnreachable"));
+}
+
+TEST_F(ValidateCFG, ReachableOpUnreachableOpBranchConditional) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranchConditional %undef %block %unreachable
+%block = OpLabel
+OpReturn
+%unreachable = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Statically reachable blocks cannot be terminated by OpUnreachable"));
+}
+
+TEST_F(ValidateCFG, ReachableOpUnreachableOpSwitch) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%undef = OpUndef %int
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSwitch %undef %block1 0 %unreachable 1 %block2
+%block1 = OpLabel
+OpReturn
+%unreachable = OpLabel
+OpUnreachable
+%block2 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Statically reachable blocks cannot be terminated by OpUnreachable"));
+}
+
+TEST_F(ValidateCFG, ReachableOpUnreachableLoop) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %unreachable %loop None
+OpBranchConditional %undef %loop %unreachable
+%unreachable = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Statically reachable blocks cannot be terminated by OpUnreachable"));
+}
+
 TEST_F(ValidateCFG, OneContinueTwoBackedges) {
 TEST_F(ValidateCFG, OneContinueTwoBackedges) {
   const std::string text = R"(
   const std::string text = R"(
 OpCapability Shader
 OpCapability Shader

+ 8 - 6
3rdparty/spirv-tools/test/val/val_id_test.cpp

@@ -6203,20 +6203,22 @@ TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock1) {
 %4 = OpTypeFunction %3
 %4 = OpTypeFunction %3
 %5 = OpFunction %1 None %2
 %5 = OpFunction %1 None %2
 %6 = OpLabel
 %6 = OpLabel
-%7 = OpFunctionCall %3 %8
+OpReturn
+%7 = OpLabel
+%8 = OpFunctionCall %3 %9
 OpUnreachable
 OpUnreachable
 OpFunctionEnd
 OpFunctionEnd
-%8 = OpFunction %3 None %4
-%9 = OpLabel
-OpReturnValue %7
+%9 = OpFunction %3 None %4
+%10 = OpLabel
+OpReturnValue %8
 OpFunctionEnd
 OpFunctionEnd
 )";
 )";
 
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("ID 7[%7] defined in block 6[%6] does not dominate its "
-                        "use in block 9[%9]\n  %9 = OpLabel"));
+              HasSubstr("ID 8[%8] defined in block 7[%7] does not dominate its "
+                        "use in block 10[%10]\n  %10 = OpLabel"));
 }
 }
 
 
 TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock2) {
 TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock2) {

+ 70 - 7
3rdparty/spirv-tools/test/val/val_image_test.cpp

@@ -2283,7 +2283,7 @@ TEST_F(ValidateImage, FetchNotImage) {
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %sampler = OpLoad %type_sampler %uniform_sampler
 %sampler = OpLoad %type_sampler %uniform_sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
-%res1 = OpImageFetch %f32vec4 %simg %u32vec2_01
+%res1 = OpImageFetch %f32vec4 %sampler %u32vec2_01
 )";
 )";
 
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   CompileSuccessfully(GenerateShaderCode(body).c_str());
@@ -2292,6 +2292,21 @@ TEST_F(ValidateImage, FetchNotImage) {
               HasSubstr("Expected Image to be of type OpTypeImage"));
               HasSubstr("Expected Image to be of type OpTypeImage"));
 }
 }
 
 
+TEST_F(ValidateImage, FetchSampledImageDirectly) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageFetch %f32vec4 %simg %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpSampledImage instruction must not appear as operand "
+                        "for OpImageFetch"));
+}
+
 TEST_F(ValidateImage, FetchNotSampled) {
 TEST_F(ValidateImage, FetchNotSampled) {
   const std::string body = R"(
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
 %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
@@ -3191,7 +3206,7 @@ TEST_F(ValidateImage, QueryFormatNotImage) {
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %sampler = OpLoad %type_sampler %uniform_sampler
 %sampler = OpLoad %type_sampler %uniform_sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
-%res1 = OpImageQueryFormat %u32 %simg
+%res1 = OpImageQueryFormat %u32 %sampler
 )";
 )";
 
 
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   CompileSuccessfully(GenerateKernelCode(body).c_str());
@@ -3227,7 +3242,7 @@ TEST_F(ValidateImage, QueryOrderNotImage) {
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %sampler = OpLoad %type_sampler %uniform_sampler
 %sampler = OpLoad %type_sampler %uniform_sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
-%res1 = OpImageQueryOrder %u32 %simg
+%res1 = OpImageQueryOrder %u32 %sampler
 )";
 )";
 
 
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   CompileSuccessfully(GenerateKernelCode(body).c_str());
@@ -3276,7 +3291,7 @@ TEST_F(ValidateImage, QuerySizeLodNotImage) {
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %sampler = OpLoad %type_sampler %uniform_sampler
 %sampler = OpLoad %type_sampler %uniform_sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
-%res1 = OpImageQuerySizeLod %u32vec2 %simg %u32_1
+%res1 = OpImageQuerySizeLod %u32vec2 %sampler %u32_1
 )";
 )";
 
 
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   CompileSuccessfully(GenerateKernelCode(body).c_str());
@@ -3285,6 +3300,21 @@ TEST_F(ValidateImage, QuerySizeLodNotImage) {
               HasSubstr("Expected Image to be of type OpTypeImage"));
               HasSubstr("Expected Image to be of type OpTypeImage"));
 }
 }
 
 
+TEST_F(ValidateImage, QuerySizeLodSampledImageDirectly) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageQuerySizeLod %u32vec2 %simg %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpSampledImage instruction must not appear as operand "
+                        "for OpImageQuerySizeLod"));
+}
+
 TEST_F(ValidateImage, QuerySizeLodWrongImageDim) {
 TEST_F(ValidateImage, QuerySizeLodWrongImageDim) {
   const std::string body = R"(
   const std::string body = R"(
 %img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001
 %img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001
@@ -3348,7 +3378,7 @@ TEST_F(ValidateImage, QuerySizeNotImage) {
 %img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010
 %img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010
 %sampler = OpLoad %type_sampler %uniform_sampler
 %sampler = OpLoad %type_sampler %uniform_sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
-%res1 = OpImageQuerySize %u32vec2 %simg
+%res1 = OpImageQuerySize %u32vec2 %sampler
 )";
 )";
 
 
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   CompileSuccessfully(GenerateKernelCode(body).c_str());
@@ -3357,6 +3387,21 @@ TEST_F(ValidateImage, QuerySizeNotImage) {
               HasSubstr("Expected Image to be of type OpTypeImage"));
               HasSubstr("Expected Image to be of type OpTypeImage"));
 }
 }
 
 
+TEST_F(ValidateImage, QuerySizeSampledImageDirectly) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageQuerySize %u32vec2 %simg
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpSampledImage instruction must not appear as operand "
+                        "for OpImageQuerySize"));
+}
+
 TEST_F(ValidateImage, QuerySizeDimSubpassDataBad) {
 TEST_F(ValidateImage, QuerySizeDimSubpassDataBad) {
   const std::string body = R"(
   const std::string body = R"(
 %img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002
 %img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002
@@ -3531,7 +3576,7 @@ TEST_F(ValidateImage, QueryLevelsNotImage) {
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
 %sampler = OpLoad %type_sampler %uniform_sampler
 %sampler = OpLoad %type_sampler %uniform_sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
-%res1 = OpImageQueryLevels %u32 %simg
+%res1 = OpImageQueryLevels %u32 %sampler
 )";
 )";
 
 
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   CompileSuccessfully(GenerateKernelCode(body).c_str());
@@ -3540,6 +3585,21 @@ TEST_F(ValidateImage, QueryLevelsNotImage) {
               HasSubstr("Expected Image to be of type OpTypeImage"));
               HasSubstr("Expected Image to be of type OpTypeImage"));
 }
 }
 
 
+TEST_F(ValidateImage, QueryLevelsSampledImageDirectly) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageQueryLevels %u32 %simg
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpSampledImage instruction must not appear as operand "
+                        "for OpImageQueryLevels"));
+}
+
 TEST_F(ValidateImage, QueryLevelsWrongDim) {
 TEST_F(ValidateImage, QueryLevelsWrongDim) {
   const std::string body = R"(
   const std::string body = R"(
 %img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001
 %img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001
@@ -4481,7 +4541,10 @@ TEST_F(ValidateImage, Issue2463NoSegFault) {
 )";
 )";
 
 
   CompileSuccessfully(spirv);
   CompileSuccessfully(spirv);
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpSampledImage instruction must not appear as operand "
+                        "for OpReturnValue"));
 }
 }
 
 
 }  // namespace
 }  // namespace

+ 8 - 6
3rdparty/spirv-tools/test/val/val_validation_state_test.cpp

@@ -56,15 +56,16 @@ OpExecutionMode %1 OriginUpperLeft
 %4 = OpTypeFunction %void
 %4 = OpTypeFunction %void
 %float = OpTypeFloat 32
 %float = OpTypeFloat 32
 %_struct_6 = OpTypeStruct %float %float
 %_struct_6 = OpTypeStruct %float %float
+%null = OpConstantNull %_struct_6
 %7 = OpTypeFunction %_struct_6
 %7 = OpTypeFunction %_struct_6
 %12 = OpFunction %_struct_6 None %7
 %12 = OpFunction %_struct_6 None %7
 %13 = OpLabel
 %13 = OpLabel
-OpUnreachable
+OpReturnValue %null
 OpFunctionEnd
 OpFunctionEnd
 %9 = OpFunction %_struct_6 None %7
 %9 = OpFunction %_struct_6 None %7
 %10 = OpLabel
 %10 = OpLabel
 %11 = OpFunctionCall %_struct_6 %12
 %11 = OpFunctionCall %_struct_6 %12
-OpUnreachable
+OpReturnValue %null
 OpFunctionEnd
 OpFunctionEnd
 %1 = OpFunction %void Pure|Const %4
 %1 = OpFunction %void Pure|Const %4
 %8 = OpLabel
 %8 = OpLabel
@@ -89,7 +90,7 @@ OpFunctionEnd
 %1 = OpFunction %void Pure|Const %4
 %1 = OpFunction %void Pure|Const %4
 %8 = OpLabel
 %8 = OpLabel
 %2 = OpFunctionCall %_struct_6 %9
 %2 = OpFunctionCall %_struct_6 %9
-OpUnreachable
+OpReturn
 OpFunctionEnd
 OpFunctionEnd
 )";
 )";
 
 
@@ -100,16 +101,17 @@ OpExecutionMode %1 OriginUpperLeft
 %4 = OpTypeFunction %void
 %4 = OpTypeFunction %void
 %float = OpTypeFloat 32
 %float = OpTypeFloat 32
 %_struct_6 = OpTypeStruct %float %float
 %_struct_6 = OpTypeStruct %float %float
+%null = OpConstantNull %_struct_6
 %7 = OpTypeFunction %_struct_6
 %7 = OpTypeFunction %_struct_6
 %9 = OpFunction %_struct_6 None %7
 %9 = OpFunction %_struct_6 None %7
 %10 = OpLabel
 %10 = OpLabel
 %11 = OpFunctionCall %_struct_6 %12
 %11 = OpFunctionCall %_struct_6 %12
-OpUnreachable
+OpReturnValue %null
 OpFunctionEnd
 OpFunctionEnd
 %12 = OpFunction %_struct_6 None %7
 %12 = OpFunction %_struct_6 None %7
 %13 = OpLabel
 %13 = OpLabel
 %14 = OpFunctionCall %_struct_6 %9
 %14 = OpFunctionCall %_struct_6 %9
-OpUnreachable
+OpReturnValue %null
 OpFunctionEnd
 OpFunctionEnd
 %1 = OpFunction %void Pure|Const %4
 %1 = OpFunction %void Pure|Const %4
 %8 = OpLabel
 %8 = OpLabel
@@ -303,7 +305,7 @@ TEST_F(ValidationStateTest, CheckWebGPUIndirectlyRecursiveBodyBad) {
             ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
             ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
   EXPECT_THAT(getDiagnosticString(),
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("For WebGPU, functions need to be defined before being "
               HasSubstr("For WebGPU, functions need to be defined before being "
-                        "called.\n  %9 = OpFunctionCall %_struct_5 %10\n"));
+                        "called.\n  %10 = OpFunctionCall %_struct_5 %11\n"));
 }
 }
 
 
 TEST_F(ValidationStateTest,
 TEST_F(ValidationStateTest,

+ 4 - 0
3rdparty/spirv-tools/tools/opt/opt.cpp

@@ -384,6 +384,8 @@ Options (in lexicographical order):
                Current workarounds: Avoid OpUnreachable in loops.
                Current workarounds: Avoid OpUnreachable in loops.
   --unify-const
   --unify-const
                Remove the duplicated constants.
                Remove the duplicated constants.
+  --validate-after-all
+               Validate the module after each pass is performed.
   -h, --help
   -h, --help
                Print this help.
                Print this help.
   --version
   --version
@@ -628,6 +630,8 @@ OptStatus ParseFlags(int argc, const char** argv,
 
 
         optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
         optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
         optimizer->RegisterWebGPUPasses();
         optimizer->RegisterWebGPUPasses();
+      } else if (0 == strcmp(cur_arg, "--validate-after-all")) {
+        optimizer->SetValidateAfterAll(true);
       } else {
       } else {
         // Some passes used to accept the form '--pass arg', canonicalize them
         // Some passes used to accept the form '--pass arg', canonicalize them
         // to '--pass=arg'.
         // to '--pass=arg'.

+ 7 - 30
3rdparty/spirv-tools/tools/reduce/reduce.cpp

@@ -18,21 +18,10 @@
 #include <functional>
 #include <functional>
 
 
 #include "source/opt/build_module.h"
 #include "source/opt/build_module.h"
-#include "source/opt/ir_context.h"
 #include "source/opt/log.h"
 #include "source/opt/log.h"
-#include "source/reduce/merge_blocks_reduction_opportunity_finder.h"
-#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
-#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h"
-#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
 #include "source/reduce/reducer.h"
 #include "source/reduce/reducer.h"
-#include "source/reduce/remove_function_reduction_opportunity_finder.h"
-#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
-#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
-#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
 #include "source/spirv_reducer_options.h"
 #include "source/spirv_reducer_options.h"
-#include "source/util/make_unique.h"
 #include "source/util/string_utils.h"
 #include "source/util/string_utils.h"
-#include "spirv-tools/libspirv.hpp"
 #include "tools/io.h"
 #include "tools/io.h"
 #include "tools/util/cli_consumer.h"
 #include "tools/util/cli_consumer.h"
 
 
@@ -103,6 +92,10 @@ should be the path to a script.
 NOTE: The reducer is a work in progress.
 NOTE: The reducer is a work in progress.
 
 
 Options (in lexicographical order):
 Options (in lexicographical order):
+
+  --fail-on-validation-error
+               Stop reduction with an error if any reduction step produces a
+               SPIR-V module that fails to validate.
   -h, --help
   -h, --help
                Print this help.
                Print this help.
   --step-limit
   --step-limit
@@ -172,6 +165,8 @@ ReduceStatus ParseFlags(int argc, const char** argv, const char** in_file,
       assert(!*interestingness_test);
       assert(!*interestingness_test);
       *interestingness_test = cur_arg;
       *interestingness_test = cur_arg;
       positional_arg_index++;
       positional_arg_index++;
+    } else if (0 == strcmp(cur_arg, "--fail-on-validation-error")) {
+      reducer_options->set_fail_on_validation_error(true);
     } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) {
     } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) {
       validator_options->SetRelaxLogicalPointer(true);
       validator_options->SetRelaxLogicalPointer(true);
     } else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
     } else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
@@ -246,25 +241,7 @@ int main(int argc, const char** argv) {
         return ExecuteCommand(command);
         return ExecuteCommand(command);
       });
       });
 
 
-  reducer.AddReductionPass(
-      spvtools::MakeUnique<
-          RemoveOpNameInstructionReductionOpportunityFinder>());
-  reducer.AddReductionPass(
-      spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
-  reducer.AddReductionPass(
-      spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
-  reducer.AddReductionPass(
-      spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
-  reducer.AddReductionPass(
-      spvtools::MakeUnique<
-          RemoveUnreferencedInstructionReductionOpportunityFinder>());
-  reducer.AddReductionPass(
-      spvtools::MakeUnique<
-          StructuredLoopToSelectionReductionOpportunityFinder>());
-  reducer.AddReductionPass(
-      spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
-  reducer.AddReductionPass(
-      spvtools::MakeUnique<RemoveFunctionReductionOpportunityFinder>());
+  reducer.AddDefaultReductionPasses();
 
 
   reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
   reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);