Бранимир Караџић 6 лет назад
Родитель
Сommit
84f36f2def
37 измененных файлов с 672 добавлено и 407 удалено
  1. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  2. 5 0
      3rdparty/spirv-tools/include/spirv-tools/libspirv.h
  3. 5 0
      3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp
  4. 56 22
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  5. 11 7
      3rdparty/spirv-tools/source/fuzz/fuzzer.h
  6. 17 0
      3rdparty/spirv-tools/source/opt/block_merge_util.cpp
  7. 6 16
      3rdparty/spirv-tools/source/opt/inline_pass.cpp
  8. 20 13
      3rdparty/spirv-tools/source/opt/optimizer.cpp
  9. 10 3
      3rdparty/spirv-tools/source/opt/register_pressure.cpp
  10. 2 1
      3rdparty/spirv-tools/source/print.cpp
  11. 7 1
      3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp
  12. 3 0
      3rdparty/spirv-tools/source/spirv_fuzzer_options.h
  13. 2 2
      3rdparty/spirv-tools/source/val/basic_block.h
  14. 3 4
      3rdparty/spirv-tools/source/val/function.cpp
  15. 21 2
      3rdparty/spirv-tools/source/val/validate_cfg.cpp
  16. 0 7
      3rdparty/spirv-tools/source/val/validate_debug.cpp
  17. 16 0
      3rdparty/spirv-tools/test/fuzz/fuzz_test_util.cpp
  18. 7 0
      3rdparty/spirv-tools/test/fuzz/fuzz_test_util.h
  19. 2 4
      3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp
  20. 2 4
      3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp
  21. 0 112
      3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp
  22. 1 1
      3rdparty/spirv-tools/test/fuzz/transformation_copy_object_test.cpp
  23. 12 8
      3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp
  24. 0 1
      3rdparty/spirv-tools/test/opt/aggressive_dead_code_elim_test.cpp
  25. 45 2
      3rdparty/spirv-tools/test/opt/block_merge_test.cpp
  26. 6 2
      3rdparty/spirv-tools/test/opt/dead_branch_elim_test.cpp
  27. 3 3
      3rdparty/spirv-tools/test/opt/inline_test.cpp
  28. 23 47
      3rdparty/spirv-tools/test/opt/local_ssa_elim_test.cpp
  29. 4 2
      3rdparty/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp
  30. 4 5
      3rdparty/spirv-tools/test/opt/pass_merge_return_test.cpp
  31. 40 0
      3rdparty/spirv-tools/test/opt/register_liveness.cpp
  32. 47 0
      3rdparty/spirv-tools/test/opt/strip_reflect_info_test.cpp
  33. 10 7
      3rdparty/spirv-tools/test/reduce/reducer_test.cpp
  34. 19 14
      3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp
  35. 19 12
      3rdparty/spirv-tools/test/tools/opt/flags.py
  36. 115 28
      3rdparty/spirv-tools/test/val/val_cfg_test.cpp
  37. 128 76
      3rdparty/spirv-tools/test/val/val_webgpu_test.cpp

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

@@ -1 +1 @@
-"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-178-g85f3e93d"
+"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-186-gb334829a"

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

@@ -627,6 +627,11 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
     spv_fuzzer_options options, uint32_t shrinker_step_limit);
 
+// Enables running the validator after every pass is applied during a fuzzing
+// run.
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
+    spv_fuzzer_options options);
+
 // Encodes the given SPIR-V assembly text to its binary representation. The
 // length parameter specifies the number of bytes for text. Encoded binary will
 // be stored into *binary. Any error will be written into *diagnostic if

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

@@ -229,6 +229,11 @@ class FuzzerOptions {
     spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit);
   }
 
+  // See spvFuzzerOptionsEnableFuzzerPassValidation.
+  void enable_fuzzer_pass_validation() {
+    spvFuzzerOptionsEnableFuzzerPassValidation(options_);
+  }
+
  private:
   spv_fuzzer_options options_;
 };

+ 56 - 22
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp

@@ -65,13 +65,26 @@ void MaybeAddPass(
 }  // namespace
 
 struct Fuzzer::Impl {
-  explicit Impl(spv_target_env env) : target_env(env) {}
-
-  const spv_target_env target_env;  // Target environment.
-  MessageConsumer consumer;         // Message consumer.
+  explicit Impl(spv_target_env env, uint32_t random_seed,
+                bool validate_after_each_pass)
+      : target_env(env),
+        seed(random_seed),
+        validate_after_each_fuzzer_pass(validate_after_each_pass) {}
+
+  bool ApplyPassAndCheckValidity(FuzzerPass* pass,
+                                 const opt::IRContext& ir_context,
+                                 const spvtools::SpirvTools& tools) const;
+
+  const spv_target_env target_env;       // Target environment.
+  const uint32_t seed;                   // Seed for random number generator.
+  bool validate_after_each_fuzzer_pass;  // Determines whether the validator
+  // should be invoked after every fuzzer pass.
+  MessageConsumer consumer;  // Message consumer.
 };
 
-Fuzzer::Fuzzer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
+Fuzzer::Fuzzer(spv_target_env env, uint32_t seed,
+               bool validate_after_each_fuzzer_pass)
+    : impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass)) {}
 
 Fuzzer::~Fuzzer() = default;
 
@@ -79,10 +92,27 @@ void Fuzzer::SetMessageConsumer(MessageConsumer c) {
   impl_->consumer = std::move(c);
 }
 
+bool Fuzzer::Impl::ApplyPassAndCheckValidity(
+    FuzzerPass* pass, const opt::IRContext& ir_context,
+    const spvtools::SpirvTools& tools) const {
+  pass->Apply();
+  if (validate_after_each_fuzzer_pass) {
+    std::vector<uint32_t> binary_to_validate;
+    ir_context.module()->ToBinary(&binary_to_validate, false);
+    if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size())) {
+      consumer(SPV_MSG_INFO, nullptr, {},
+               "Binary became invalid during fuzzing (set a breakpoint to "
+               "inspect); stopping.");
+      return false;
+    }
+  }
+  return true;
+}
+
 Fuzzer::FuzzerResultStatus Fuzzer::Run(
     const std::vector<uint32_t>& binary_in,
     const protobufs::FactSequence& initial_facts,
-    spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out,
+    std::vector<uint32_t>* binary_out,
     protobufs::TransformationSequence* transformation_sequence_out) const {
   // Check compatibility between the library version being linked with and the
   // header files being used.
@@ -108,10 +138,8 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
       impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size());
   assert(ir_context);
 
-  // Make a PRNG, either from a given seed or from a random device.
-  PseudoRandomGenerator random_generator(
-      options->has_random_seed ? options->random_seed
-                               : static_cast<uint32_t>(std::random_device()()));
+  // Make a PRNG from the seed passed to the fuzzer on creation.
+  PseudoRandomGenerator random_generator(impl_->seed);
 
   // The fuzzer will introduce new ids into the module.  The module's id bound
   // gives the smallest id that can be used for this purpose.  We add an offset
@@ -128,9 +156,13 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
 
   // Add some essential ingredients to the module if they are not already
   // present, such as boolean constants.
-  FuzzerPassAddUsefulConstructs(ir_context.get(), &fact_manager,
-                                &fuzzer_context, transformation_sequence_out)
-      .Apply();
+  FuzzerPassAddUsefulConstructs add_useful_constructs(
+      ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
+  if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context,
+                                        tools)) {
+    return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
+  }
 
   // Apply some semantics-preserving passes.
   std::vector<std::unique_ptr<FuzzerPass>> passes;
@@ -168,7 +200,11 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
          (is_first ||
           fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
     is_first = false;
-    passes[fuzzer_context.RandomIndex(passes)]->Apply();
+    if (!impl_->ApplyPassAndCheckValidity(
+            passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context,
+            tools)) {
+      return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
+    }
   }
 
   // Now apply some passes that it does not make sense to apply repeatedly,
@@ -186,15 +222,13 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
   MaybeAddPass<FuzzerPassAdjustSelectionControls>(
       &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
       transformation_sequence_out);
+  MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
+      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
   for (auto& pass : final_passes) {
-    pass->Apply();
-  }
-
-  if (fuzzer_context.ChooseEven()) {
-    FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager,
-                                          &fuzzer_context,
-                                          transformation_sequence_out)
-        .Apply();
+    if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
+      return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
+    }
   }
 
   // Encode the module as a binary.

+ 11 - 7
3rdparty/spirv-tools/source/fuzz/fuzzer.h

@@ -32,11 +32,16 @@ class Fuzzer {
   enum class FuzzerResultStatus {
     kComplete,
     kFailedToCreateSpirvToolsInterface,
+    kFuzzerPassLedToInvalidModule,
     kInitialBinaryInvalid,
   };
 
-  // Constructs a fuzzer from the given target environment.
-  explicit Fuzzer(spv_target_env env);
+  // Constructs a fuzzer from the given target environment |env|.  |seed| is a
+  // seed for pseudo-random number generation.
+  // |validate_after_each_fuzzer_pass| controls whether the validator will be
+  // invoked after every fuzzer pass is applied.
+  explicit Fuzzer(spv_target_env env, uint32_t seed,
+                  bool validate_after_each_fuzzer_pass);
 
   // Disables copy/move constructor/assignment operations.
   Fuzzer(const Fuzzer&) = delete;
@@ -51,14 +56,13 @@ class Fuzzer {
   void SetMessageConsumer(MessageConsumer consumer);
 
   // Transforms |binary_in| to |binary_out| by running a number of randomized
-  // fuzzer passes, controlled via |options|.  Initial facts about the input
-  // binary and the context in which it will execute are provided via
-  // |initial_facts|.  The transformation sequence that was applied is returned
-  // via |transformation_sequence_out|.
+  // fuzzer passes.  Initial facts about the input binary and the context in
+  // which it will execute are provided via |initial_facts|.  The transformation
+  // sequence that was applied is returned via |transformation_sequence_out|.
   FuzzerResultStatus Run(
       const std::vector<uint32_t>& binary_in,
       const protobufs::FactSequence& initial_facts,
-      spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out,
+      std::vector<uint32_t>* binary_out,
       protobufs::TransformationSequence* transformation_sequence_out) const;
 
  private:

+ 17 - 0
3rdparty/spirv-tools/source/opt/block_merge_util.cpp

@@ -49,6 +49,18 @@ bool IsMerge(IRContext* context, BasicBlock* block) {
   return IsMerge(context, block->id());
 }
 
+// Returns true if |id| is the continue target of a merge instruction.
+bool IsContinue(IRContext* context, uint32_t id) {
+  return !context->get_def_use_mgr()->WhileEachUse(
+      id, [](Instruction* user, uint32_t index) {
+        SpvOp op = user->opcode();
+        if (op == SpvOpLoopMerge && index == 1u) {
+          return false;
+        }
+        return true;
+      });
+}
+
 // Removes any OpPhi instructions in |block|, which should have exactly one
 // predecessor, replacing uses of OpPhi ids with the ids associated with the
 // predecessor.
@@ -86,6 +98,11 @@ bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) {
     return false;
   }
 
+  if (pred_is_merge && IsContinue(context, lab_id)) {
+    // Cannot merge a continue target with a merge block.
+    return false;
+  }
+
   // Don't bother trying to merge unreachable blocks.
   if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) {
     if (!dominators->IsReachable(block)) return false;

+ 6 - 16
3rdparty/spirv-tools/source/opt/inline_pass.cpp

@@ -27,7 +27,6 @@
 static const int kSpvFunctionCallFunctionId = 2;
 static const int kSpvFunctionCallArgumentId = 3;
 static const int kSpvReturnValueId = 0;
-static const int kSpvLoopMergeContinueTargetIdInIdx = 1;
 
 namespace spvtools {
 namespace opt {
@@ -285,19 +284,14 @@ bool InlinePass::GenInlineCode(
     if (rid != 0) callee_result_ids.insert(rid);
   });
 
-  // If the caller is in a single-block loop, and the callee has multiple
-  // blocks, then the normal inlining logic will place the OpLoopMerge in
-  // the last of several blocks in the loop.  Instead, it should be placed
-  // at the end of the first block.  First determine if the caller is in a
-  // single block loop.  We'll wait to move the OpLoopMerge until the end
-  // of the regular inlining logic, and only if necessary.
-  bool caller_is_single_block_loop = false;
+  // If the caller is a loop header and the callee has multiple blocks, then the
+  // normal inlining logic will place the OpLoopMerge in the last of several
+  // blocks in the loop.  Instead, it should be placed at the end of the first
+  // block.  We'll wait to move the OpLoopMerge until the end of the regular
+  // inlining logic, and only if necessary.
   bool caller_is_loop_header = false;
-  if (auto* loop_merge = call_block_itr->GetLoopMergeInst()) {
+  if (call_block_itr->GetLoopMergeInst()) {
     caller_is_loop_header = true;
-    caller_is_single_block_loop =
-        call_block_itr->id() ==
-        loop_merge->GetSingleWordInOperand(kSpvLoopMergeContinueTargetIdInIdx);
   }
 
   bool callee_begins_with_structured_header =
@@ -611,10 +605,6 @@ bool InlinePass::GenInlineCode(
     --loop_merge_itr;
     assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
     std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
-    if (caller_is_single_block_loop) {
-      // Also, update its continue target to point to the last block.
-      cp_inst->SetInOperand(kSpvLoopMergeContinueTargetIdInIdx, {last->id()});
-    }
     first->tail().InsertBefore(std::move(cp_inst));
 
     // Remove the loop merge from the last block.

+ 20 - 13
3rdparty/spirv-tools/source/opt/optimizer.cpp

@@ -198,28 +198,35 @@ Optimizer& Optimizer::RegisterSizePasses() {
       .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateMergeReturnPass())
       .RegisterPass(CreateInlineExhaustivePass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateEliminateDeadFunctionsPass())
       .RegisterPass(CreatePrivateToLocalPass())
-      .RegisterPass(CreateScalarReplacementPass())
-      .RegisterPass(CreateLocalAccessChainConvertPass())
-      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
-      .RegisterPass(CreateLocalSingleStoreElimPass())
-      .RegisterPass(CreateAggressiveDCEPass())
-      .RegisterPass(CreateSimplificationPass())
-      .RegisterPass(CreateDeadInsertElimPass())
+      .RegisterPass(CreateScalarReplacementPass(0))
       .RegisterPass(CreateLocalMultiStoreElimPass())
-      .RegisterPass(CreateAggressiveDCEPass())
       .RegisterPass(CreateCCPPass())
-      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateLoopUnrollPass(true))
       .RegisterPass(CreateDeadBranchElimPass())
+      .RegisterPass(CreateSimplificationPass())
+      .RegisterPass(CreateScalarReplacementPass(0))
+      .RegisterPass(CreateLocalSingleStoreElimPass())
       .RegisterPass(CreateIfConversionPass())
+      .RegisterPass(CreateSimplificationPass())
       .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateBlockMergePass())
-      .RegisterPass(CreateSimplificationPass())
+      .RegisterPass(CreateLocalAccessChainConvertPass())
+      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
+      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateCopyPropagateArraysPass())
+      .RegisterPass(CreateVectorDCEPass())
       .RegisterPass(CreateDeadInsertElimPass())
+      .RegisterPass(CreateEliminateDeadMembersPass())
+      .RegisterPass(CreateLocalSingleStoreElimPass())
+      .RegisterPass(CreateBlockMergePass())
+      .RegisterPass(CreateLocalMultiStoreElimPass())
       .RegisterPass(CreateRedundancyEliminationPass())
-      .RegisterPass(CreateCFGCleanupPass())
-      .RegisterPass(CreateAggressiveDCEPass());
+      .RegisterPass(CreateSimplificationPass())
+      .RegisterPass(CreateAggressiveDCEPass())
+      .RegisterPass(CreateCFGCleanupPass());
 }
 
 Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {

+ 10 - 3
3rdparty/spirv-tools/source/opt/register_pressure.cpp

@@ -78,9 +78,16 @@ class ComputeRegisterLiveness {
   //   - Second, walk loop forest to propagate registers crossing back-edges
   //   (add iterative values into the liveness set).
   void Compute() {
-    cfg_.ForEachBlockInPostOrder(&*function_->begin(), [this](BasicBlock* bb) {
-      ComputePartialLiveness(bb);
-    });
+    for (BasicBlock& start_bb : *function_) {
+      if (reg_pressure_->Get(start_bb.id()) != nullptr) {
+        continue;
+      }
+      cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) {
+        if (reg_pressure_->Get(bb->id()) == nullptr) {
+          ComputePartialLiveness(bb);
+        }
+      });
+    }
     DoLoopLivenessUnification();
     EvaluateRegisterRequirements();
   }

+ 2 - 1
3rdparty/spirv-tools/source/print.cpp

@@ -15,7 +15,8 @@
 #include "source/print.h"
 
 #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
-    defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || defined(SPIRV_EMSCRIPTEN)
+    defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) ||                         \
+    defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA)
 namespace spvtools {
 
 clr::reset::operator const char*() { return "\x1b[0m"; }

+ 7 - 1
3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp

@@ -23,7 +23,8 @@ spv_fuzzer_options_t::spv_fuzzer_options_t()
     : has_random_seed(false),
       random_seed(0),
       replay_validation_enabled(false),
-      shrinker_step_limit(kDefaultStepLimit) {}
+      shrinker_step_limit(kDefaultStepLimit),
+      fuzzer_pass_validation_enabled(false) {}
 
 SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() {
   return new spv_fuzzer_options_t();
@@ -48,3 +49,8 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
     spv_fuzzer_options options, uint32_t shrinker_step_limit) {
   options->shrinker_step_limit = shrinker_step_limit;
 }
+
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
+    spv_fuzzer_options options) {
+  options->fuzzer_pass_validation_enabled = true;
+}

+ 3 - 0
3rdparty/spirv-tools/source/spirv_fuzzer_options.h

@@ -34,6 +34,9 @@ struct spv_fuzzer_options_t {
 
   // See spvFuzzerOptionsSetShrinkerStepLimit.
   uint32_t shrinker_step_limit;
+
+  // See spvFuzzerOptionsValidateAfterEveryPass.
+  bool fuzzer_pass_validation_enabled;
 };
 
 #endif  // SOURCE_SPIRV_FUZZER_OPTIONS_H_

+ 2 - 2
3rdparty/spirv-tools/source/val/basic_block.h

@@ -15,8 +15,8 @@
 #ifndef SOURCE_VAL_BASIC_BLOCK_H_
 #define SOURCE_VAL_BASIC_BLOCK_H_
 
-#include <cstdint>
 #include <bitset>
+#include <cstdint>
 #include <functional>
 #include <memory>
 #include <vector>
@@ -28,7 +28,7 @@ namespace val {
 
 enum BlockType : uint32_t {
   kBlockTypeUndefined,
-  kBlockTypeHeader,
+  kBlockTypeSelection,
   kBlockTypeLoop,
   kBlockTypeMerge,
   kBlockTypeBreak,

+ 3 - 4
3rdparty/spirv-tools/source/val/function.cpp

@@ -14,9 +14,8 @@
 
 #include "source/val/function.h"
 
-#include <cassert>
-
 #include <algorithm>
+#include <cassert>
 #include <sstream>
 #include <unordered_map>
 #include <unordered_set>
@@ -99,7 +98,7 @@ spv_result_t Function::RegisterLoopMerge(uint32_t merge_id,
 spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) {
   RegisterBlock(merge_id, false);
   BasicBlock& merge_block = blocks_.at(merge_id);
-  current_block_->set_type(kBlockTypeHeader);
+  current_block_->set_type(kBlockTypeSelection);
   merge_block.set_type(kBlockTypeMerge);
   merge_block_header_[&merge_block] = current_block_;
 
@@ -344,7 +343,7 @@ int Function::GetBlockDepth(BasicBlock* bb) {
     BasicBlock* header = merge_block_header_[bb];
     assert(header);
     block_depth_[bb] = GetBlockDepth(header);
-  } else if (bb_dom->is_type(kBlockTypeHeader) ||
+  } else if (bb_dom->is_type(kBlockTypeSelection) ||
              bb_dom->is_type(kBlockTypeLoop)) {
     // The dominator of the given block is a header block. So, the nesting
     // depth of this block is: 1 + nesting depth of the header.

+ 21 - 2
3rdparty/spirv-tools/source/val/validate_cfg.cpp

@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/val/validate.h"
-
 #include <algorithm>
 #include <cassert>
 #include <functional>
@@ -34,6 +32,7 @@
 #include "source/val/basic_block.h"
 #include "source/val/construct.h"
 #include "source/val/function.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -755,6 +754,26 @@ spv_result_t StructuredControlFlowChecks(
                  << header_name << " <ID> " << header->id();
         }
       }
+
+      if (block->is_type(BlockType::kBlockTypeSelection) ||
+          block->is_type(BlockType::kBlockTypeLoop)) {
+        size_t index = (block->terminator() - &_.ordered_instructions()[0]) - 1;
+        const auto& merge_inst = _.ordered_instructions()[index];
+        if (merge_inst.opcode() == SpvOpSelectionMerge ||
+            merge_inst.opcode() == SpvOpLoopMerge) {
+          uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0);
+          auto merge_block = function->GetBlock(merge_id).first;
+          if (merge_block->reachable() &&
+              !construct_blocks.count(merge_block)) {
+            return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
+                   << "Header block " << _.getIdName(block->id())
+                   << " is contained in the " << construct_name
+                   << " construct headed by " << _.getIdName(header->id())
+                   << ", but it's merge block " << _.getIdName(merge_id)
+                   << " is not";
+          }
+        }
+      }
     }
 
     // Checks rules for case constructs.

+ 0 - 7
3rdparty/spirv-tools/source/val/validate_debug.cpp

@@ -56,13 +56,6 @@ spv_result_t ValidateLine(ValidationState_t& _, const Instruction* inst) {
 }  // namespace
 
 spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst) {
-  if (spvIsWebGPUEnv(_.context()->target_env) &&
-      spvOpcodeIsDebug(inst->opcode())) {
-    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-           << "Debugging instructions are not allowed in the WebGPU execution "
-           << "environment.";
-  }
-
   switch (inst->opcode()) {
     case SpvOpMemberName:
       if (auto error = ValidateMemberName(_, inst)) return error;

+ 16 - 0
3rdparty/spirv-tools/test/fuzz/fuzz_test_util.cpp

@@ -14,6 +14,7 @@
 
 #include "test/fuzz/fuzz_test_util.h"
 
+#include <fstream>
 #include <iostream>
 
 #include "tools/io.h"
@@ -105,5 +106,20 @@ void DumpShader(const std::vector<uint32_t>& binary, const char* filename) {
   }
 }
 
+void DumpTransformationsJson(
+    const protobufs::TransformationSequence& transformations,
+    const char* filename) {
+  std::string json_string;
+  auto json_options = google::protobuf::util::JsonOptions();
+  json_options.add_whitespace = true;
+  auto json_generation_status = google::protobuf::util::MessageToJsonString(
+      transformations, &json_string, json_options);
+  if (json_generation_status == google::protobuf::util::Status::OK) {
+    std::ofstream transformations_json_file(filename);
+    transformations_json_file << json_string;
+    transformations_json_file.close();
+  }
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

+ 7 - 0
3rdparty/spirv-tools/test/fuzz/fuzz_test_util.h

@@ -19,6 +19,7 @@
 
 #include <vector>
 
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
 #include "spirv-tools/libspirv.h"
@@ -100,6 +101,12 @@ void DumpShader(opt::IRContext* context, const char* filename);
 // Dumps |binary| to file |filename|. Useful for interactive debugging.
 void DumpShader(const std::vector<uint32_t>& binary, const char* filename);
 
+// Dumps |transformations| to file |filename| in JSON format. Useful for
+// interactive debugging.
+void DumpTransformationsJson(
+    const protobufs::TransformationSequence& transformations,
+    const char* filename);
+
 }  // namespace fuzz
 }  // namespace spvtools
 

+ 2 - 4
3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp

@@ -62,13 +62,11 @@ void RunFuzzerAndReplayer(const std::string& shader,
   for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
     std::vector<uint32_t> fuzzer_binary_out;
     protobufs::TransformationSequence fuzzer_transformation_sequence_out;
-    spvtools::FuzzerOptions fuzzer_options;
-    spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
 
-    Fuzzer fuzzer(env);
+    Fuzzer fuzzer(env, seed, true);
     fuzzer.SetMessageConsumer(kSilentConsumer);
     auto fuzzer_result_status =
-        fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out,
+        fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
                    &fuzzer_transformation_sequence_out);
     ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
     ASSERT_TRUE(t.Validate(fuzzer_binary_out));

+ 2 - 4
3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp

@@ -165,12 +165,10 @@ void RunFuzzerAndShrinker(const std::string& shader,
   // Run the fuzzer and check that it successfully yields a valid binary.
   std::vector<uint32_t> fuzzer_binary_out;
   protobufs::TransformationSequence fuzzer_transformation_sequence_out;
-  spvtools::FuzzerOptions fuzzer_options;
-  spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
-  Fuzzer fuzzer(env);
+  Fuzzer fuzzer(env, seed, true);
   fuzzer.SetMessageConsumer(kSilentConsumer);
   auto fuzzer_result_status =
-      fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out,
+      fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
                  &fuzzer_transformation_sequence_out);
   ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
   ASSERT_TRUE(t.Validate(fuzzer_binary_out));

+ 0 - 112
3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp

@@ -209,117 +209,6 @@ TEST(TransformationAddDeadContinueTest, SimpleExample) {
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 
-TEST(TransformationAddDeadContinueTest,
-     DoNotAllowContinueToMergeBlockOfAnotherLoop) {
-  // A loop header must dominate its merge block if that merge block is
-  // reachable. We are thus not allowed to add a dead continue that would result
-  // in violation of this property. This test checks for such a scenario.
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %16 %139
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %8 = OpTypeBool
-         %14 = OpTypeVector %6 4
-         %15 = OpTypePointer Input %14
-         %16 = OpVariable %15 Input
-        %138 = OpTypePointer Output %14
-        %139 = OpVariable %138 Output
-        %400 = OpConstantTrue %8
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpBranch %500
-        %500 = OpLabel
-               OpLoopMerge %501 %502 None
-               OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502
-        %503 = OpLabel
-               OpLoopMerge %502 %504 None
-               OpBranchConditional %400 %505 %504
-        %505 = OpLabel
-               OpBranch %502
-        %504 = OpLabel
-               OpBranch %503
-        %502 = OpLabel
-               OpBranchConditional %400 %501 %500
-        %501 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-  FactManager fact_manager;
-
-  ASSERT_FALSE(TransformationAddDeadContinue(500, true, {})
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationAddDeadContinue(500, false, {})
-                   .IsApplicable(context.get(), fact_manager));
-}
-
-TEST(TransformationAddDeadContinueTest, DoNotAllowContinueToSelectionMerge) {
-  // A selection header must dominate its merge block if that merge block is
-  // reachable. We are thus not allowed to add a dead continue that would result
-  // in violation of this property. This test checks for such a scenario.
-
-  std::string shader = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %16 %139
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %8 = OpTypeBool
-         %14 = OpTypeVector %6 4
-         %15 = OpTypePointer Input %14
-         %16 = OpVariable %15 Input
-        %138 = OpTypePointer Output %14
-        %139 = OpVariable %138 Output
-        %400 = OpConstantTrue %8
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpBranch %500
-        %500 = OpLabel
-               OpLoopMerge %501 %502 None
-               OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502
-        %503 = OpLabel
-               OpSelectionMerge %502 None
-               OpBranchConditional %400 %505 %504
-        %505 = OpLabel
-               OpBranch %502
-        %504 = OpLabel
-               OpBranch %502
-        %502 = OpLabel
-               OpBranchConditional %400 %501 %500
-        %501 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-  ASSERT_TRUE(IsValid(env, context.get()));
-  FactManager fact_manager;
-
-  ASSERT_FALSE(TransformationAddDeadContinue(500, true, {})
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationAddDeadContinue(500, false, {})
-                   .IsApplicable(context.get(), fact_manager));
-}
-
 TEST(TransformationAddDeadContinueTest, LoopNest) {
   // Checks some allowed and disallowed scenarios for a nest of loops, including
   // continuing a loop from an if or switch.
@@ -1420,7 +1309,6 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous2) {
                OpLoopMerge %1557 %1570 None
                OpBranchConditional %395 %1562 %1557
        %1562 = OpLabel
-               OpSelectionMerge %1570 None
                OpBranchConditional %395 %1571 %1570
        %1571 = OpLabel
                OpBranch %1557

+ 1 - 1
3rdparty/spirv-tools/test/fuzz/transformation_copy_object_test.cpp

@@ -291,7 +291,7 @@ TEST(TransformationCopyObjectTest, CheckIllegalCases) {
          %31 = OpLabel
          %42 = OpAccessChain %36 %18 %41
          %43 = OpLoad %11 %42
-               OpSelectionMerge %47 None
+               OpSelectionMerge %45 None
                OpSwitch %43 %46 0 %44 1 %45
          %46 = OpLabel
          %69 = OpIAdd %11 %96 %27

+ 12 - 8
3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp

@@ -159,18 +159,20 @@ const std::string kComplexShader = R"(
          %65 = OpAccessChain %13 %11 %64
          %66 = OpLoad %6 %65
          %67 = OpSGreaterThan %29 %84 %66
-               OpSelectionMerge %69 None
+               OpSelectionMerge %1000 None
                OpBranchConditional %67 %68 %72
          %68 = OpLabel
          %71 = OpIAdd %6 %84 %26
-               OpBranch %69
+               OpBranch %1000
          %72 = OpLabel
          %74 = OpIAdd %6 %84 %64
         %205 = OpCopyObject %6 %74
-               OpBranch %69
-         %69 = OpLabel
+               OpBranch %1000
+       %1000 = OpLabel
          %86 = OpPhi %6 %71 %68 %74 %72
         %301 = OpPhi %6 %71 %68 %15 %72
+               OpBranch %69
+         %69 = OpLabel
                OpBranch %20
          %22 = OpLabel
          %75 = OpAccessChain %46 %42 %50
@@ -421,18 +423,20 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) {
          %65 = OpAccessChain %13 %11 %64
          %66 = OpLoad %6 %65
          %67 = OpSGreaterThan %29 %84 %66
-               OpSelectionMerge %69 None
+               OpSelectionMerge %1000 None
                OpBranchConditional %67 %68 %72
          %68 = OpLabel
          %71 = OpIAdd %6 %84 %26
-               OpBranch %69
+               OpBranch %1000
          %72 = OpLabel
          %74 = OpIAdd %6 %84 %64
         %205 = OpCopyObject %6 %74
-               OpBranch %69
-         %69 = OpLabel
+               OpBranch %1000
+       %1000 = OpLabel
          %86 = OpPhi %6 %71 %68 %205 %72
         %301 = OpPhi %6 %71 %68 %15 %72
+               OpBranch %69
+         %69 = OpLabel
                OpBranch %20
          %22 = OpLabel
          %75 = OpAccessChain %46 %42 %50

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

@@ -5932,7 +5932,6 @@ OpBranch %42
 %42 = OpLabel
 %43 = OpLoad %int %i
 %44 = OpSLessThan %bool %43 %int_1
-OpSelectionMerge %45 None
 OpBranchConditional %44 %46 %40
 %46 = OpLabel
 %47 = OpLoad %int %i

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

@@ -447,10 +447,53 @@ OpBranch %header
 OpLoopMerge %merge %continue None
 OpBranch %inner_header
 %inner_header = OpLabel
-OpSelectionMerge %continue None
-OpBranchConditional %true %then %continue
+OpSelectionMerge %if_merge None
+OpBranchConditional %true %then %if_merge
 %then = OpLabel
 OpBranch %continue
+%if_merge = OpLabel
+OpBranch %continue
+%continue = OpLabel
+OpBranchConditional %false %merge %header
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<BlockMergePass>(text, true);
+}
+
+TEST_F(BlockMergeTest, CannotMergeContinue) {
+  const std::string text = R"(
+; CHECK: OpBranch [[loop_header:%\w+]]
+; CHECK: [[loop_header]] = OpLabel
+; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue:%\w+]]
+; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_header:%\w+]]
+; CHECK: [[if_header]] = OpLabel
+; CHECK-NEXT: OpSelectionMerge
+; CHECK: [[continue]] = OpLabel
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %func "func"
+OpExecutionMode %func OriginUpperLeft
+%void = OpTypeVoid
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%false = OpConstantFalse  %bool
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%entry = OpLabel
+OpBranch %header
+%header = OpLabel
+OpLoopMerge %merge %continue None
+OpBranchConditional %true %inner_header %merge
+%inner_header = OpLabel
+OpSelectionMerge %if_merge None
+OpBranchConditional %true %then %if_merge
+%then = OpLabel
+OpBranch %continue
+%if_merge = OpLabel
+OpBranch %continue
 %continue = OpLabel
 OpBranchConditional %false %merge %header
 %merge = OpLabel

+ 6 - 2
3rdparty/spirv-tools/test/opt/dead_branch_elim_test.cpp

@@ -2798,7 +2798,9 @@ OpReturn
 OpFunctionEnd
 )";
 
-  SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
+  // The selection merge in the loop naming the continue target as merge is
+  // invalid, but handled by this pass so validation is disabled.
+  SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, false);
 }
 
 TEST_F(DeadBranchElimTest, SelectionMergeWithNestedLoop) {
@@ -2942,7 +2944,9 @@ OpReturn
 OpFunctionEnd
 )";
 
-  SinglePassRunAndMatch<DeadBranchElimPass>(body, true);
+  // The selection merge in the loop naming the continue target as merge is
+  // invalid, but handled by this pass so validation is disabled.
+  SinglePassRunAndMatch<DeadBranchElimPass>(body, false);
 }
 
 TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) {

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

@@ -1819,7 +1819,7 @@ OpFunctionEnd
 %9 = OpLabel
 OpBranch %10
 %10 = OpLabel
-OpLoopMerge %12 %13 None
+OpLoopMerge %12 %10 None
 OpBranch %13
 %13 = OpLabel
 OpBranchConditional %true %10 %12
@@ -1980,7 +1980,7 @@ OpFunctionEnd
 OpBranch %13
 %13 = OpLabel
 %14 = OpCopyObject %bool %false
-OpLoopMerge %16 %19 None
+OpLoopMerge %16 %13 None
 OpBranch %17
 %17 = OpLabel
 %18 = OpCopyObject %bool %true
@@ -2145,7 +2145,7 @@ OpBranch %19
 %19 = OpLabel
 %20 = OpCopyObject %int %int_2
 %25 = OpCopyObject %int %int_0
-OpLoopMerge %23 %26 None
+OpLoopMerge %23 %19 None
 OpBranch %26
 %27 = OpLabel
 %28 = OpCopyObject %int %int_1

+ 23 - 47
3rdparty/spirv-tools/test/opt/local_ssa_elim_test.cpp

@@ -193,7 +193,24 @@ OpDecorate %fo Location 0
 )";
 
   const std::string before =
-      R"(%main = OpFunction %void None %9
+      R"(
+; CHECK: = OpFunction
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK: [[outer_header:%\w+]] = OpLabel
+; CHECK-NEXT: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]]
+; CHECK-NEXT: [[i:%\w+]] = OpPhi %int %int_0 [[entry]] [[i_next:%\w+]] [[outer_be]]
+; CHECK-NEXT: OpSLessThan {{%\w+}} [[i]]
+; CHECK: [[inner_pre_header:%\w+]] = OpLabel
+; CHECK: [[inner_header:%\w+]] = OpLabel
+; CHECK-NEXT: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]]
+; CHECK-NEXT: [[j:%\w+]] = OpPhi %int %int_0 [[inner_pre_header]] [[j_next:%\w+]] [[inner_be]]
+; CHECK: [[inner_be]] = OpLabel
+; CHECK: [[f_next]] = OpFAdd %float [[inner_f]]
+; CHECK: [[j_next]] = OpIAdd %int [[j]] %int_1
+; CHECK: [[outer_be]] = OpLabel
+; CHECK: [[i_next]] = OpIAdd
+; CHECK: OpStore %fo [[outer_f]]
+%main = OpFunction %void None %9
 %24 = OpLabel
 %f = OpVariable %_ptr_Function_float Function
 %i = OpVariable %_ptr_Function_int Function
@@ -212,8 +229,8 @@ OpBranch %31
 %31 = OpLabel
 %32 = OpLoad %int %j
 %33 = OpSLessThan %bool %32 %int_4
-OpLoopMerge %29 %34 None
-OpBranchConditional %33 %34 %29
+OpLoopMerge %50 %34 None
+OpBranchConditional %33 %34 %50
 %34 = OpLabel
 %35 = OpLoad %float %f
 %36 = OpLoad %int %i
@@ -226,6 +243,8 @@ OpStore %f %40
 %42 = OpIAdd %int %41 %int_1
 OpStore %j %42
 OpBranch %31
+%50 = OpLabel
+OpBranch %29
 %29 = OpLabel
 %43 = OpLoad %int %i
 %44 = OpIAdd %int %43 %int_1
@@ -238,50 +257,7 @@ OpReturn
 OpFunctionEnd
 )";
 
-  const std::string after =
-      R"(%main = OpFunction %void None %9
-%24 = OpLabel
-%f = OpVariable %_ptr_Function_float Function
-%i = OpVariable %_ptr_Function_int Function
-%j = OpVariable %_ptr_Function_int Function
-OpStore %f %float_0
-OpStore %i %int_0
-OpBranch %25
-%25 = OpLabel
-%47 = OpPhi %float %float_0 %24 %50 %29
-%46 = OpPhi %int %int_0 %24 %44 %29
-%27 = OpSLessThan %bool %46 %int_4
-OpLoopMerge %28 %29 None
-OpBranchConditional %27 %30 %28
-%30 = OpLabel
-OpStore %j %int_0
-OpBranch %31
-%31 = OpLabel
-%50 = OpPhi %float %47 %30 %40 %34
-%48 = OpPhi %int %int_0 %30 %42 %34
-%33 = OpSLessThan %bool %48 %int_4
-OpLoopMerge %29 %34 None
-OpBranchConditional %33 %34 %29
-%34 = OpLabel
-%38 = OpAccessChain %_ptr_Input_float %BC %46 %48
-%39 = OpLoad %float %38
-%40 = OpFAdd %float %50 %39
-OpStore %f %40
-%42 = OpIAdd %int %48 %int_1
-OpStore %j %42
-OpBranch %31
-%29 = OpLabel
-%44 = OpIAdd %int %46 %int_1
-OpStore %i %44
-OpBranch %25
-%28 = OpLabel
-OpStore %fo %47
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
-                                        true);
+  SinglePassRunAndMatch<SSARewritePass>(predefs + before, true);
 }
 
 TEST_F(LocalSSAElimTest, ForLoopWithContinue) {

+ 4 - 2
3rdparty/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp

@@ -3177,7 +3177,7 @@ TEST_F(FusionLegalTest, OuterloopWithBreakContinueInInner) {
          %21 = OpLabel
          %29 = OpSMod %6 %96 %28
          %30 = OpIEqual %17 %29 %9
-               OpSelectionMerge %23 None
+               OpSelectionMerge %sel_merge None
                OpBranchConditional %30 %31 %48
          %31 = OpLabel
          %44 = OpAccessChain %7 %41 %91 %96
@@ -3187,8 +3187,10 @@ TEST_F(FusionLegalTest, OuterloopWithBreakContinueInInner) {
                OpStore %47 %46
                OpBranch %32
          %48 = OpLabel
-               OpBranch %23
+               OpBranch %sel_merge
          %32 = OpLabel
+               OpBranch %sel_merge
+  %sel_merge = OpLabel
                OpBranch %23
          %23 = OpLabel
          %52 = OpIAdd %6 %96 %51

+ 4 - 5
3rdparty/spirv-tools/test/opt/pass_merge_return_test.cpp

@@ -1390,7 +1390,6 @@ TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) {
                OpLoopMerge %36 %40 None
                OpBranch %35
          %35 = OpLabel
-               OpSelectionMerge %40 None
                OpBranchConditional %77 %39 %40
          %39 = OpLabel
                OpReturnValue %16
@@ -1402,7 +1401,6 @@ TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) {
                OpLoopMerge %45 %49 None
                OpBranch %44
          %44 = OpLabel
-               OpSelectionMerge %49 None
                OpBranchConditional %77 %48 %49
          %48 = OpLabel
                OpReturnValue %16
@@ -1415,7 +1413,6 @@ TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) {
                OpLoopMerge %64 %68 None
                OpBranch %63
          %63 = OpLabel
-               OpSelectionMerge %68 None
                OpBranchConditional %77 %67 %68
          %67 = OpLabel
                OpReturnValue %16
@@ -1813,12 +1810,14 @@ TEST_F(MergeReturnPassTest, PhiInSecondMerge) {
                OpLoopMerge %11 %12 None
                OpBranch %13
          %13 = OpLabel
-               OpLoopMerge %12 %14 None
-               OpBranchConditional %8 %15 %12
+               OpLoopMerge %18 %14 None
+               OpBranchConditional %8 %15 %18
          %15 = OpLabel
                OpReturn
          %14 = OpLabel
                OpBranch %13
+         %18 = OpLabel
+               OpBranch %12
          %12 = OpLabel
          %16 = OpUndef %float
                OpBranchConditional %8 %10 %11

+ 40 - 0
3rdparty/spirv-tools/test/opt/register_liveness.cpp

@@ -1277,6 +1277,46 @@ TEST_F(PassClassTest, FissionSimulation) {
   }
 }
 
+// Test that register liveness does not fail when there is an unreachable block.
+// We are not testing if the liveness is computed correctly because the specific
+// results do not matter for unreachable blocks.
+TEST_F(PassClassTest, RegisterLivenessWithUnreachableBlock) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginLowerLeft
+               OpSource GLSL 330
+               OpSourceExtension "GL_ARB_shading_language_420pack"
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+          %2 = OpFunction %void None %4
+          %5 = OpLabel
+               OpBranch %6
+          %6 = OpLabel
+               OpLoopMerge %7 %8 None
+               OpBranch %9
+          %9 = OpLabel
+               OpBranch %7
+          %8 = OpLabel
+               OpBranch %6
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  Module* module = context->module();
+  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+                             << text << std::endl;
+  Function* f = &*module->begin();
+  LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis();
+  liveness_analysis->Get(f);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools

+ 47 - 0
3rdparty/spirv-tools/test/opt/strip_reflect_info_test.cpp

@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include <string>
+#include "gmock/gmock.h"
+
+#include "spirv-tools/optimizer.hpp"
 
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
@@ -23,6 +26,50 @@ namespace {
 
 using StripLineReflectInfoTest = PassTest<::testing::Test>;
 
+// This test acts as an end-to-end code example on how to strip
+// reflection info from a SPIR-V module.  Use this code pattern
+// when you have compiled HLSL code with Glslang or DXC using
+// option -fhlsl_functionality1 to insert reflection information,
+// but then want to filter out the extra instructions before sending
+// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1.
+TEST_F(StripLineReflectInfoTest, StripReflectEnd2EndExample) {
+  // This is a non-sensical example, but exercises the instructions.
+  std::string before = R"(OpCapability Shader
+OpCapability Linkage
+OpExtension "SPV_GOOGLE_decorate_string"
+OpExtension "SPV_GOOGLE_hlsl_functionality1"
+OpMemoryModel Logical Simple
+OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar"
+OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness"
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+)";
+  SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  std::vector<uint32_t> binary_in;
+  tools.Assemble(before, &binary_in);
+
+  // Instantiate the optimizer, and run the strip-reflection-info
+  // pass over the |binary_in| module, and place the modified module
+  // into |binary_out|.
+  spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1);
+  optimizer.RegisterPass(spvtools::CreateStripReflectInfoPass());
+  std::vector<uint32_t> binary_out;
+  optimizer.Run(binary_in.data(), binary_in.size(), &binary_out);
+
+  // Check results
+  std::string disassembly;
+  tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
+  std::string after = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical Simple
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+)";
+  EXPECT_THAT(disassembly, testing::Eq(after));
+}
+
+// This test is functionally the same as the end-to-end test above,
+// but uses the test SinglePassRunAndCheck test fixture instead.
 TEST_F(StripLineReflectInfoTest, StripHlslSemantic) {
   // This is a non-sensical example, but exercises the instructions.
   std::string before = R"(OpCapability Shader

+ 10 - 7
3rdparty/spirv-tools/test/reduce/reducer_test.cpp

@@ -125,19 +125,21 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
          %29 = OpAccessChain %28 %27 %9
          %30 = OpLoad %24 %29
          %32 = OpFOrdGreaterThan %22 %30 %31
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %32 %33 %46
          %33 = OpLabel
          %40 = OpFAdd %24 %71 %30
          %45 = OpISub %6 %73 %21
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
          %50 = OpFMul %24 %71 %30
          %54 = OpSDiv %6 %73 %21
-               OpBranch %34
-         %34 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
          %77 = OpPhi %6 %45 %33 %54 %46
          %76 = OpPhi %24 %40 %33 %50 %46
+               OpBranch %34
+         %34 = OpLabel
          %57 = OpIAdd %6 %70 %56
                OpBranch %10
          %12 = OpLabel
@@ -193,11 +195,13 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
                OpLoopMerge %12 %34 None
                OpBranchConditional %100 %11 %12
          %11 = OpLabel
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %100 %33 %46
          %33 = OpLabel
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
                OpBranch %34
          %34 = OpLabel
                OpBranch %10
@@ -345,7 +349,6 @@ const std::string kShaderWithLoopsDivAndMul = R"(
                OpLoopMerge %33 %38 None
                OpBranch %32
          %32 = OpLabel
-               OpSelectionMerge %38 None
                OpBranchConditional %30 %37 %38
          %37 = OpLabel
                OpSelectionMerge %42 None

+ 19 - 14
3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp

@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "source/reduce/reducer.h"
-
 #include "source/reduce/reduction_opportunity.h"
 #include "source/reduce/remove_instruction_reduction_opportunity.h"
 #include "test/reduce/reduce_test_util.h"
@@ -23,8 +22,8 @@ namespace reduce {
 namespace {
 
 using opt::Function;
-using opt::IRContext;
 using opt::Instruction;
+using opt::IRContext;
 
 // A dumb reduction opportunity finder that finds opportunities to remove global
 // values regardless of whether they are referenced. This is very likely to make
@@ -181,19 +180,21 @@ TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) {
          %29 = OpAccessChain %28 %27 %9
          %30 = OpLoad %24 %29
          %32 = OpFOrdGreaterThan %22 %30 %31
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %32 %33 %46
          %33 = OpLabel
          %40 = OpFAdd %24 %71 %30
          %45 = OpISub %6 %73 %21
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
          %50 = OpFMul %24 %71 %30
          %54 = OpSDiv %6 %73 %21
-               OpBranch %34
-         %34 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
          %77 = OpPhi %6 %45 %33 %54 %46
          %76 = OpPhi %24 %40 %33 %50 %46
+               OpBranch %34
+         %34 = OpLabel
          %57 = OpIAdd %6 %70 %56
                OpBranch %10
          %12 = OpLabel
@@ -303,19 +304,21 @@ TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) {
          %29 = OpAccessChain %28 %27 %9
          %30 = OpLoad %24 %29
          %32 = OpFOrdGreaterThan %22 %30 %31
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %32 %33 %46
          %33 = OpLabel
          %40 = OpFAdd %24 %71 %30
          %45 = OpISub %6 %73 %21
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
          %50 = OpFMul %24 %71 %30
          %54 = OpSDiv %6 %73 %21
-               OpBranch %34
-         %34 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
          %77 = OpPhi %6 %45 %33 %54 %46
          %76 = OpPhi %24 %40 %33 %50 %46
+               OpBranch %34
+         %34 = OpLabel
          %57 = OpIAdd %6 %70 %56
                OpBranch %10
          %12 = OpLabel
@@ -392,19 +395,21 @@ TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) {
          %29 = OpAccessChain %28 %27 %9
          %30 = OpLoad %24 %29
          %32 = OpFOrdGreaterThan %22 %30 %31
-               OpSelectionMerge %34 None
+               OpSelectionMerge %90 None
                OpBranchConditional %32 %33 %46
          %33 = OpLabel
          %40 = OpFAdd %24 %71 %30
          %45 = OpISub %6 %73 %21
-               OpBranch %34
+               OpBranch %90
          %46 = OpLabel
          %50 = OpFMul %24 %71 %30
          %54 = OpSDiv %6 %73 %21
-               OpBranch %34
-         %34 = OpLabel
+               OpBranch %90
+         %90 = OpLabel
          %77 = OpPhi %6 %45 %33 %54 %46
          %76 = OpPhi %24 %40 %33 %50 %46
+               OpBranch %34
+         %34 = OpLabel
          %57 = OpIAdd %6 %70 %56
                OpBranch %10
          %12 = OpLabel

+ 19 - 12
3rdparty/spirv-tools/test/tools/opt/flags.py

@@ -187,28 +187,35 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_5,
       'eliminate-dead-branches',
       'merge-return',
       'inline-entry-points-exhaustive',
-      'eliminate-dead-code-aggressive',
+      'eliminate-dead-functions',
       'private-to-local',
-      'scalar-replacement=100',
-      'convert-local-access-chains',
-      'eliminate-local-single-block',
-      'eliminate-local-single-store',
-      'eliminate-dead-code-aggressive',
-      'simplify-instructions',
-      'eliminate-dead-inserts',
+      'scalar-replacement=0',
       'ssa-rewrite',
-      'eliminate-dead-code-aggressive',
       'ccp',
-      'eliminate-dead-code-aggressive',
+      'loop-unroll',
       'eliminate-dead-branches',
+      'simplify-instructions',
+      'scalar-replacement=0',
+      'eliminate-local-single-store',
       'if-conversion',
+      'simplify-instructions',
       'eliminate-dead-code-aggressive',
+      'eliminate-dead-branches',
       'merge-blocks',
-      'simplify-instructions',
+      'convert-local-access-chains',
+      'eliminate-local-single-block',
+      'eliminate-dead-code-aggressive',
+      'copy-propagate-arrays',
+      'vector-dce',
       'eliminate-dead-inserts',
+      'eliminate-dead-members',
+      'eliminate-local-single-store',
+      'merge-blocks',
+      'ssa-rewrite',
       'redundancy-elimination',
-      'cfg-cleanup',
+      'simplify-instructions',
       'eliminate-dead-code-aggressive',
+      'cfg-cleanup',
   ]
   shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
   output = placeholder.TempFileName('output.spv')

+ 115 - 28
3rdparty/spirv-tools/test/val/val_cfg_test.cpp

@@ -23,7 +23,6 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-
 #include "source/diagnostic.h"
 #include "source/spirv_target_env.h"
 #include "source/val/validate.h"
@@ -1355,7 +1354,7 @@ std::string GetReachableMergeAndContinue(SpvCapability cap,
   }
   if (cap == SpvCapabilityShader) {
     branch.AppendBody("OpLoopMerge %merge %target None\n");
-    body.AppendBody("OpSelectionMerge %target None\n");
+    body.AppendBody("OpSelectionMerge %f None\n");
   }
 
   if (!spvIsWebGPUEnv(env))
@@ -1650,25 +1649,27 @@ TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) {
   Block entry("entry");
   Block loop1("loop1", SpvOpBranchConditional);
   Block loop2("loop2", SpvOpBranchConditional);
-  Block loop2_merge("loop2_merge", SpvOpBranchConditional);
+  Block loop2_merge("loop2_merge");
+  Block loop1_cont("loop1_cont", SpvOpBranchConditional);
   Block be_block("be_block");
   Block exit("exit", SpvOpReturn);
 
   entry.SetBody("%cond    = OpSLessThan %boolt %one %two\n");
   if (is_shader) {
-    loop1.SetBody("OpLoopMerge %exit %loop2_merge None\n");
+    loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n");
     loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
   }
 
-  std::string str = GetDefaultHeader(GetParam()) +
-                    nameOps("loop1", "loop2", "be_block", "loop2_merge") +
-                    types_consts() +
-                    "%func    = OpFunction %voidt None %funct\n";
+  std::string str =
+      GetDefaultHeader(GetParam()) +
+      nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") +
+      types_consts() + "%func    = OpFunction %voidt None %funct\n";
 
   str += entry >> loop1;
   str += loop1 >> std::vector<Block>({loop2, exit});
   str += loop2 >> std::vector<Block>({loop2, loop2_merge});
-  str += loop2_merge >> std::vector<Block>({be_block, exit});
+  str += loop2_merge >> loop1_cont;
+  str += loop1_cont >> std::vector<Block>({be_block, exit});
   str += be_block >> loop1;
   str += exit;
   str += "OpFunctionEnd";
@@ -1678,7 +1679,7 @@ TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) {
     ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
     EXPECT_THAT(getDiagnosticString(),
                 MatchesRegex("The continue construct with the continue target "
-                             ".\\[%loop2_merge\\] is not post dominated by the "
+                             ".\\[%loop1_cont\\] is not post dominated by the "
                              "back-edge block .\\[%be_block\\]\n"
                              "  %be_block = OpLabel\n"));
   } else {
@@ -2037,13 +2038,10 @@ TEST_P(ValidateCFG,
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
 }
 
-TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) {
-  // This example is valid.  It shows that the validator can't just add
-  // an edge from the loop head to the continue target.  If that edge
-  // is added, then the "if_merge" block is both the continue target
-  // for the loop and also the merge block for the nested selection, but
-  // then it wouldn't be dominated by "if_head", the header block for the
-  // nested selection.
+TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) {
+  // The continue construct cannot be the merge target of a nested selection
+  // because the loop construct must contain "if_merge" because it contains
+  // "if_head".
   bool is_shader = GetParam() == SpvCapabilityShader;
   Block entry("entry");
   Block loop("loop");
@@ -2072,7 +2070,16 @@ TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) {
   str += "OpFunctionEnd";
 
   CompileSuccessfully(str);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
+  if (is_shader) {
+    EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+    EXPECT_THAT(
+        getDiagnosticString(),
+        HasSubstr("Header block 3[%if_head] is contained in the loop construct "
+                  "headed "
+                  "by 2[%loop], but it's merge block 5[%if_merge] is not"));
+  } else {
+    EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
+  }
 }
 
 TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) {
@@ -3681,17 +3688,21 @@ OpBranch %7
 OpLoopMerge %8 %9 None
 OpBranch %10
 %10 = OpLabel
-OpLoopMerge %9 %11 None
-OpBranch %12
-%12 = OpLabel
-OpSelectionMerge %11 None
-OpBranchConditional %3 %11 %13
+OpLoopMerge %11 %12 None
+OpBranch %13
 %13 = OpLabel
+OpSelectionMerge %14 None
+OpBranchConditional %3 %14 %15
+%15 = OpLabel
 OpBranch %8
+%14 = OpLabel
+OpBranch %12
+%12 = OpLabel
+OpBranchConditional %3 %10 %11
 %11 = OpLabel
-OpBranchConditional %3 %9 %10
+OpBranch %9
 %9 = OpLabel
-OpBranchConditional %3 %8 %7
+OpBranchConditional %3 %7 %8
 %8 = OpLabel
 OpReturn
 OpFunctionEnd
@@ -3700,7 +3711,7 @@ OpFunctionEnd
   CompileSuccessfully(text);
   EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("block <ID> 13[%13] exits the loop headed by <ID> "
+              HasSubstr("block <ID> 15[%15] exits the loop headed by <ID> "
                         "10[%10], but not via a structured exit"));
 }
 
@@ -3726,9 +3737,11 @@ OpBranchConditional %undef %11 %12
 OpSelectionMerge %31 None
 OpBranchConditional %undef %30 %31
 %30 = OpLabel
-OpSelectionMerge %37 None
-OpBranchConditional %undef %36 %37
+OpSelectionMerge %38 None
+OpBranchConditional %undef %36 %38
 %36 = OpLabel
+OpBranch %38
+%38 = OpLabel
 OpBranch %37
 %37 = OpLabel
 OpBranch %10
@@ -4100,6 +4113,80 @@ OpFunctionEnd
   EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
 }
 
+TEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpName %loop "loop"
+OpName %continue "continue"
+OpName %body "body"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %exit %continue None
+OpBranch %body
+%body = OpLabel
+OpSelectionMerge %continue None
+OpBranchConditional %undef %exit %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Header block 3[%body] is contained in the loop construct headed by "
+          "1[%loop], but it's merge block 2[%continue] is not"));
+}
+
+TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpName %loop "loop"
+OpName %continue "continue"
+OpName %inner "inner"
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %exit %continue None
+OpBranchConditional %undef %exit %inner
+%inner = OpLabel
+OpLoopMerge %continue %inner None
+OpBranchConditional %undef %inner %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Header block 3[%inner] is contained in the loop construct headed by "
+          "1[%loop], but it's merge block 2[%continue] is not"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools

+ 128 - 76
3rdparty/spirv-tools/test/val/val_webgpu_test.cpp

@@ -54,117 +54,170 @@ TEST_F(ValidateWebGPU, OpUndefIsDisallowed) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed"));
 }
 
-TEST_F(ValidateWebGPU, OpNameIsDisallowed) {
+TEST_F(ValidateWebGPU, OpNameIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpName %1 "foo"
-%1 = OpTypeFloat 32
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpName %1 "foo"
+       %1 = OpTypeFloat 32
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpName %foo \"foo\"\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-TEST_F(ValidateWebGPU, OpMemberNameIsDisallowed) {
+TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpMemberName %2 0 "foo"
-%1 = OpTypeFloat 32
-%2 = OpTypeStruct %1
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpMemberName %2 0 "foo"
+       %1 = OpTypeFloat 32
+       %2 = OpTypeStruct %1
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
+
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpMemberName %_struct_1 0 "
-                        "\"foo\"\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-TEST_F(ValidateWebGPU, OpSourceIsDisallowed) {
+TEST_F(ValidateWebGPU, OpSourceIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpSource GLSL 450
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpSource GLSL 450
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpSource GLSL 450\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-// OpSourceContinued does not have a test case, because it requires being
-// preceded by OpSource, which will cause a validation error.
-
-TEST_F(ValidateWebGPU, OpSourceExtensionIsDisallowed) {
+TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpSourceExtension "bar"
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpSource GLSL 450
+            OpSourceContinued "I am a happy shader! Yay! ;"
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpSourceExtension "
-                        "\"bar\"\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-TEST_F(ValidateWebGPU, OpStringIsDisallowed) {
+TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-%1 = OpString "foo"
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpSourceExtension "bar"
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  %1 = OpString \"foo\"\n"));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
-// OpLine does not have a test case, because it requires being preceded by
-// OpString, which will cause a validation error.
+TEST_F(ValidateWebGPU, OpStringIsAllowed) {
+  std::string spirv = R"(
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+       %1 = OpString "foo"
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
 
-TEST_F(ValidateWebGPU, OpNoLineDisallowed) {
+TEST_F(ValidateWebGPU, OpLineIsAllowed) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
-     OpNoLine
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+       %1 = OpString "minimal.vert"
+            OpLine %1 1 1
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
 
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Debugging instructions are not allowed in the WebGPU "
-                        "execution environment.\n  OpNoLine\n"));
+TEST_F(ValidateWebGPU, OpNoLineIsAllowed) {
+  std::string spirv = R"(
+            OpCapability Shader
+            OpCapability VulkanMemoryModelKHR
+            OpExtension "SPV_KHR_vulkan_memory_model"
+            OpMemoryModel Logical VulkanKHR
+            OpEntryPoint Vertex %func "shader"
+            OpNoLine
+  %void   = OpTypeVoid
+  %void_f = OpTypeFunction %void
+  %func   = OpFunction %void None %void_f
+  %label  = OpLabel
+            OpReturn
+            OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }
 
 TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
@@ -183,7 +236,6 @@ TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
 )";
 
   CompileSuccessfully(spirv);
-
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
 }