Quellcode durchsuchen

Updated spirv-tools.

Бранимир Караџић vor 6 Jahren
Ursprung
Commit
b17a9c55d0
47 geänderte Dateien mit 1305 neuen und 635 gelöschten Zeilen
  1. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  2. 13 3
      3rdparty/spirv-tools/source/CMakeLists.txt
  3. 4 3
      3rdparty/spirv-tools/source/binary.cpp
  4. 8 3
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  5. 33 31
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
  6. 10 11
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
  7. 10 10
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp
  8. 12 7
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp
  9. 30 21
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp
  10. 4 29
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
  11. 3 8
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
  12. 12 23
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  13. 16 26
      3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.cpp
  14. 4 4
      3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h
  15. 19 31
      3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp
  16. 4 2
      3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h
  17. 41 49
      3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp
  18. 3 2
      3rdparty/spirv-tools/source/fuzz/transformation_split_block.h
  19. 8 3
      3rdparty/spirv-tools/source/link/CMakeLists.txt
  20. 8 3
      3rdparty/spirv-tools/source/opt/CMakeLists.txt
  21. 6 0
      3rdparty/spirv-tools/source/opt/fold.cpp
  22. 59 6
      3rdparty/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp
  23. 0 15
      3rdparty/spirv-tools/source/opt/folding_rules.cpp
  24. 5 2
      3rdparty/spirv-tools/source/opt/optimizer.cpp
  25. 8 3
      3rdparty/spirv-tools/source/reduce/CMakeLists.txt
  26. 4 3
      3rdparty/spirv-tools/source/text.cpp
  27. 92 1
      3rdparty/spirv-tools/source/util/bitutils.h
  28. 13 7
      3rdparty/spirv-tools/source/val/validate_composites.cpp
  29. 4 0
      3rdparty/spirv-tools/source/val/validate_derivatives.cpp
  30. 2 1
      3rdparty/spirv-tools/test/binary_header_get_test.cpp
  31. 105 66
      3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp
  32. 79 44
      3rdparty/spirv-tools/test/fuzz/transformation_copy_object_test.cpp
  33. 76 46
      3rdparty/spirv-tools/test/fuzz/transformation_split_block_test.cpp
  34. 32 0
      3rdparty/spirv-tools/test/fuzzers/BUILD.gn
  35. 70 0
      3rdparty/spirv-tools/test/fuzzers/spvtools_as_fuzzer.cpp
  36. 64 0
      3rdparty/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp
  37. 120 10
      3rdparty/spirv-tools/test/opt/fold_spec_const_op_composite_test.cpp
  38. 1 10
      3rdparty/spirv-tools/test/opt/fold_test.cpp
  39. 48 0
      3rdparty/spirv-tools/test/opt/optimizer_test.cpp
  40. 0 28
      3rdparty/spirv-tools/test/opt/reduce_load_size_test.cpp
  41. 0 31
      3rdparty/spirv-tools/test/opt/scalar_replacement_test.cpp
  42. 0 68
      3rdparty/spirv-tools/test/opt/vector_dce_test.cpp
  43. 1 0
      3rdparty/spirv-tools/test/util/CMakeLists.txt
  44. 193 0
      3rdparty/spirv-tools/test/util/bitutils_test.cpp
  45. 39 23
      3rdparty/spirv-tools/test/val/val_composites_test.cpp
  46. 40 0
      3rdparty/spirv-tools/test/val/val_derivatives_test.cpp
  47. 1 1
      3rdparty/spirv-tools/utils/update_build_version.py

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

@@ -1 +1 @@
-"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-120-g2276e597"
+"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-133-ge8c3f9b0"

+ 13 - 3
3rdparty/spirv-tools/source/CMakeLists.txt

@@ -340,7 +340,7 @@ spvtools_default_compile_options(${SPIRV_TOOLS})
 target_include_directories(${SPIRV_TOOLS}
   PUBLIC
     $<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
-    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/include>
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
   PRIVATE ${SPIRV_HEADER_INCLUDE_DIR}
   )
@@ -353,7 +353,7 @@ spvtools_default_compile_options(${SPIRV_TOOLS}-shared)
 target_include_directories(${SPIRV_TOOLS}-shared
   PUBLIC
     $<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
-    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/include>
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
   PRIVATE ${SPIRV_HEADER_INCLUDE_DIR}
   )
@@ -379,7 +379,17 @@ if(ENABLE_SPIRV_TOOLS_INSTALL)
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
-  install(EXPORT ${SPIRV_TOOLS}Targets DESTINATION lib/cmake)
+  export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake)
+
+  spvtools_config_package_dir(${SPIRV_TOOLS} PACKAGE_DIR)
+  install(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake DESTINATION ${PACKAGE_DIR})
+
+  # Special config file for root library compared to other libs.
+  file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake
+    "include(\${CMAKE_CURRENT_LIST_DIR}/${SPIRV_TOOLS}Target.cmake)\n"
+    "set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n"
+    "get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n")
+  install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)
 
 if(MSVC)

+ 4 - 3
3rdparty/spirv-tools/source/binary.cpp

@@ -781,9 +781,10 @@ spv_result_t spvBinaryParse(const spv_const_context context, void* user_data,
 // TODO(dneto): This probably belongs in text.cpp since that's the only place
 // that a spv_binary_t value is created.
 void spvBinaryDestroy(spv_binary binary) {
-  if (!binary) return;
-  delete[] binary->code;
-  delete binary;
+  if (binary) {
+    if (binary->code) delete[] binary->code;
+    delete binary;
+  }
 }
 
 size_t spv_strnlen_s(const char* str, size_t strsz) {

+ 8 - 3
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt

@@ -153,8 +153,6 @@ if(SPIRV_BUILD_FUZZER)
   target_include_directories(SPIRV-Tools-fuzz
 		PUBLIC
 			$<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
-			$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/include>
-		PUBLIC
 			$<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
 			$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
         PRIVATE ${spirv-tools_BINARY_DIR}
@@ -174,7 +172,14 @@ if(SPIRV_BUILD_FUZZER)
             RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
             LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
             ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
-	  install(EXPORT SPIRV-Tools-fuzzTargets DESTINATION lib/cmake)
+      export(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake)
+
+      spvtools_config_package_dir(SPIRV-Tools-fuzz PACKAGE_DIR)
+      install(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake
+            DESTINATION ${PACKAGE_DIR})
+
+      spvtools_generate_config_file(SPIRV-Tools-fuzz)
+      install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-fuzzConfig.cmake DESTINATION ${PACKAGE_DIR})
   endif(ENABLE_SPIRV_TOOLS_INSTALL)
 
 endif(SPIRV_BUILD_FUZZER)

+ 33 - 31
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp

@@ -14,6 +14,8 @@
 
 #include "source/fuzz/fuzzer_pass.h"
 
+#include "source/fuzz/instruction_descriptor.h"
+
 namespace spvtools {
 namespace fuzz {
 
@@ -69,9 +71,10 @@ std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
 }
 
 void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
-    std::function<uint32_t(
-        const opt::Function& function, opt::BasicBlock* block,
-        opt::BasicBlock::iterator inst_it, uint32_t base, uint32_t offset)>
+    std::function<
+        void(const opt::Function& function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)>
         maybe_apply_transformation) {
   // Consider every block in every function.
   for (auto& function : *GetIRContext()->module()) {
@@ -80,46 +83,45 @@ void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
       // whether to apply a transformation before it.
 
       // In order for transformations to insert new instructions, they need to
-      // be able to identify the instruction to insert before.  We enable this
-      // by tracking a base instruction, which must generate a result id, and
-      // an offset (to allow us to identify instructions that do not generate
-      // result ids).
+      // be able to identify the instruction to insert before.  We describe an
+      // instruction via its opcode, 'opc', a base instruction 'base' that has a
+      // result id, and the number of instructions with opcode 'opc' that we
+      // should skip when searching from 'base' for the desired instruction.
+      // (An instruction that has a result id is represented by its own opcode,
+      // itself as 'base', and a skip-count of 0.)
+      std::vector<std::tuple<uint32_t, SpvOp, uint32_t>>
+          base_opcode_skip_triples;
 
       // The initial base instruction is the block label.
       uint32_t base = block.id();
-      uint32_t offset = 0;
-      // Consider every instruction in the block.
+
+      // Counts the number of times we have seen each opcode since we reset the
+      // base instruction.
+      std::map<SpvOp, uint32_t> skip_count;
+
+      // Consider every instruction in the block.  The label is excluded: it is
+      // only necessary to consider it as a base in case the first instruction
+      // in the block does not have a result id.
       for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) {
         if (inst_it->HasResultId()) {
           // In the case that the instruction has a result id, we use the
-          // instruction as its own base, with zero offset.
+          // instruction as its own base, and clear the skip counts we have
+          // collected.
           base = inst_it->result_id();
-          offset = 0;
-        } else {
-          // The instruction does not have a result id, so we need to identify
-          // it via the latest instruction that did have a result id (base), and
-          // an incremented offset.
-          offset++;
+          skip_count.clear();
         }
+        const SpvOp opcode = inst_it->opcode();
 
         // Invoke the provided function, which might apply a transformation.
-        // Its return value informs us of how many instructions it inserted.
-        // (This will be 0 if no transformation was applied.)
-        uint32_t num_instructions_inserted =
-            maybe_apply_transformation(function, &block, inst_it, base, offset);
+        maybe_apply_transformation(
+            function, &block, inst_it,
+            MakeInstructionDescriptor(
+                base, opcode,
+                skip_count.count(opcode) ? skip_count.at(opcode) : 0));
 
         if (!inst_it->HasResultId()) {
-          // We are tracking the current id-less instruction via an offset,
-          // |offset|, from a previous instruction, |base|, that has an id. We
-          // increment |offset| to reflect any newly-inserted instructions.
-          //
-          // An alternative would be to reset |base| to be an id generated by
-          // a newly-inserted instruction, but that would be more complex, and
-          // sticking to a |base| that already existed before this
-          // transformation was applied makes the applicability of future
-          // transformations less tightly coupled with the presence of the just-
-          // applied transformation.
-          offset += num_instructions_inserted;
+          skip_count[opcode] =
+              skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1;
         }
       }
     }

+ 10 - 11
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h

@@ -67,27 +67,26 @@ class FuzzerPass {
           instruction_is_relevant);
 
   // A helper method that iterates through each instruction in each block, at
-  // all times tracking a base instruction and offset that allows that latest
+  // all times tracking an instruction descriptor that allows the latest
   // instruction to be located even if it has no result id.
   //
-  // The code to manipulate the base and offset is a bit fiddly, and the point
-  // of this method is to avoiding having to duplicate it in multiple
+  // The code to manipulate the instruction descriptor is a bit fiddly, and the
+  // point of this method is to avoiding having to duplicate it in multiple
   // transformation passes.
   //
   // The function |maybe_apply_transformation| is invoked for each instruction
   // |inst_it| in block |block| of function |function| that is encountered.  The
-  // |base| and |offset| parameters to the function object allow |inst_it| to be
-  // identified.
+  // |instruction_descriptor| parameter to the function object allows |inst_it|
+  // to be identified.
   //
   // The job of |maybe_apply_transformation| is to randomly decide whether to
   // try to apply some transformation, and then - if selected - to attempt to
-  // apply it.  The function returns the number of instructions that were
-  // inserted before |inst_it|, so that |offset| can be updated.
-  //
+  // apply it.
   void MaybeAddTransformationBeforeEachInstruction(
-      std::function<uint32_t(
-          const opt::Function& function, opt::BasicBlock* block,
-          opt::BasicBlock::iterator inst_it, uint32_t base, uint32_t offset)>
+      std::function<
+          void(const opt::Function& function, opt::BasicBlock* block,
+               opt::BasicBlock::iterator inst_it,
+               const protobufs::InstructionDescriptor& instruction_descriptor)>
           maybe_apply_transformation);
 
  private:

+ 10 - 10
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp

@@ -43,21 +43,22 @@ void FuzzerPassConstructComposites::Apply() {
   }
 
   MaybeAddTransformationBeforeEachInstruction(
-      [this, &composite_type_ids](const opt::Function& function,
-                                  opt::BasicBlock* block,
-                                  opt::BasicBlock::iterator inst_it,
-                                  uint32_t base, uint32_t offset) -> uint32_t {
+      [this, &composite_type_ids](
+          const opt::Function& function, opt::BasicBlock* block,
+          opt::BasicBlock::iterator inst_it,
+          const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
         // Check whether it is legitimate to insert a composite construction
         // before the instruction.
         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
                 SpvOpCompositeConstruct, inst_it)) {
-          return 0;
+          return;
         }
 
         // Randomly decide whether to try inserting an object copy here.
         if (!GetFuzzerContext()->ChoosePercentage(
                 GetFuzzerContext()->GetChanceOfConstructingComposite())) {
-          return 0;
+          return;
         }
 
         // For each instruction that is available at this program point (i.e. an
@@ -134,21 +135,20 @@ void FuzzerPassConstructComposites::Apply() {
           // We did not manage to make a composite; return 0 to indicate that no
           // instructions were added.
           assert(constructor_arguments == nullptr);
-          return 0;
+          return;
         }
         assert(constructor_arguments != nullptr);
 
         // Make and apply a transformation.
         TransformationConstructComposite transformation(
-            chosen_composite_type, *constructor_arguments, base, offset,
-            GetFuzzerContext()->GetFreshId());
+            chosen_composite_type, *constructor_arguments,
+            instruction_descriptor, GetFuzzerContext()->GetFreshId());
         assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
                "This transformation should be applicable by construction.");
         transformation.Apply(GetIRContext(), GetFactManager());
         *GetTransformations()->add_transformation() =
             transformation.ToMessage();
         // Indicate that one instruction was added.
-        return 1;
       });
 }
 

+ 12 - 7
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp

@@ -31,19 +31,25 @@ FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
 void FuzzerPassCopyObjects::Apply() {
   MaybeAddTransformationBeforeEachInstruction(
       [this](const opt::Function& function, opt::BasicBlock* block,
-             opt::BasicBlock::iterator inst_it, uint32_t base,
-             uint32_t offset) -> uint32_t {
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(inst_it->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
         // Check whether it is legitimate to insert a copy before this
         // instruction.
         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
                                                           inst_it)) {
-          return 0;
+          return;
         }
 
         // Randomly decide whether to try inserting an object copy here.
         if (!GetFuzzerContext()->ChoosePercentage(
                 GetFuzzerContext()->GetChanceOfCopyingObject())) {
-          return 0;
+          return;
         }
 
         std::vector<opt::Instruction*> relevant_instructions =
@@ -53,21 +59,20 @@ void FuzzerPassCopyObjects::Apply() {
         // At this point, |relevant_instructions| contains all the instructions
         // we might think of copying.
         if (relevant_instructions.empty()) {
-          return 0;
+          return;
         }
 
         // Choose a copyable instruction at random, and create and apply an
         // object copying transformation based on it.
         uint32_t index = GetFuzzerContext()->RandomIndex(relevant_instructions);
         TransformationCopyObject transformation(
-            relevant_instructions[index]->result_id(), base, offset,
+            relevant_instructions[index]->result_id(), instruction_descriptor,
             GetFuzzerContext()->GetFreshId());
         assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
                "This transformation should be applicable by construction.");
         transformation.Apply(GetIRContext(), GetFactManager());
         *GetTransformations()->add_transformation() =
             transformation.ToMessage();
-        return 1;
       });
 }
 

+ 30 - 21
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp

@@ -14,9 +14,9 @@
 
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 
-#include <utility>
 #include <vector>
 
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_split_block.h"
 
 namespace spvtools {
@@ -49,40 +49,49 @@ void FuzzerPassSplitBlocks::Apply() {
       // We are not going to try to split this block.
       continue;
     }
+
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2964): consider
+    //  taking a simpler approach to identifying the instruction before which
+    //  to split a block.
+
     // We are going to try to split this block.  We now need to choose where
-    // to split it.  We do this by finding a base instruction that has a
-    // result id, and an offset from that base instruction.  We would like
-    // offsets to be as small as possible and ideally 0 - we only need offsets
-    // because not all instructions can be identified by a result id (e.g.
-    // OpStore instructions cannot).
-    std::vector<std::pair<uint32_t, uint32_t>> base_offset_pairs;
+    // to split it.  We describe the instruction before which we would like to
+    // split a block via an InstructionDescriptor, details of which are
+    // commented in the protobufs definition file.
+    std::vector<protobufs::InstructionDescriptor> instruction_descriptors;
+
     // The initial base instruction is the block label.
     uint32_t base = block->id();
-    uint32_t offset = 0;
+
+    // Counts the number of times we have seen each opcode since we reset the
+    // base instruction.
+    std::map<SpvOp, uint32_t> skip_count;
+
     // Consider every instruction in the block.  The label is excluded: it is
     // only necessary to consider it as a base in case the first instruction
     // in the block does not have a result id.
     for (auto& inst : *block) {
       if (inst.HasResultId()) {
         // In the case that the instruction has a result id, we use the
-        // instruction as its own base, with zero offset.
+        // instruction as its own base, and clear the skip counts we have
+        // collected.
         base = inst.result_id();
-        offset = 0;
-      } else {
-        // The instruction does not have a result id, so we need to identify
-        // it via the latest instruction that did have a result id (base), and
-        // an incremented offset.
-        offset++;
+        skip_count.clear();
+      }
+      const SpvOp opcode = inst.opcode();
+      instruction_descriptors.emplace_back(MakeInstructionDescriptor(
+          base, opcode, skip_count.count(opcode) ? skip_count.at(opcode) : 0));
+      if (!inst.HasResultId()) {
+        skip_count[opcode] =
+            skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1;
       }
-      base_offset_pairs.emplace_back(base, offset);
     }
     // Having identified all the places we might be able to split the block,
     // we choose one of them.
-    auto base_offset =
-        base_offset_pairs[GetFuzzerContext()->RandomIndex(base_offset_pairs)];
-    auto transformation =
-        TransformationSplitBlock(base_offset.first, base_offset.second,
-                                 GetFuzzerContext()->GetFreshId());
+    auto transformation = TransformationSplitBlock(
+        instruction_descriptors[GetFuzzerContext()->RandomIndex(
+            instruction_descriptors)],
+        GetFuzzerContext()->GetFreshId());
     // If the position we have chosen turns out to be a valid place to split
     // the block, we apply the split. Otherwise the block just doesn't get
     // split.

+ 4 - 29
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp

@@ -170,39 +170,14 @@ bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
   return false;
 }
 
-opt::BasicBlock::iterator GetIteratorForBaseInstructionAndOffset(
-    opt::BasicBlock* block, const opt::Instruction* base_inst,
-    uint32_t offset) {
-  // The cases where |base_inst| is the block's label, vs. inside the block,
-  // are dealt with separately.
-  if (base_inst == block->GetLabelInst()) {
-    // |base_inst| is the block's label.
-    if (offset == 0) {
-      // We cannot return an iterator to the block's label.
-      return block->end();
-    }
-    // Conceptually, the first instruction in the block is [label + 1].
-    // We thus start from 1 when applying the offset.
-    auto inst_it = block->begin();
-    for (uint32_t i = 1; i < offset && inst_it != block->end(); i++) {
-      ++inst_it;
-    }
-    // This is either the desired instruction, or the end of the block.
-    return inst_it;
-  }
-  // |base_inst| is inside the block.
+opt::BasicBlock::iterator GetIteratorForInstruction(
+    opt::BasicBlock* block, const opt::Instruction* inst) {
   for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) {
-    if (base_inst == &*inst_it) {
-      // We have found the base instruction; we now apply the offset.
-      for (uint32_t i = 0; i < offset && inst_it != block->end(); i++) {
-        ++inst_it;
-      }
-      // This is either the desired instruction, or the end of the block.
+    if (inst == &*inst_it) {
       return inst_it;
     }
   }
-  assert(false && "The base instruction was not found.");
-  return nullptr;
+  return block->end();
 }
 
 bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,

+ 3 - 8
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h

@@ -64,15 +64,10 @@ void AddUnreachableEdgeAndUpdateOpPhis(
 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
                                     uint32_t maybe_loop_header_id);
 
-// Requires that |base_inst| is either the label instruction of |block| or an
-// instruction inside |block|.
-//
-// If the block contains a (non-label, non-terminator) instruction |offset|
-// instructions after |base_inst|, an iterator to this instruction is returned.
-//
+// If |block| contains |inst|, an iterator for |inst| is returned.
 // Otherwise |block|->end() is returned.
-opt::BasicBlock::iterator GetIteratorForBaseInstructionAndOffset(
-    opt::BasicBlock* block, const opt::Instruction* base_inst, uint32_t offset);
+opt::BasicBlock::iterator GetIteratorForInstruction(
+    opt::BasicBlock* block, const opt::Instruction* inst);
 
 // The function determines whether adding an edge from |bb_from| to |bb_to| -
 // is legitimate with respect to the SPIR-V rule that a definition must

+ 12 - 23
3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto

@@ -331,15 +331,12 @@ message TransformationCopyObject {
   // Id of the object to be copied
   uint32 object = 1;
 
-  // The id of an instruction in a block
-  uint32 base_instruction_id = 2;
-
-  // An offset, such that OpCopyObject instruction should be inserted right
-  // before the instruction |offset| instructions after |base_instruction_id|
-  uint32 offset = 3;
+  // A descriptor for an instruction in a block before which the new
+  // OpCopyObject instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 2;
 
   // A fresh id for the copied object
-  uint32 fresh_id = 4;
+  uint32 fresh_id = 3;
 
 }
 
@@ -354,16 +351,12 @@ message TransformationConstructComposite {
   // Ids of the objects that will form the components of the composite
   repeated uint32 component = 2;
 
-  // The id of an instruction in a block
-  uint32 base_instruction_id = 3;
-
-  // An offset, such that OpCompositeConstruct instruction should be inserted
-  // right before the instruction |offset| instructions after
-  // |base_instruction_id|
-  uint32 offset = 4;
+  // A descriptor for an instruction in a block before which the new
+  // OpCompositeConstruct instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 3;
 
   // A fresh id for the composite object
-  uint32 fresh_id = 5;
+  uint32 fresh_id = 4;
 
 }
 
@@ -491,13 +484,9 @@ message TransformationSplitBlock {
 
   // A transformation that splits a basic block into two basic blocks
 
-  // The result id of an instruction
-  uint32 base_instruction_id = 1;
-
-  // An offset, such that the block containing |base_instruction_id| should be
-  // split right before the instruction |offset| instructions after
-  // |base_instruction_id|
-  uint32 offset = 2;
+  // A descriptor for an instruction such that the block containing the
+  // described instruction should be split right before the instruction.
+  InstructionDescriptor instruction_to_split_before = 1;
 
   // An id that must not yet be used by the module to which this transformation
   // is applied.  Rather than having the transformation choose a suitable id on
@@ -507,6 +496,6 @@ message TransformationSplitBlock {
   // transformation, and if we end up changing what that id is, due to removing
   // earlier transformations, it may inhibit later transformations from
   // applying.
-  uint32 fresh_id = 3;
+  uint32 fresh_id = 2;
 
 }

+ 16 - 26
3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.cpp

@@ -15,6 +15,8 @@
 #include "source/fuzz/transformation_construct_composite.h"
 
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/opt/instruction.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -25,13 +27,14 @@ TransformationConstructComposite::TransformationConstructComposite(
 
 TransformationConstructComposite::TransformationConstructComposite(
     uint32_t composite_type_id, std::vector<uint32_t> component,
-    uint32_t base_instruction_id, uint32_t offset, uint32_t fresh_id) {
+    const protobufs::InstructionDescriptor& instruction_to_insert_before,
+    uint32_t fresh_id) {
   message_.set_composite_type_id(composite_type_id);
   for (auto a_component : component) {
     message_.add_component(a_component);
   }
-  message_.set_base_instruction_id(base_instruction_id);
-  message_.set_offset(offset);
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
   message_.set_fresh_id(fresh_id);
 }
 
@@ -42,24 +45,11 @@ bool TransformationConstructComposite::IsApplicable(
     return false;
   }
 
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  if (!base_instruction) {
-    // The given id to insert after is not defined.
-    return false;
-  }
-
-  auto destination_block = context->get_instr_block(base_instruction);
-  if (!destination_block) {
-    // The given id to insert after is not in a block.
-    return false;
-  }
-
-  auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      destination_block, base_instruction, message_.offset());
-
-  if (insert_before == destination_block->end()) {
-    // The offset was inappropriate.
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!insert_before) {
+    // The instruction before which the composite should be inserted was not
+    // found.
     return false;
   }
 
@@ -125,11 +115,11 @@ void TransformationConstructComposite::Apply(opt::IRContext* context,
                                              FactManager* fact_manager) const {
   // Use the base and offset information from the transformation to determine
   // where in the module a new instruction should be inserted.
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  auto destination_block = context->get_instr_block(base_instruction);
-  auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      destination_block, base_instruction, message_.offset());
+  auto insert_before_inst =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  auto destination_block = context->get_instr_block(insert_before_inst);
+  auto insert_before = fuzzerutil::GetIteratorForInstruction(
+      destination_block, insert_before_inst);
 
   // Prepare the input operands for an OpCompositeConstruct instruction.
   opt::Instruction::OperandList in_operands;

+ 4 - 4
3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h

@@ -28,10 +28,10 @@ class TransformationConstructComposite : public Transformation {
   explicit TransformationConstructComposite(
       const protobufs::TransformationConstructComposite& message);
 
-  TransformationConstructComposite(uint32_t composite_type_id,
-                                   std::vector<uint32_t> component,
-                                   uint32_t base_instruction_id,
-                                   uint32_t offset, uint32_t fresh_id);
+  TransformationConstructComposite(
+      uint32_t composite_type_id, std::vector<uint32_t> component,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before,
+      uint32_t fresh_id);
 
   // - |message_.fresh_id| must not be used by the module.
   // - |message_.composite_type_id| must be the id of a composite type

+ 19 - 31
3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp

@@ -15,6 +15,7 @@
 #include "source/fuzz/transformation_copy_object.h"
 
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/opt/instruction.h"
 #include "source/util/make_unique.h"
 
@@ -25,13 +26,13 @@ TransformationCopyObject::TransformationCopyObject(
     const protobufs::TransformationCopyObject& message)
     : message_(message) {}
 
-TransformationCopyObject::TransformationCopyObject(uint32_t object,
-                                                   uint32_t base_instruction_id,
-                                                   uint32_t offset,
-                                                   uint32_t fresh_id) {
+TransformationCopyObject::TransformationCopyObject(
+    uint32_t object,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before,
+    uint32_t fresh_id) {
   message_.set_object(object);
-  message_.set_base_instruction_id(base_instruction_id);
-  message_.set_offset(offset);
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
   message_.set_fresh_id(fresh_id);
 }
 
@@ -50,24 +51,10 @@ bool TransformationCopyObject::IsApplicable(
     return false;
   }
 
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  if (!base_instruction) {
-    // The given id to insert after is not defined.
-    return false;
-  }
-
-  auto destination_block = context->get_instr_block(base_instruction);
-  if (!destination_block) {
-    // The given id to insert after is not in a block.
-    return false;
-  }
-
-  auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      destination_block, base_instruction, message_.offset());
-
-  if (insert_before == destination_block->end()) {
-    // The offset was inappropriate.
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!insert_before) {
+    // The instruction before which the copy should be inserted was not found.
     return false;
   }
 
@@ -86,7 +73,9 @@ bool TransformationCopyObject::IsApplicable(
   // insert it before the object's defining instruction.
   return !context->get_instr_block(object_inst) ||
          (object_inst != &*insert_before &&
-          context->GetDominatorAnalysis(destination_block->GetParent())
+          context
+              ->GetDominatorAnalysis(
+                  context->get_instr_block(insert_before)->GetParent())
               ->Dominates(object_inst, &*insert_before));
 }
 
@@ -94,13 +83,12 @@ void TransformationCopyObject::Apply(opt::IRContext* context,
                                      FactManager* fact_manager) const {
   auto object_inst = context->get_def_use_mgr()->GetDef(message_.object());
   assert(object_inst && "The object to be copied must exist.");
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  assert(base_instruction && "The base instruction must exist.");
-  auto destination_block = context->get_instr_block(base_instruction);
+  auto insert_before_inst =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  auto destination_block = context->get_instr_block(insert_before_inst);
   assert(destination_block && "The base instruction must be in a block.");
-  auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      destination_block, base_instruction, message_.offset());
+  auto insert_before = fuzzerutil::GetIteratorForInstruction(
+      destination_block, insert_before_inst);
   assert(insert_before != destination_block->end() &&
          "There must be an instruction before which the copy can be inserted.");
 

+ 4 - 2
3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h

@@ -28,8 +28,10 @@ class TransformationCopyObject : public Transformation {
   explicit TransformationCopyObject(
       const protobufs::TransformationCopyObject& message);
 
-  TransformationCopyObject(uint32_t object, uint32_t base_instruction_id,
-                           uint32_t offset, uint32_t fresh_id);
+  TransformationCopyObject(
+      uint32_t object,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before,
+      uint32_t fresh_id);
 
   // - |message_.fresh_id| must not be used by the module.
   // - |message_.object| must be a result id that is a legitimate operand for

+ 41 - 49
3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp

@@ -17,6 +17,7 @@
 #include <utility>
 
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/util/make_unique.h"
 
 namespace spvtools {
@@ -26,11 +27,10 @@ TransformationSplitBlock::TransformationSplitBlock(
     const spvtools::fuzz::protobufs::TransformationSplitBlock& message)
     : message_(message) {}
 
-TransformationSplitBlock::TransformationSplitBlock(uint32_t base_instruction_id,
-                                                   uint32_t offset,
-                                                   uint32_t fresh_id) {
-  message_.set_base_instruction_id(base_instruction_id);
-  message_.set_offset(offset);
+TransformationSplitBlock::TransformationSplitBlock(
+    const protobufs::InstructionDescriptor& instruction_to_split_before,
+    uint32_t fresh_id) {
+  *message_.mutable_instruction_to_split_before() = instruction_to_split_before;
   message_.set_fresh_id(fresh_id);
 }
 
@@ -40,31 +40,28 @@ bool TransformationSplitBlock::IsApplicable(
     // We require the id for the new block to be unused.
     return false;
   }
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  if (!base_instruction) {
+  auto instruction_to_split_before =
+      FindInstruction(message_.instruction_to_split_before(), context);
+  if (!instruction_to_split_before) {
     // The instruction describing the block we should split does not exist.
     return false;
   }
-  auto block_containing_base_instruction =
-      context->get_instr_block(base_instruction);
-  if (!block_containing_base_instruction) {
-    // The instruction describing the block we should split is not contained in
-    // a block.
-    return false;
-  }
+  auto block_to_split = context->get_instr_block(instruction_to_split_before);
+  assert(block_to_split &&
+         "We should not have managed to find the "
+         "instruction if it was not contained in a block.");
 
-  if (block_containing_base_instruction->IsLoopHeader()) {
+  if (block_to_split->IsLoopHeader()) {
     // We cannot split a loop header block: back-edges would become invalid.
     return false;
   }
 
-  auto split_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      block_containing_base_instruction, base_instruction, message_.offset());
-  if (split_before == block_containing_base_instruction->end()) {
-    // The offset was inappropriate.
-    return false;
-  }
+  auto split_before = fuzzerutil::GetIteratorForInstruction(
+      block_to_split, instruction_to_split_before);
+  assert(split_before != block_to_split->end() &&
+         "At this point we know the"
+         " block split point exists.");
+
   if (split_before->PreviousNode() &&
       split_before->PreviousNode()->opcode() == SpvOpSelectionMerge) {
     // We cannot split directly after a selection merge: this would separate
@@ -84,44 +81,39 @@ bool TransformationSplitBlock::IsApplicable(
 
 void TransformationSplitBlock::Apply(opt::IRContext* context,
                                      FactManager* /*unused*/) const {
-  auto base_instruction =
-      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
-  assert(base_instruction && "Base instruction must exist");
-  auto block_containing_base_instruction =
-      context->get_instr_block(base_instruction);
-  assert(block_containing_base_instruction &&
-         "Base instruction must be in a block");
-  auto split_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
-      block_containing_base_instruction, base_instruction, message_.offset());
-  assert(split_before != block_containing_base_instruction->end() &&
+  opt::Instruction* instruction_to_split_before =
+      FindInstruction(message_.instruction_to_split_before(), context);
+  opt::BasicBlock* block_to_split =
+      context->get_instr_block(instruction_to_split_before);
+  auto split_before = fuzzerutil::GetIteratorForInstruction(
+      block_to_split, instruction_to_split_before);
+  assert(split_before != block_to_split->end() &&
          "If the transformation is applicable, we should have an "
          "instruction to split on.");
+
   // We need to make sure the module's id bound is large enough to add the
   // fresh id.
   fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
   // Split the block.
-  auto new_bb = block_containing_base_instruction->SplitBasicBlock(
-      context, message_.fresh_id(), split_before);
+  auto new_bb = block_to_split->SplitBasicBlock(context, message_.fresh_id(),
+                                                split_before);
   // The split does not automatically add a branch between the two parts of
   // the original block, so we add one.
-  block_containing_base_instruction->AddInstruction(
-      MakeUnique<opt::Instruction>(
-          context, SpvOpBranch, 0, 0,
-          std::initializer_list<opt::Operand>{
-              opt::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-                           {message_.fresh_id()})}));
+  block_to_split->AddInstruction(MakeUnique<opt::Instruction>(
+      context, SpvOpBranch, 0, 0,
+      std::initializer_list<opt::Operand>{opt::Operand(
+          spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})}));
   // If we split before OpPhi instructions, we need to update their
   // predecessor operand so that the block they used to be inside is now the
   // predecessor.
-  new_bb->ForEachPhiInst(
-      [block_containing_base_instruction](opt::Instruction* phi_inst) {
-        // The following assertion is a sanity check.  It is guaranteed to hold
-        // if IsApplicable holds.
-        assert(phi_inst->NumInOperands() == 2 &&
-               "We can only split a block before an OpPhi if block has exactly "
-               "one predecessor.");
-        phi_inst->SetInOperand(1, {block_containing_base_instruction->id()});
-      });
+  new_bb->ForEachPhiInst([block_to_split](opt::Instruction* phi_inst) {
+    // The following assertion is a sanity check.  It is guaranteed to hold
+    // if IsApplicable holds.
+    assert(phi_inst->NumInOperands() == 2 &&
+           "We can only split a block before an OpPhi if block has exactly "
+           "one predecessor.");
+    phi_inst->SetInOperand(1, {block_to_split->id()});
+  });
   // Invalidate all analyses
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
 }

+ 3 - 2
3rdparty/spirv-tools/source/fuzz/transformation_split_block.h

@@ -28,8 +28,9 @@ class TransformationSplitBlock : public Transformation {
   explicit TransformationSplitBlock(
       const protobufs::TransformationSplitBlock& message);
 
-  TransformationSplitBlock(uint32_t base_instruction_id, uint32_t offset,
-                           uint32_t fresh_id);
+  TransformationSplitBlock(
+      const protobufs::InstructionDescriptor& instruction_to_split_before,
+      uint32_t fresh_id);
 
   // - |message_.base_instruction_id| must be the result id of an instruction
   //   'base' in some block 'blk'.

+ 8 - 3
3rdparty/spirv-tools/source/link/CMakeLists.txt

@@ -19,8 +19,6 @@ spvtools_default_compile_options(SPIRV-Tools-link)
 target_include_directories(SPIRV-Tools-link
   PUBLIC
     $<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
-	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/include>
-  PUBLIC
 	$<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
 	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
@@ -37,5 +35,12 @@ if(ENABLE_SPIRV_TOOLS_INSTALL)
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
-  install(EXPORT SPIRV-Tools-linkTargets DESTINATION lib/cmake)
+  export(EXPORT SPIRV-Tools-linkTargets FILE SPIRV-Tools-linkTargets.cmake)
+
+  spvtools_config_package_dir(SPIRV-Tools-link PACKAGE_DIR)
+  install(EXPORT SPIRV-Tools-linkTargets FILE SPIRV-Tools-linkTargets.cmake
+  	DESTINATION ${PACKAGE_DIR})
+
+  spvtools_generate_config_file(SPIRV-Tools-link)
+  install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-linkConfig.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)

+ 8 - 3
3rdparty/spirv-tools/source/opt/CMakeLists.txt

@@ -235,8 +235,6 @@ spvtools_default_compile_options(SPIRV-Tools-opt)
 target_include_directories(SPIRV-Tools-opt
   PUBLIC
 	$<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
-	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/include>
-  PUBLIC
 	$<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
 	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
@@ -253,5 +251,12 @@ if(ENABLE_SPIRV_TOOLS_INSTALL)
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
-  install(EXPORT SPIRV-Tools-optTargets DESTINATION lib/cmake)
+  export(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake)
+
+  spvtools_config_package_dir(SPIRV-Tools-opt PACKAGE_DIR)
+  install(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake
+  	DESTINATION ${PACKAGE_DIR})
+
+  spvtools_generate_config_file(SPIRV-Tools-opt)
+  install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-optConfig.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)

+ 6 - 0
3rdparty/spirv-tools/source/opt/fold.cpp

@@ -56,6 +56,10 @@ uint32_t InstructionFolder::UnaryOperate(SpvOp opcode, uint32_t operand) const {
       return ~operand;
     case SpvOp::SpvOpLogicalNot:
       return !static_cast<bool>(operand);
+    case SpvOp::SpvOpUConvert:
+      return operand;
+    case SpvOp::SpvOpSConvert:
+      return operand;
     default:
       assert(false &&
              "Unsupported unary operation for OpSpecConstantOp instruction");
@@ -596,6 +600,8 @@ bool InstructionFolder::IsFoldableOpcode(SpvOp opcode) const {
     case SpvOp::SpvOpSMod:
     case SpvOp::SpvOpSNegate:
     case SpvOp::SpvOpSRem:
+    case SpvOp::SpvOpSConvert:
+    case SpvOp::SpvOpUConvert:
     case SpvOp::SpvOpUDiv:
     case SpvOp::SpvOpUGreaterThan:
     case SpvOp::SpvOpUGreaterThanEqual:

+ 59 - 6
3rdparty/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp

@@ -316,6 +316,59 @@ bool IsValidTypeForComponentWiseOperation(const analysis::Type* type) {
   }
   return false;
 }
+
+// Encodes the integer |value| of in a word vector format appropriate for
+// representing this value as a operands for a constant definition. Performs
+// zero-extension/sign-extension/truncation when needed, based on the signess of
+// the given target type.
+//
+// Note: type |type| argument must be either Integer or Bool.
+utils::SmallVector<uint32_t, 2> EncodeIntegerAsWords(const analysis::Type& type,
+                                                     uint32_t value) {
+  const uint32_t all_ones = ~0;
+  uint32_t bit_width = 0;
+  uint32_t pad_value = 0;
+  bool result_type_signed = false;
+  if (auto* int_ty = type.AsInteger()) {
+    bit_width = int_ty->width();
+    result_type_signed = int_ty->IsSigned();
+    if (result_type_signed && static_cast<int32_t>(value) < 0) {
+      pad_value = all_ones;
+    }
+  } else if (type.AsBool()) {
+    bit_width = 1;
+  } else {
+    assert(false && "type must be Integer or Bool");
+  }
+
+  assert(bit_width > 0);
+  uint32_t first_word = value;
+  const uint32_t bits_per_word = 32;
+
+  // Truncate first_word if the |type| has width less than uint32.
+  if (bit_width < bits_per_word) {
+    const uint32_t num_high_bits_to_mask = bits_per_word - bit_width;
+    const bool is_negative_after_truncation =
+        result_type_signed &&
+        utils::IsBitAtPositionSet(first_word, bit_width - 1);
+
+    if (is_negative_after_truncation) {
+      // Truncate and sign-extend |first_word|. No padding words will be
+      // added and |pad_value| can be left as-is.
+      first_word = utils::SetHighBits(first_word, num_high_bits_to_mask);
+    } else {
+      first_word = utils::ClearHighBits(first_word, num_high_bits_to_mask);
+    }
+  }
+
+  utils::SmallVector<uint32_t, 2> words = {first_word};
+  for (uint32_t current_bit = bits_per_word; current_bit < bit_width;
+       current_bit += bits_per_word) {
+    words.push_back(pad_value);
+  }
+
+  return words;
+}
 }  // namespace
 
 Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
@@ -345,10 +398,10 @@ Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
 
   if (result_type->AsInteger() || result_type->AsBool()) {
     // Scalar operation
-    uint32_t result_val =
+    const uint32_t result_val =
         context()->get_instruction_folder().FoldScalars(spec_opcode, operands);
-    auto result_const =
-        context()->get_constant_mgr()->GetConstant(result_type, {result_val});
+    auto result_const = context()->get_constant_mgr()->GetConstant(
+        result_type, EncodeIntegerAsWords(*result_type, result_val));
     return context()->get_constant_mgr()->BuildInstructionAndAddToModule(
         result_const, pos);
   } else if (result_type->AsVector()) {
@@ -360,9 +413,9 @@ Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
         context()->get_instruction_folder().FoldVectors(spec_opcode, num_dims,
                                                         operands);
     std::vector<const analysis::Constant*> result_vector_components;
-    for (uint32_t r : result_vec) {
-      if (auto rc =
-              context()->get_constant_mgr()->GetConstant(element_type, {r})) {
+    for (const uint32_t r : result_vec) {
+      if (auto rc = context()->get_constant_mgr()->GetConstant(
+              element_type, EncodeIntegerAsWords(*element_type, r))) {
         result_vector_components.push_back(rc);
         if (!context()->get_constant_mgr()->BuildInstructionAndAddToModule(
                 rc, pos)) {

+ 0 - 15
3rdparty/spirv-tools/source/opt/folding_rules.cpp

@@ -1407,20 +1407,6 @@ FoldingRule CompositeExtractFeedingConstruct() {
   };
 }
 
-// Folds an OpCompositeExtract instruction with no indexes into an OpCopyObject.
-bool ExtractWithNoIndexes(IRContext*, Instruction* inst,
-                          const std::vector<const analysis::Constant*>&) {
-  assert(inst->opcode() == SpvOpCompositeExtract &&
-         "Wrong opcode.  Should be OpCompositeExtract.");
-
-  if (inst->NumInOperands() > 1) {
-    return false;
-  }
-
-  inst->SetOpcode(SpvOpCopyObject);
-  return true;
-}
-
 FoldingRule InsertFeedingExtract() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
@@ -2227,7 +2213,6 @@ void FoldingRules::AddFoldingRules() {
   // Take that into consideration.
   rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct());
 
-  rules_[SpvOpCompositeExtract].push_back(ExtractWithNoIndexes);
   rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract());
   rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract());
   rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract());

+ 5 - 2
3rdparty/spirv-tools/source/opt/optimizer.cpp

@@ -565,8 +565,11 @@ bool Optimizer::Run(const uint32_t* original_binary,
 
 #ifndef NDEBUG
   if (status == opt::Pass::Status::SuccessWithoutChange) {
-    auto changed = optimized_binary->size() != original_binary_size ||
-                   memcmp(optimized_binary->data(), original_binary,
+    std::vector<uint32_t> optimized_binary_with_nop;
+    context->module()->ToBinary(&optimized_binary_with_nop,
+                                /* skip_nop = */ false);
+    auto changed = optimized_binary_with_nop.size() != original_binary_size ||
+                   memcmp(optimized_binary_with_nop.data(), original_binary,
                           original_binary_size) != 0;
     assert(!changed &&
            "Binary unexpectedly changed despite optimizer saying there was no "

+ 8 - 3
3rdparty/spirv-tools/source/reduce/CMakeLists.txt

@@ -79,8 +79,6 @@ spvtools_default_compile_options(SPIRV-Tools-reduce)
 target_include_directories(SPIRV-Tools-reduce
   PUBLIC
 	$<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
-	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/include>
-  PUBLIC
 	$<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
 	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
@@ -98,5 +96,12 @@ if(ENABLE_SPIRV_TOOLS_INSTALL)
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
-  install(EXPORT SPIRV-Tools-reduceTargets DESTINATION lib/cmake)
+  export(EXPORT SPIRV-Tools-reduceTargets FILE SPIRV-Tools-reduceTarget.cmake)
+
+  spvtools_config_package_dir(SPIRV-Tools-reduce PACKAGE_DIR)
+  install(EXPORT SPIRV-Tools-reduceTargets FILE SPIRV-Tools-reduceTarget.cmake
+  	DESTINATION ${PACKAGE_DIR})
+
+  spvtools_generate_config_file(SPIRV-Tools-reduce)
+  install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-reduceConfig.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)

+ 4 - 3
3rdparty/spirv-tools/source/text.cpp

@@ -810,7 +810,8 @@ spv_result_t spvTextToBinaryWithOptions(const spv_const_context context,
 }
 
 void spvTextDestroy(spv_text text) {
-  if (!text) return;
-  delete[] text->str;
-  delete text;
+  if (text) {
+    if (text->str) delete[] text->str;
+    delete text;
+  }
 }

+ 92 - 1
3rdparty/spirv-tools/source/util/bitutils.h

@@ -15,8 +15,10 @@
 #ifndef SOURCE_UTIL_BITUTILS_H_
 #define SOURCE_UTIL_BITUTILS_H_
 
+#include <cassert>
 #include <cstdint>
 #include <cstring>
+#include <type_traits>
 
 namespace spvtools {
 namespace utils {
@@ -31,6 +33,14 @@ Dest BitwiseCast(Src source) {
   return dest;
 }
 
+// Calculates the bit width of the integer type |T|.
+template <typename T>
+struct IntegerBitWidth {
+  static_assert(std::is_integral<T>::value, "Integer type required");
+  static const size_t kBitsPerByte = 8;
+  static const size_t get = sizeof(T) * kBitsPerByte;
+};
+
 // SetBits<T, First, Num> returns an integer of type <T> with bits set
 // for position <First> through <First + Num - 1>, counting from the least
 // significant bit. In particular when Num == 0, no positions are set to 1.
@@ -38,7 +48,7 @@ Dest BitwiseCast(Src source) {
 // a bit that will not fit in the underlying type is set.
 template <typename T, size_t First = 0, size_t Num = 0>
 struct SetBits {
-  static_assert(First < sizeof(T) * 8,
+  static_assert(First < IntegerBitWidth<T>::get,
                 "Tried to set a bit that is shifted too far.");
   const static T get = (T(1) << First) | SetBits<T, First + 1, Num - 1>::get;
 };
@@ -49,6 +59,11 @@ struct SetBits<T, Last, 0> {
 };
 
 // This is all compile-time so we can put our tests right here.
+static_assert(IntegerBitWidth<uint32_t>::get == 32, "IntegerBitWidth mismatch");
+static_assert(IntegerBitWidth<int32_t>::get == 32, "IntegerBitWidth mismatch");
+static_assert(IntegerBitWidth<uint64_t>::get == 64, "IntegerBitWidth mismatch");
+static_assert(IntegerBitWidth<uint8_t>::get == 8, "IntegerBitWidth mismatch");
+
 static_assert(SetBits<uint32_t, 0, 0>::get == uint32_t(0x00000000),
               "SetBits failed");
 static_assert(SetBits<uint32_t, 0, 1>::get == uint32_t(0x00000001),
@@ -90,6 +105,82 @@ size_t CountSetBits(T word) {
   return count;
 }
 
+// Checks if the bit at the |position| is set to '1'.
+// Bits zero-indexed starting at the least significant bit.
+// |position| must be within the bit width of |T|.
+template <typename T>
+bool IsBitAtPositionSet(T word, size_t position) {
+  static_assert(std::is_integral<T>::value, "Integer type required");
+  static_assert(std::is_unsigned<T>::value, "Unsigned type required");
+  assert(position < IntegerBitWidth<T>::get &&
+         "position must be less than the bit width");
+  return word & T(T(1) << position);
+}
+
+// Returns a value obtained by setting a range of adjacent bits of |word| to
+// |value|. Affected bits are within the range:
+//   [first_position, first_position + num_bits_to_mutate),
+// assuming zero-based indexing starting at the least
+// significant bit. Bits to mutate must be within the bit width of |T|.
+template <typename T>
+T MutateBits(T word, size_t first_position, size_t num_bits_to_mutate,
+             bool value) {
+  static_assert(std::is_integral<T>::value, "Integer type required");
+  static_assert(std::is_unsigned<T>::value, "Unsigned type required");
+  static const size_t word_bit_width = IntegerBitWidth<T>::get;
+  assert(first_position < word_bit_width &&
+         "Mutated bits must be within bit width");
+  assert(first_position + num_bits_to_mutate <= word_bit_width &&
+         "Mutated bits must be within bit width");
+  if (num_bits_to_mutate == 0) {
+    return word;
+  }
+
+  const T all_ones = ~T(0);
+  const size_t num_unaffected_low_bits = first_position;
+  const T unaffected_low_mask =
+      T(T(all_ones >> num_unaffected_low_bits) << num_unaffected_low_bits);
+
+  const size_t num_unaffected_high_bits =
+      word_bit_width - (first_position + num_bits_to_mutate);
+  const T unaffected_high_mask =
+      T(T(all_ones << num_unaffected_high_bits) >> num_unaffected_high_bits);
+
+  const T mutation_mask = unaffected_low_mask & unaffected_high_mask;
+  if (value) {
+    return word | mutation_mask;
+  }
+  return word & T(~mutation_mask);
+}
+
+// Returns a value obtained by setting the |num_bits_to_set| highest bits to
+// '1'. |num_bits_to_set| must be not be greater than the bit width of |T|.
+template <typename T>
+T SetHighBits(T word, size_t num_bits_to_set) {
+  if (num_bits_to_set == 0) {
+    return word;
+  }
+  const size_t word_bit_width = IntegerBitWidth<T>::get;
+  assert(num_bits_to_set <= word_bit_width &&
+         "Can't set more bits than bit width");
+  return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set,
+                    true);
+}
+
+// Returns a value obtained by setting the |num_bits_to_set| highest bits to
+// '0'. |num_bits_to_set| must be not be greater than the bit width of |T|.
+template <typename T>
+T ClearHighBits(T word, size_t num_bits_to_set) {
+  if (num_bits_to_set == 0) {
+    return word;
+  }
+  const size_t word_bit_width = IntegerBitWidth<T>::get;
+  assert(num_bits_to_set <= word_bit_width &&
+         "Can't clear more bits than bit width");
+  return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set,
+                    false);
+}
+
 }  // namespace utils
 }  // namespace spvtools
 

+ 13 - 7
3rdparty/spirv-tools/source/val/validate_composites.cpp

@@ -30,8 +30,8 @@ namespace {
 // OpCompositeInsert instruction. The function traverses the hierarchy of
 // nested data structures (structs, arrays, vectors, matrices) as directed by
 // the sequence of indices in the instruction. May return error if traversal
-// fails (encountered non-composite, out of bounds, nesting too deep).
-// Returns the type of Composite operand if the instruction has no indices.
+// fails (encountered non-composite, out of bounds, no indices, nesting too
+// deep).
 spv_result_t GetExtractInsertValueType(ValidationState_t& _,
                                        const Instruction* inst,
                                        uint32_t* member_type) {
@@ -40,10 +40,15 @@ spv_result_t GetExtractInsertValueType(ValidationState_t& _,
   uint32_t word_index = opcode == SpvOpCompositeExtract ? 4 : 5;
   const uint32_t num_words = static_cast<uint32_t>(inst->words().size());
   const uint32_t composite_id_index = word_index - 1;
-
   const uint32_t num_indices = num_words - word_index;
   const uint32_t kCompositeExtractInsertMaxNumIndices = 255;
-  if (num_indices > kCompositeExtractInsertMaxNumIndices) {
+
+  if (num_indices == 0) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected at least one index to Op"
+           << spvOpcodeString(inst->opcode()) << ", zero found";
+
+  } else if (num_indices > kCompositeExtractInsertMaxNumIndices) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "The number of indexes in Op" << spvOpcodeString(opcode)
            << " may not exceed " << kCompositeExtractInsertMaxNumIndices
@@ -386,20 +391,20 @@ spv_result_t ValidateCompositeExtract(ValidationState_t& _,
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot extract from a composite of 8- or 16-bit types";
   }
+
   return SPV_SUCCESS;
 }
 
 spv_result_t ValidateCompositeInsert(ValidationState_t& _,
                                      const Instruction* inst) {
-  const SpvOp opcode = inst->opcode();
   const uint32_t object_type = _.GetOperandTypeId(inst, 2);
   const uint32_t composite_type = _.GetOperandTypeId(inst, 3);
   const uint32_t result_type = inst->type_id();
   if (result_type != composite_type) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "The Result Type must be the same as Composite type in Op"
-           << spvOpcodeString(opcode) << " yielding Result Id " << result_type
-           << ".";
+           << spvOpcodeString(inst->opcode()) << " yielding Result Id "
+           << result_type << ".";
   }
 
   uint32_t member_type = 0;
@@ -421,6 +426,7 @@ spv_result_t ValidateCompositeInsert(ValidationState_t& _,
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Cannot insert into a composite of 8- or 16-bit types";
   }
+
   return SPV_SUCCESS;
 }
 

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

@@ -46,6 +46,10 @@ spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst) {
                << "Expected Result Type to be float scalar or vector type: "
                << spvOpcodeString(opcode);
       }
+      if (!_.ContainsSizedIntOrFloatType(result_type, SpvOpTypeFloat, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Result type component width must be 32 bits";
+      }
 
       const uint32_t p_type = _.GetOperandTypeId(inst, 2);
       if (p_type != result_type) {

+ 2 - 1
3rdparty/spirv-tools/test/binary_header_get_test.cpp

@@ -51,7 +51,8 @@ TEST_F(BinaryHeaderGet, Default) {
   ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header));
 
   ASSERT_EQ(static_cast<uint32_t>(SpvMagicNumber), header.magic);
-  ASSERT_EQ(0x00010400u, header.version);
+  // Expect SPIRV-Headers updated to SPIR-V 1.5.
+  ASSERT_EQ(0x00010500u, header.version);
   ASSERT_EQ(static_cast<uint32_t>(SPV_GENERATOR_CODEPLAY), header.generator);
   ASSERT_EQ(1u, header.bound);
   ASSERT_EQ(0u, header.schema);

+ 105 - 66
3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp

@@ -14,6 +14,7 @@
 
 #include "source/fuzz/transformation_construct_composite.h"
 #include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -144,11 +145,13 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
   FactManager fact_manager;
 
   // Make a vec2[3]
-  TransformationConstructComposite make_vec2_array_length_3(37, {41, 45, 27},
-                                                            46, 0, 200);
+  TransformationConstructComposite make_vec2_array_length_3(
+      37, {41, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
+      200);
   // Bad: there are too many components
   TransformationConstructComposite make_vec2_array_length_3_bad(
-      37, {41, 45, 27, 27}, 46, 0, 200);
+      37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
+      200);
   ASSERT_TRUE(
       make_vec2_array_length_3.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(
@@ -160,11 +163,11 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 27, 200, {2}));
 
   // Make a float[2]
-  TransformationConstructComposite make_float_array_length_2(9, {24, 40}, 71, 1,
-                                                             201);
+  TransformationConstructComposite make_float_array_length_2(
+      9, {24, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
   // Bad: %41 does not have type float
-  TransformationConstructComposite make_float_array_length_2_bad(9, {41, 40},
-                                                                 71, 1, 201);
+  TransformationConstructComposite make_float_array_length_2_bad(
+      9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
   ASSERT_TRUE(
       make_float_array_length_2.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(
@@ -175,11 +178,13 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 40, 201, {1}));
 
   // Make a bool[3]
-  TransformationConstructComposite make_bool_array_length_3(47, {33, 50, 50},
-                                                            33, 1, 202);
+  TransformationConstructComposite make_bool_array_length_3(
+      47, {33, 50, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
+      202);
   // Bad: %54 is not available at the desired program point.
   TransformationConstructComposite make_bool_array_length_3_bad(
-      47, {33, 54, 50}, 33, 1, 202);
+      47, {33, 54, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
+      202);
   ASSERT_TRUE(
       make_bool_array_length_3.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(
@@ -191,11 +196,11 @@ TEST(TransformationConstructCompositeTest, ConstructArrays) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 50, 202, {2}));
 
   // make a uvec3[2][2]
-  TransformationConstructComposite make_uvec3_array_length_2_2(58, {69, 100},
-                                                               64, 1, 203);
-  // Bad: Offset 100 is too large.
+  TransformationConstructComposite make_uvec3_array_length_2_2(
+      58, {69, 100}, MakeInstructionDescriptor(64, SpvOpStore, 0), 203);
+  // Bad: Skip count 100 is too large.
   TransformationConstructComposite make_uvec3_array_length_2_2_bad(
-      58, {33, 54}, 64, 100, 203);
+      58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203);
   ASSERT_TRUE(
       make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(),
@@ -393,9 +398,11 @@ TEST(TransformationConstructCompositeTest, ConstructMatrices) {
   FactManager fact_manager;
 
   // make a mat3x4
-  TransformationConstructComposite make_mat34(32, {25, 28, 31}, 31, 2, 200);
+  TransformationConstructComposite make_mat34(
+      32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
   // Bad: %35 is mat4x3, not mat3x4.
-  TransformationConstructComposite make_mat34_bad(35, {25, 28, 31}, 31, 2, 200);
+  TransformationConstructComposite make_mat34_bad(
+      35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
   ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager));
   make_mat34.Apply(context.get(), &fact_manager);
@@ -405,11 +412,11 @@ TEST(TransformationConstructCompositeTest, ConstructMatrices) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 31, 200, {2}));
 
   // make a mat4x3
-  TransformationConstructComposite make_mat43(35, {11, 13, 16, 100}, 31, 1,
-                                              201);
+  TransformationConstructComposite make_mat43(
+      35, {11, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
   // Bad: %25 does not match the matrix's column type.
-  TransformationConstructComposite make_mat43_bad(35, {25, 13, 16, 100}, 31, 1,
-                                                  201);
+  TransformationConstructComposite make_mat43_bad(
+      35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
   ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager));
   make_mat43.Apply(context.get(), &fact_manager);
@@ -592,9 +599,11 @@ TEST(TransformationConstructCompositeTest, ConstructStructs) {
   FactManager fact_manager;
 
   // make an Inner
-  TransformationConstructComposite make_inner(9, {25, 19}, 57, 0, 200);
+  TransformationConstructComposite make_inner(
+      9, {25, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
   // Bad: Too few fields to make the struct.
-  TransformationConstructComposite make_inner_bad(9, {25}, 57, 0, 200);
+  TransformationConstructComposite make_inner_bad(
+      9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
   ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager));
   make_inner.Apply(context.get(), &fact_manager);
@@ -603,10 +612,13 @@ TEST(TransformationConstructCompositeTest, ConstructStructs) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 19, 200, {1}));
 
   // make an Outer
-  TransformationConstructComposite make_outer(33, {46, 200, 56}, 200, 1, 201);
+  TransformationConstructComposite make_outer(
+      33, {46, 200, 56}, MakeInstructionDescriptor(200, SpvOpAccessChain, 0),
+      201);
   // Bad: %200 is not available at the desired program point.
-  TransformationConstructComposite make_outer_bad(33, {46, 200, 56}, 200, 0,
-                                                  201);
+  TransformationConstructComposite make_outer_bad(
+      33, {46, 200, 56},
+      MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201);
   ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager));
   make_outer.Apply(context.get(), &fact_manager);
@@ -900,9 +912,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
 
   FactManager fact_manager;
 
-  TransformationConstructComposite make_vec2(7, {17, 11}, 100, 1, 200);
+  TransformationConstructComposite make_vec2(
+      7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
   // Bad: not enough data for a vec2
-  TransformationConstructComposite make_vec2_bad(7, {11}, 100, 1, 200);
+  TransformationConstructComposite make_vec2_bad(
+      7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
   ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager));
   make_vec2.Apply(context.get(), &fact_manager);
@@ -910,9 +924,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 17, 200, {0}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 200, {1}));
 
-  TransformationConstructComposite make_vec3(25, {12, 32}, 35, 0, 201);
+  TransformationConstructComposite make_vec3(
+      25, {12, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0),
+      201);
   // Bad: too much data for a vec3
-  TransformationConstructComposite make_vec3_bad(25, {12, 32, 32}, 35, 0, 201);
+  TransformationConstructComposite make_vec3_bad(
+      25, {12, 32, 32},
+      MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201);
   ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_vec3_bad.IsApplicable(context.get(), fact_manager));
   make_vec3.Apply(context.get(), &fact_manager);
@@ -920,10 +938,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 12, 201, {0}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 201, {2}));
 
-  TransformationConstructComposite make_vec4(44, {32, 32, 10, 11}, 75, 0, 202);
+  TransformationConstructComposite make_vec4(
+      44, {32, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
+      202);
   // Bad: id 48 is not available at the insertion points
-  TransformationConstructComposite make_vec4_bad(44, {48, 32, 10, 11}, 75, 0,
-                                                 202);
+  TransformationConstructComposite make_vec4_bad(
+      44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
+      202);
   ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_vec4_bad.IsApplicable(context.get(), fact_manager));
   make_vec4.Apply(context.get(), &fact_manager);
@@ -933,9 +954,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 10, 202, {2}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 202, {3}));
 
-  TransformationConstructComposite make_ivec2(51, {126, 120}, 128, 0, 203);
+  TransformationConstructComposite make_ivec2(
+      51, {126, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
   // Bad: if 128 is not available at the instruction that defines 128
-  TransformationConstructComposite make_ivec2_bad(51, {128, 120}, 128, 0, 203);
+  TransformationConstructComposite make_ivec2_bad(
+      51, {128, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
   ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager));
   make_ivec2.Apply(context.get(), &fact_manager);
@@ -943,10 +966,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 126, 203, {0}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 120, 203, {1}));
 
-  TransformationConstructComposite make_ivec3(114, {56, 117, 56}, 66, 1, 204);
+  TransformationConstructComposite make_ivec3(
+      114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
+      204);
   // Bad because 1300 is not an id
-  TransformationConstructComposite make_ivec3_bad(114, {56, 117, 1300}, 66, 1,
-                                                  204);
+  TransformationConstructComposite make_ivec3_bad(
+      114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
+      204);
   ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_ivec3_bad.IsApplicable(context.get(), fact_manager));
   make_ivec3.Apply(context.get(), &fact_manager);
@@ -955,11 +981,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 204, {1}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 204, {2}));
 
-  TransformationConstructComposite make_ivec4(122, {56, 117, 117, 117}, 66, 0,
-                                              205);
+  TransformationConstructComposite make_ivec4(
+      122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
+      205);
   // Bad because 86 is the wrong type.
-  TransformationConstructComposite make_ivec4_bad(86, {56, 117, 117, 117}, 66,
-                                                  0, 205);
+  TransformationConstructComposite make_ivec4_bad(
+      86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
+      205);
   ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_ivec4_bad.IsApplicable(context.get(), fact_manager));
   make_ivec4.Apply(context.get(), &fact_manager);
@@ -969,8 +997,10 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {2}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {3}));
 
-  TransformationConstructComposite make_uvec2(86, {18, 38}, 133, 2, 206);
-  TransformationConstructComposite make_uvec2_bad(86, {18, 38}, 133, 200, 206);
+  TransformationConstructComposite make_uvec2(
+      86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206);
+  TransformationConstructComposite make_uvec2_bad(
+      86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206);
   ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager));
   make_uvec2.Apply(context.get(), &fact_manager);
@@ -978,10 +1008,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 206, {0}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 38, 206, {1}));
 
-  TransformationConstructComposite make_uvec3(59, {14, 18, 136}, 137, 2, 207);
+  TransformationConstructComposite make_uvec3(
+      59, {14, 18, 136}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
   // Bad because 1300 is not an id
-  TransformationConstructComposite make_uvec3_bad(59, {14, 18, 1300}, 137, 2,
-                                                  207);
+  TransformationConstructComposite make_uvec3_bad(
+      59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
   ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager));
   make_uvec3.Apply(context.get(), &fact_manager);
@@ -990,11 +1021,13 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 207, {1}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 207, {2}));
 
-  TransformationConstructComposite make_uvec4(131, {14, 18, 136, 136}, 137, 0,
-                                              208);
+  TransformationConstructComposite make_uvec4(
+      131, {14, 18, 136, 136},
+      MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
   // Bad because 86 is the wrong type.
-  TransformationConstructComposite make_uvec4_bad(86, {14, 18, 136, 136}, 137,
-                                                  0, 208);
+  TransformationConstructComposite make_uvec4_bad(
+      86, {14, 18, 136, 136},
+      MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
   ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_uvec4_bad.IsApplicable(context.get(), fact_manager));
   make_uvec4.Apply(context.get(), &fact_manager);
@@ -1004,19 +1037,21 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {2}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {3}));
 
-  TransformationConstructComposite make_bvec2(102,
-                                              {
-                                                  111,
-                                                  41,
-                                              },
-                                              75, 0, 209);
+  TransformationConstructComposite make_bvec2(
+      102,
+      {
+          111,
+          41,
+      },
+      MakeInstructionDescriptor(75, SpvOpAccessChain, 0), 209);
   // Bad because 0 is not a valid base instruction id
-  TransformationConstructComposite make_bvec2_bad(102,
-                                                  {
-                                                      111,
-                                                      41,
-                                                  },
-                                                  0, 0, 209);
+  TransformationConstructComposite make_bvec2_bad(
+      102,
+      {
+          111,
+          41,
+      },
+      MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209);
   ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_bvec2_bad.IsApplicable(context.get(), fact_manager));
   make_bvec2.Apply(context.get(), &fact_manager);
@@ -1024,9 +1059,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 111, 209, {0}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 41, 209, {1}));
 
-  TransformationConstructComposite make_bvec3(93, {108, 73}, 108, 1, 210);
+  TransformationConstructComposite make_bvec3(
+      93, {108, 73}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
   // Bad because there are too many components for a bvec3
-  TransformationConstructComposite make_bvec3_bad(93, {108, 108}, 108, 1, 210);
+  TransformationConstructComposite make_bvec3_bad(
+      93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
   ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager));
   make_bvec3.Apply(context.get(), &fact_manager);
@@ -1034,9 +1071,11 @@ TEST(TransformationConstructCompositeTest, ConstructVectors) {
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 210, {0}));
   ASSERT_TRUE(SynonymFactHolds(fact_manager, 73, 210, {2}));
 
-  TransformationConstructComposite make_bvec4(70, {108, 108}, 108, 3, 211);
+  TransformationConstructComposite make_bvec4(
+      70, {108, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
   // Bad because 21 is a type, not a result id
-  TransformationConstructComposite make_bvec4_bad(70, {21, 108}, 108, 3, 211);
+  TransformationConstructComposite make_bvec4_bad(
+      70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
   ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager));
   make_bvec4.Apply(context.get(), &fact_manager);

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

@@ -14,6 +14,7 @@
 
 #include "source/fuzz/transformation_copy_object.h"
 #include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -49,7 +50,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
 
   ASSERT_EQ(0, fact_manager.GetIdsForWhichSynonymsAreKnown().size());
 
-  TransformationCopyObject copy_true(7, 5, 1, 100);
+  TransformationCopyObject copy_true(
+      7, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
   ASSERT_TRUE(copy_true.IsApplicable(context.get(), fact_manager));
   copy_true.Apply(context.get(), &fact_manager);
 
@@ -63,7 +65,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
   ASSERT_TRUE(DataDescriptorEquals()(&descriptor_100,
                                      &fact_manager.GetSynonymsForId(7)[0]));
 
-  TransformationCopyObject copy_false(8, 100, 1, 101);
+  TransformationCopyObject copy_false(
+      8, MakeInstructionDescriptor(100, SpvOpReturn, 0), 101);
   ASSERT_TRUE(copy_false.IsApplicable(context.get(), fact_manager));
   copy_false.Apply(context.get(), &fact_manager);
   ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
@@ -74,7 +77,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
   ASSERT_TRUE(DataDescriptorEquals()(&descriptor_101,
                                      &fact_manager.GetSynonymsForId(8)[0]));
 
-  TransformationCopyObject copy_false_again(101, 5, 3, 102);
+  TransformationCopyObject copy_false_again(
+      101, MakeInstructionDescriptor(5, SpvOpReturn, 0), 102);
   ASSERT_TRUE(copy_false_again.IsApplicable(context.get(), fact_manager));
   copy_false_again.Apply(context.get(), &fact_manager);
   ASSERT_EQ(3, ids_for_which_synonyms_are_known.size());
@@ -85,7 +89,8 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
   ASSERT_TRUE(DataDescriptorEquals()(&descriptor_102,
                                      &fact_manager.GetSynonymsForId(101)[0]));
 
-  TransformationCopyObject copy_true_again(7, 102, 1, 103);
+  TransformationCopyObject copy_true_again(
+      7, MakeInstructionDescriptor(102, SpvOpReturn, 0), 103);
   ASSERT_TRUE(copy_true_again.IsApplicable(context.get(), fact_manager));
   copy_true_again.Apply(context.get(), &fact_manager);
   // This does re-uses an id for which synonyms are already known, so the count
@@ -318,90 +323,113 @@ TEST(TransformationCopyObjectTest, CheckIllegalCases) {
   FactManager fact_manager;
 
   // Inapplicable because %18 is decorated.
-  ASSERT_FALSE(TransformationCopyObject(18, 21, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   18, MakeInstructionDescriptor(21, SpvOpAccessChain, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because %77 is decorated.
-  ASSERT_FALSE(TransformationCopyObject(17, 17, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   77, MakeInstructionDescriptor(77, SpvOpBranch, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because %80 is decorated.
-  ASSERT_FALSE(TransformationCopyObject(80, 77, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   80, MakeInstructionDescriptor(77, SpvOpIAdd, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because %84 is not available at the requested point
-  ASSERT_FALSE(TransformationCopyObject(84, 32, 1, 200)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationCopyObject(
+          84, MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0), 200)
+          .IsApplicable(context.get(), fact_manager));
 
   // Fine because %84 is available at the requested point
-  ASSERT_TRUE(TransformationCopyObject(84, 32, 2, 200)
-                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationCopyObject(
+          84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 200)
+          .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because id %9 is already in use
-  ASSERT_FALSE(TransformationCopyObject(84, 32, 2, 9)
-                   .IsApplicable(context.get(), fact_manager));
-
-  // Inapplicable because the requested point is not in a block
-  ASSERT_FALSE(TransformationCopyObject(84, 86, 3, 200)
+  ASSERT_FALSE(
+      TransformationCopyObject(
+          84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 9)
+          .IsApplicable(context.get(), fact_manager));
+
+  // Inapplicable because the requested point does not exist
+  ASSERT_FALSE(TransformationCopyObject(
+                   84, MakeInstructionDescriptor(86, SpvOpReturn, 2), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because %9 is not in a function
-  ASSERT_FALSE(TransformationCopyObject(9, 9, 1, 200)
-                   .IsApplicable(context.get(), fact_manager));
-
-  // Inapplicable because %9 is not in a function
-  ASSERT_FALSE(TransformationCopyObject(9, 9, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(9, SpvOpTypeInt, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the insert point is right before, or inside, a chunk
   // of OpPhis
-  ASSERT_FALSE(TransformationCopyObject(9, 30, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(30, SpvOpPhi, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationCopyObject(9, 99, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(99, SpvOpPhi, 1), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // OK, because the insert point is just after a chunk of OpPhis.
-  ASSERT_TRUE(TransformationCopyObject(9, 96, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(96, SpvOpAccessChain, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the insert point is right after an OpSelectionMerge
-  ASSERT_FALSE(TransformationCopyObject(9, 58, 2, 200)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationCopyObject(
+          9, MakeInstructionDescriptor(58, SpvOpBranchConditional, 0), 200)
+          .IsApplicable(context.get(), fact_manager));
 
   // OK, because the insert point is right before the OpSelectionMerge
-  ASSERT_TRUE(TransformationCopyObject(9, 58, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the insert point is right after an OpSelectionMerge
-  ASSERT_FALSE(TransformationCopyObject(9, 43, 2, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(43, SpvOpSwitch, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // OK, because the insert point is right before the OpSelectionMerge
-  ASSERT_TRUE(TransformationCopyObject(9, 43, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the insert point is right after an OpLoopMerge
-  ASSERT_FALSE(TransformationCopyObject(9, 40, 2, 200)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationCopyObject(
+          9, MakeInstructionDescriptor(40, SpvOpBranchConditional, 0), 200)
+          .IsApplicable(context.get(), fact_manager));
 
   // OK, because the insert point is right before the OpLoopMerge
-  ASSERT_TRUE(TransformationCopyObject(9, 40, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because id %300 does not exist
-  ASSERT_FALSE(TransformationCopyObject(300, 40, 1, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   300, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // Inapplicable because the following instruction is OpVariable
-  ASSERT_FALSE(TransformationCopyObject(9, 180, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(180, SpvOpVariable, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationCopyObject(9, 181, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationCopyObject(9, 182, 0, 200)
+  ASSERT_FALSE(TransformationCopyObject(
+                   9, MakeInstructionDescriptor(182, SpvOpVariable, 0), 200)
                    .IsApplicable(context.get(), fact_manager));
 
   // OK, because this is just past the group of OpVariable instructions.
-  ASSERT_TRUE(TransformationCopyObject(9, 182, 1, 200)
+  ASSERT_TRUE(TransformationCopyObject(
+                  9, MakeInstructionDescriptor(182, SpvOpAccessChain, 0), 200)
                   .IsApplicable(context.get(), fact_manager));
 }
 
@@ -470,13 +498,20 @@ TEST(TransformationCopyObjectTest, MiscellaneousCopies) {
   FactManager fact_manager;
 
   std::vector<TransformationCopyObject> transformations = {
-      TransformationCopyObject(19, 22, 1, 100),
-      TransformationCopyObject(22, 22, 1, 101),
-      TransformationCopyObject(12, 22, 1, 102),
-      TransformationCopyObject(11, 22, 1, 103),
-      TransformationCopyObject(16, 22, 1, 104),
-      TransformationCopyObject(8, 22, 1, 105),
-      TransformationCopyObject(17, 22, 1, 106)};
+      TransformationCopyObject(19, MakeInstructionDescriptor(22, SpvOpStore, 0),
+                               100),
+      TransformationCopyObject(
+          22, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 101),
+      TransformationCopyObject(
+          12, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 102),
+      TransformationCopyObject(
+          11, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 103),
+      TransformationCopyObject(
+          16, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 104),
+      TransformationCopyObject(
+          8, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 105),
+      TransformationCopyObject(
+          17, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 106)};
 
   for (auto& transformation : transformations) {
     ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));

+ 76 - 46
3rdparty/spirv-tools/test/fuzz/transformation_split_block_test.cpp

@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_split_block.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -90,44 +91,55 @@ TEST(TransformationSplitBlockTest, NotApplicable) {
   FactManager fact_manager;
 
   // No split before OpVariable
-  ASSERT_FALSE(TransformationSplitBlock(8, 0, 100).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(8, 1, 100).IsApplicable(context.get(),
-                                                                fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(8, SpvOpVariable, 0), 100)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(8, SpvOpVariable, 1), 100)
+                   .IsApplicable(context.get(), fact_manager));
 
   // No split before OpLabel
-  ASSERT_FALSE(TransformationSplitBlock(14, 0, 100)
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(14, SpvOpLabel, 0), 100)
                    .IsApplicable(context.get(), fact_manager));
 
   // No split if base instruction is outside a function
-  ASSERT_FALSE(TransformationSplitBlock(1, 0, 100).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(1, 4, 100).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(1, 35, 100)
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(1, SpvOpLabel, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(1, SpvOpExecutionMode, 0), 100)
                    .IsApplicable(context.get(), fact_manager));
 
   // No split if block is loop header
-  ASSERT_FALSE(TransformationSplitBlock(27, 0, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(27, 1, 100)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
+          .IsApplicable(context.get(), fact_manager));
 
   // No split if base instruction does not exist
-  ASSERT_FALSE(TransformationSplitBlock(88, 0, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(88, 22, 100)
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(88, SpvOpIAdd, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(88, SpvOpIMul, 22), 100)
                    .IsApplicable(context.get(), fact_manager));
 
-  // No split if offset is too large (goes into another block)
-  ASSERT_FALSE(TransformationSplitBlock(18, 3, 100)
-                   .IsApplicable(context.get(), fact_manager));
+  // No split if too many instructions with the desired opcode are skipped
+  ASSERT_FALSE(
+      TransformationSplitBlock(
+          MakeInstructionDescriptor(18, SpvOpBranchConditional, 1), 100)
+          .IsApplicable(context.get(), fact_manager));
 
   // No split if id in use
-  ASSERT_FALSE(TransformationSplitBlock(18, 0, 27).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(18, 0, 14).IsApplicable(context.get(),
-                                                                fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 27)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 14)
+                   .IsApplicable(context.get(), fact_manager));
 }
 
 TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
@@ -188,7 +200,8 @@ TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
 
   FactManager fact_manager;
 
-  auto split_1 = TransformationSplitBlock(5, 3, 100);
+  auto split_1 = TransformationSplitBlock(
+      MakeInstructionDescriptor(5, SpvOpStore, 0), 100);
   ASSERT_TRUE(split_1.IsApplicable(context.get(), fact_manager));
   split_1.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -235,7 +248,8 @@ TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
   )";
   ASSERT_TRUE(IsEqual(env, after_split_1, context.get()));
 
-  auto split_2 = TransformationSplitBlock(11, 1, 101);
+  auto split_2 = TransformationSplitBlock(
+      MakeInstructionDescriptor(11, SpvOpStore, 0), 101);
   ASSERT_TRUE(split_2.IsApplicable(context.get(), fact_manager));
   split_2.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -284,7 +298,8 @@ TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
   )";
   ASSERT_TRUE(IsEqual(env, after_split_2, context.get()));
 
-  auto split_3 = TransformationSplitBlock(14, 0, 102);
+  auto split_3 = TransformationSplitBlock(
+      MakeInstructionDescriptor(14, SpvOpLoad, 0), 102);
   ASSERT_TRUE(split_3.IsApplicable(context.get(), fact_manager));
   split_3.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -399,12 +414,17 @@ TEST(TransformationSplitBlockTest, SplitBlockBeforeSelectBranch) {
   FactManager fact_manager;
 
   // Illegal to split between the merge and the conditional branch.
-  ASSERT_FALSE(TransformationSplitBlock(14, 2, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(12, 3, 100)
-                   .IsApplicable(context.get(), fact_manager));
-
-  auto split = TransformationSplitBlock(14, 1, 100);
+  ASSERT_FALSE(
+      TransformationSplitBlock(
+          MakeInstructionDescriptor(14, SpvOpBranchConditional, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(
+          MakeInstructionDescriptor(12, SpvOpBranchConditional, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+
+  auto split = TransformationSplitBlock(
+      MakeInstructionDescriptor(14, SpvOpSelectionMerge, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
   split.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -523,12 +543,15 @@ TEST(TransformationSplitBlockTest, SplitBlockBeforeSwitchBranch) {
   FactManager fact_manager;
 
   // Illegal to split between the merge and the conditional branch.
-  ASSERT_FALSE(TransformationSplitBlock(9, 2, 100).IsApplicable(context.get(),
-                                                                fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(15, 3, 100)
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(9, SpvOpSwitch, 0), 100)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSplitBlock(
+                   MakeInstructionDescriptor(15, SpvOpSwitch, 0), 100)
                    .IsApplicable(context.get(), fact_manager));
 
-  auto split = TransformationSplitBlock(9, 1, 100);
+  auto split = TransformationSplitBlock(
+      MakeInstructionDescriptor(9, SpvOpSelectionMerge, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
   split.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
@@ -654,12 +677,15 @@ TEST(TransformationSplitBlockTest, NoSplitDuringOpPhis) {
 
   // We cannot split before OpPhi instructions, since the number of incoming
   // blocks may not appropriately match after splitting.
-  ASSERT_FALSE(TransformationSplitBlock(26, 0, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(27, 0, 100)
-                   .IsApplicable(context.get(), fact_manager));
-  ASSERT_FALSE(TransformationSplitBlock(27, 1, 100)
-                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(26, SpvOpPhi, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
+          .IsApplicable(context.get(), fact_manager));
 }
 
 TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
@@ -701,9 +727,13 @@ TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
 
   FactManager fact_manager;
 
-  ASSERT_TRUE(TransformationSplitBlock(21, 0, 100)
-                  .IsApplicable(context.get(), fact_manager));
-  auto split = TransformationSplitBlock(20, 1, 100);
+  ASSERT_TRUE(
+      TransformationSplitBlock(MakeInstructionDescriptor(21, SpvOpPhi, 0), 100)
+          .IsApplicable(context.get(), fact_manager));
+  // An equivalent transformation to the above, just described with respect to a
+  // different base instruction.
+  auto split =
+      TransformationSplitBlock(MakeInstructionDescriptor(20, SpvOpPhi, 0), 100);
   ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
   split.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));

+ 32 - 0
3rdparty/spirv-tools/test/fuzzers/BUILD.gn

@@ -33,7 +33,9 @@ if (!build_with_chromium || use_fuzzing_engine) {
     testonly = true
 
     deps = [
+      ":spvtools_as_fuzzer",
       ":spvtools_binary_parser_fuzzer",
+      ":spvtools_dis_fuzzer",
       ":spvtools_opt_legalization_fuzzer",
       ":spvtools_opt_performance_fuzzer",
       ":spvtools_opt_size_fuzzer",
@@ -66,12 +68,24 @@ template("spvtools_fuzzer") {
   }
 }
 
+spvtools_fuzzer("spvtools_as_fuzzer_src") {
+  sources = [
+    "spvtools_as_fuzzer.cpp",
+  ]
+}
+
 spvtools_fuzzer("spvtools_binary_parser_fuzzer_src") {
   sources = [
     "spvtools_binary_parser_fuzzer.cpp",
   ]
 }
 
+spvtools_fuzzer("spvtools_dis_fuzzer_src") {
+  sources = [
+    "spvtools_dis_fuzzer.cpp",
+  ]
+}
+
 spvtools_fuzzer("spvtools_opt_performance_fuzzer_src") {
   sources = [
     "spvtools_opt_performance_fuzzer.cpp",
@@ -116,6 +130,15 @@ spvtools_fuzzer("spvtools_val_webgpu_fuzzer_src") {
 }
 
 if (!build_with_chromium || use_fuzzing_engine) {
+  fuzzer_test("spvtools_as_fuzzer") {
+    sources = []
+    deps = [
+      ":spvtools_as_fuzzer_src",
+    ]
+    # Intentionally doesn't use the seed corpus, because it consumes
+    #  part of the input as not part of the file.
+  }
+
   fuzzer_test("spvtools_binary_parser_fuzzer") {
     sources = []
     deps = [
@@ -125,6 +148,15 @@ if (!build_with_chromium || use_fuzzing_engine) {
     #  part of the input as not part of the file.
   }
 
+  fuzzer_test("spvtools_dis_fuzzer") {
+    sources = []
+    deps = [
+      ":spvtools_dis_fuzzer_src",
+    ]
+    # Intentionally doesn't use the seed corpus, because it consumes
+    #  part of the input as not part of the file.
+  }
+
   fuzzer_test("spvtools_opt_performance_fuzzer") {
     sources = []
     deps = [

+ 70 - 0
3rdparty/spirv-tools/test/fuzzers/spvtools_as_fuzzer.cpp

@@ -0,0 +1,70 @@
+// Copyright (c) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+#include <cstring>  // memcpy
+#include <vector>
+
+#include "source/spirv_target_env.h"
+#include "spirv-tools/libspirv.hpp"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < sizeof(spv_target_env) + 1) return 0;
+
+  const spv_context context =
+      spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
+  if (context == nullptr) return 0;
+
+  data += sizeof(spv_target_env);
+  size -= sizeof(spv_target_env);
+
+  std::vector<uint32_t> input;
+
+  std::vector<char> input_str;
+  size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char);
+  input_str.resize(char_count);
+  memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t));
+
+  spv_binary binary;
+  spv_diagnostic diagnostic = nullptr;
+  spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
+                             SPV_TEXT_TO_BINARY_OPTION_NONE, &binary,
+                             &diagnostic);
+  if (diagnostic) {
+    spvDiagnosticPrint(diagnostic);
+    spvDiagnosticDestroy(diagnostic);
+    diagnostic = nullptr;
+  }
+
+  if (binary) {
+    spvBinaryDestroy(binary);
+    binary = nullptr;
+  }
+
+  spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
+                             SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS,
+                             &binary, &diagnostic);
+  if (diagnostic) {
+    spvDiagnosticPrint(diagnostic);
+    spvDiagnosticDestroy(diagnostic);
+    diagnostic = nullptr;
+  }
+
+  if (binary) {
+    spvBinaryDestroy(binary);
+    binary = nullptr;
+  }
+
+  return 0;
+}

+ 64 - 0
3rdparty/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp

@@ -0,0 +1,64 @@
+// Copyright (c) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+#include <cstring>  // memcpy
+#include <vector>
+
+#include "source/spirv_target_env.h"
+#include "spirv-tools/libspirv.hpp"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < sizeof(spv_target_env) + 1) return 0;
+
+  const spv_context context =
+      spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
+  if (context == nullptr) return 0;
+
+  data += sizeof(spv_target_env);
+  size -= sizeof(spv_target_env);
+
+  std::vector<uint32_t> input;
+  input.resize(size >> 2);
+  size_t count = 0;
+  for (size_t i = 0; (i + 3) < size; i += 4) {
+    input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
+                     (data[i + 3]) << 24;
+  }
+
+  std::vector<char> input_str;
+  size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char);
+  input_str.resize(char_count);
+  memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t));
+
+  spv_text text = nullptr;
+  spv_diagnostic diagnostic = nullptr;
+
+  for (uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE;
+       options <
+       (SPV_BINARY_TO_TEXT_OPTION_PRINT | SPV_BINARY_TO_TEXT_OPTION_COLOR |
+        SPV_BINARY_TO_TEXT_OPTION_INDENT |
+        SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET |
+        SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+       options++) {
+    spvBinaryToText(context, input.data(), input.size(), options, &text,
+                    &diagnostic);
+    if (diagnostic) spvDiagnosticDestroy(diagnostic);
+    if (text) spvTextDestroy(text);
+  }
+
+  spvContextDestroy(context);
+  return 0;
+}

+ 120 - 10
3rdparty/spirv-tools/test/opt/fold_spec_const_op_composite_test.cpp

@@ -112,8 +112,12 @@ std::vector<std::string> CommonTypesAndConstants() {
       // clang-format off
       // scalar types
       "%bool = OpTypeBool",
+      "%ushort = OpTypeInt 16 0",
+      "%short = OpTypeInt 16 1",
       "%uint = OpTypeInt 32 0",
       "%int = OpTypeInt 32 1",
+      "%ulong = OpTypeInt 64 0",
+      "%long = OpTypeInt 64 1",
       "%float = OpTypeFloat 32",
       "%double = OpTypeFloat 64",
       // vector types
@@ -122,6 +126,8 @@ std::vector<std::string> CommonTypesAndConstants() {
       "%v2int = OpTypeVector %int 2",
       "%v3int = OpTypeVector %int 3",
       "%v4int = OpTypeVector %int 4",
+      "%v2long = OpTypeVector %long 2",
+      "%v2ulong = OpTypeVector %ulong 2",
       "%v2float = OpTypeVector %float 2",
       "%v2double = OpTypeVector %double 2",
       // variable pointer types
@@ -145,6 +151,8 @@ std::vector<std::string> CommonTypesAndConstants() {
       "%bool_null = OpConstantNull %bool",
       "%signed_zero = OpConstant %int 0",
       "%unsigned_zero = OpConstant %uint 0",
+      "%long_zero = OpConstant %long 0",
+      "%ulong_zero = OpConstant %ulong 0",
       "%signed_one = OpConstant %int 1",
       "%unsigned_one = OpConstant %uint 1",
       "%signed_two = OpConstant %int 2",
@@ -153,6 +161,7 @@ std::vector<std::string> CommonTypesAndConstants() {
       "%unsigned_three = OpConstant %uint 3",
       "%signed_null = OpConstantNull %int",
       "%unsigned_null = OpConstantNull %uint",
+      "%signed_minus_one = OpConstant %int -1",
       // vector constants:
       "%bool_true_vec = OpConstantComposite %v2bool %bool_true %bool_true",
       "%bool_false_vec = OpConstantComposite %v2bool %bool_false %bool_false",
@@ -167,6 +176,7 @@ std::vector<std::string> CommonTypesAndConstants() {
       "%unsigned_three_vec = OpConstantComposite %v2uint %unsigned_three %unsigned_three",
       "%signed_null_vec = OpConstantNull %v2int",
       "%unsigned_null_vec = OpConstantNull %v2uint",
+      "%signed_minus_one_vec = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
       "%v4int_0_1_2_3 = OpConstantComposite %v4int %signed_zero %signed_one %signed_two %signed_three",
       // clang-format on
   };
@@ -345,9 +355,9 @@ INSTANTIATE_TEST_SUITE_P(
 // Tests for operations that resulting in different types.
 INSTANTIATE_TEST_SUITE_P(
     Cast, FoldSpecConstantOpAndCompositePassTest,
-    ::testing::ValuesIn(
-        std::vector<FoldSpecConstantOpAndCompositePassTestCase>({
-            // clang-format off
+    ::testing::ValuesIn(std::vector<
+                        FoldSpecConstantOpAndCompositePassTestCase>({
+        // clang-format off
             // int -> bool scalar
             {
               // original
@@ -575,8 +585,108 @@ INSTANTIATE_TEST_SUITE_P(
                 "%spec_uint_from_null = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero",
               },
             },
-            // clang-format on
-        })));
+
+            // UConvert scalar
+            {
+              // original
+              {
+                "%spec_uint_zero = OpSpecConstantOp %uint UConvert %bool_false",
+                "%spec_uint_one = OpSpecConstantOp %uint UConvert %bool_true",
+                "%spec_ulong_zero = OpSpecConstantOp %ulong UConvert %unsigned_zero",
+                "%spec_ulong_one = OpSpecConstantOp %ulong UConvert %unsigned_one",
+                "%spec_short_zero = OpSpecConstantOp %ushort UConvert %unsigned_zero",
+                "%spec_short_one = OpSpecConstantOp %ushort UConvert %unsigned_one",
+                "%uint_max = OpConstant %uint 4294967295",
+                "%spec_ushort_max = OpSpecConstantOp %ushort UConvert %uint_max",
+                "%uint_0xDDDDDDDD = OpConstant %uint 3722304989",
+                "%spec_ushort_0xDDDD = OpSpecConstantOp %ushort UConvert %uint_0xDDDDDDDD",
+              },
+              // expected
+              {
+                "%spec_uint_zero = OpConstant %uint 0",
+                "%spec_uint_one = OpConstant %uint 1",
+                "%spec_ulong_zero = OpConstant %ulong 0",
+                "%spec_ulong_one = OpConstant %ulong 1",
+                "%spec_short_zero = OpConstant %ushort 0",
+                "%spec_short_one = OpConstant %ushort 1",
+                "%uint_max = OpConstant %uint 4294967295",
+                "%spec_ushort_max = OpConstant %ushort 65535",
+                "%uint_0xDDDDDDDD = OpConstant %uint 3722304989",
+                "%spec_ushort_0xDDDD = OpConstant %ushort 56797",
+              },
+            },
+
+            // SConvert scalar
+            {
+              // original
+              {
+                "%spec_long_zero = OpSpecConstantOp %long SConvert %signed_zero",
+                "%spec_long_one = OpSpecConstantOp %long SConvert %signed_one",
+                "%spec_long_minus_one = OpSpecConstantOp %long SConvert %signed_minus_one",
+                "%spec_short_minus_one_trunc = OpSpecConstantOp %short SConvert %signed_minus_one",
+                "%int_2_to_17_minus_one = OpConstant %int 131071",
+                "%spec_short_minus_one_trunc2 = OpSpecConstantOp %short SConvert %int_2_to_17_minus_one",
+              },
+              // expected
+              {
+                "%spec_long_zero = OpConstant %long 0",
+                "%spec_long_one = OpConstant %long 1",
+                "%spec_long_minus_one = OpConstant %long -1",
+                "%spec_short_minus_one_trunc = OpConstant %short -1",
+                "%int_2_to_17_minus_one = OpConstant %int 131071",
+                "%spec_short_minus_one_trunc2 = OpConstant %short -1",
+              },
+            },
+
+            // UConvert vector
+            {
+              // original
+              {
+                "%spec_v2uint_zero = OpSpecConstantOp %v2uint UConvert %bool_false_vec",
+                "%spec_v2uint_one = OpSpecConstantOp %v2uint UConvert %bool_true_vec",
+                "%spec_v2ulong_zero = OpSpecConstantOp %v2ulong UConvert %unsigned_zero_vec",
+                "%spec_v2ulong_one = OpSpecConstantOp %v2ulong UConvert %unsigned_one_vec",
+              },
+              // expected
+              {
+                "%uint_0 = OpConstant %uint 0",
+                "%uint_0_0 = OpConstant %uint 0",
+                "%spec_v2uint_zero = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero",
+                "%uint_1 = OpConstant %uint 1",
+                "%uint_1_0 = OpConstant %uint 1",
+                "%spec_v2uint_one = OpConstantComposite %v2uint %unsigned_one %unsigned_one",
+                "%ulong_0 = OpConstant %ulong 0",
+                "%ulong_0_0 = OpConstant %ulong 0",
+                "%spec_v2ulong_zero = OpConstantComposite %v2ulong %ulong_zero %ulong_zero",
+                "%ulong_1 = OpConstant %ulong 1",
+                "%ulong_1_0 = OpConstant %ulong 1",
+                "%spec_v2ulong_one = OpConstantComposite %v2ulong %ulong_1 %ulong_1",
+              },
+            },
+
+            // SConvert vector
+            {
+              // original
+              {
+                "%spec_v2long_zero = OpSpecConstantOp %v2long SConvert %signed_zero_vec",
+                "%spec_v2long_one = OpSpecConstantOp %v2long SConvert %signed_one_vec",
+                "%spec_v2long_minus_one = OpSpecConstantOp %v2long SConvert %signed_minus_one_vec",
+              },
+              // expected
+              {
+                "%long_0 = OpConstant %long 0",
+                "%long_0_0 = OpConstant %long 0",
+                "%spec_v2long_zero = OpConstantComposite %v2long %long_zero %long_zero",
+                "%long_1 = OpConstant %long 1",
+                "%long_1_0 = OpConstant %long 1",
+                "%spec_v2long_one = OpConstantComposite %v2long %long_1 %long_1",
+                "%long_n1 = OpConstant %long -1",
+                "%long_n1_0 = OpConstant %long -1",
+                "%spec_v2long_minus_one = OpConstantComposite %v2long %long_n1 %long_n1",
+              },
+            },
+        // clang-format on
+    })));
 
 // Tests about boolean scalar logical operations and comparison operations with
 // scalar int/uint type.
@@ -851,7 +961,7 @@ INSTANTIATE_TEST_SUITE_P(
               {
                 "%int_n1 = OpConstant %int -1",
                 "%int_n1_0 = OpConstant %int -1",
-                "%v2int_minus_1 = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%v2int_minus_1 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
                 "%int_n2 = OpConstant %int -2",
                 "%int_n2_0 = OpConstant %int -2",
                 "%v2int_minus_2 = OpConstantComposite %v2int %int_n2 %int_n2",
@@ -956,13 +1066,13 @@ INSTANTIATE_TEST_SUITE_P(
                 "%7_srem_3 = OpConstantComposite %v2int %signed_one %signed_one",
                 "%int_n1 = OpConstant %int -1",
                 "%int_n1_0 = OpConstant %int -1",
-                "%minus_7_srem_3 = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%minus_7_srem_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
                 "%int_1_1 = OpConstant %int 1",
                 "%int_1_2 = OpConstant %int 1",
                 "%7_srem_minus_3 = OpConstantComposite %v2int %signed_one %signed_one",
                 "%int_n1_1 = OpConstant %int -1",
                 "%int_n1_2 = OpConstant %int -1",
-                "%minus_7_srem_minus_3 = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%minus_7_srem_minus_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
                 // smod
                 "%int_1_3 = OpConstant %int 1",
                 "%int_1_4 = OpConstant %int 1",
@@ -975,7 +1085,7 @@ INSTANTIATE_TEST_SUITE_P(
                 "%7_smod_minus_3 = OpConstantComposite %v2int %int_n2 %int_n2",
                 "%int_n1_3 = OpConstant %int -1",
                 "%int_n1_4 = OpConstant %int -1",
-                "%minus_7_smod_minus_3 = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%minus_7_smod_minus_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
                 // umod
                 "%uint_1 = OpConstant %uint 1",
                 "%uint_1_0 = OpConstant %uint 1",
@@ -1018,7 +1128,7 @@ INSTANTIATE_TEST_SUITE_P(
                 "%unsigned_right_shift_logical = OpConstantComposite %v2uint %unsigned_one %unsigned_one",
                 "%int_n1 = OpConstant %int -1",
                 "%int_n1_0 = OpConstant %int -1",
-                "%signed_right_shift_arithmetic = OpConstantComposite %v2int %int_n1 %int_n1",
+                "%signed_right_shift_arithmetic = OpConstantComposite %v2int %signed_minus_one %signed_minus_one",
               },
             },
             // Skip folding if any vector operands or components of the operands

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

@@ -3333,16 +3333,7 @@ INSTANTIATE_TEST_SUITE_P(CompositeExtractFoldingTest, GeneralInstructionFoldingT
             "%3 = OpCompositeExtract %float %2 4\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        3, 0),
-    // Test case 14: Fold OpCompositeExtract with no indexes.
-    InstructionFoldingCase<uint32_t>(
-        Header() + "%main = OpFunction %void None %void_func\n" +
-            "%main_lab = OpLabel\n" +
-            "%2 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1\n" +
-            "%3 = OpCompositeExtract %v4float %2\n" +
-            "OpReturn\n" +
-            "OpFunctionEnd",
-        3, 2)
+        3, 0)
 ));
 
 INSTANTIATE_TEST_SUITE_P(CompositeConstructFoldingTest, GeneralInstructionFoldingTest,

+ 48 - 0
3rdparty/spirv-tools/test/opt/optimizer_test.cpp

@@ -744,6 +744,54 @@ INSTANTIATE_TEST_SUITE_P(
          // pass
          "compact-ids"}}));
 
+TEST(Optimizer, RemoveNop) {
+  // Test that OpNops are removed even if no optimizations are run.
+  const std::string before = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpNop
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  std::vector<uint32_t> binary;
+  {
+    SpirvTools tools(SPV_ENV_VULKAN_1_1);
+    tools.Assemble(before, &binary);
+  }
+
+  Optimizer opt(SPV_ENV_VULKAN_1_1);
+
+  std::vector<uint32_t> optimized;
+  class ValidatorOptions validator_options;
+  ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
+                      validator_options, true))
+      << before << "\n";
+  std::string disassembly;
+  {
+    SpirvTools tools(SPV_ENV_WEBGPU_0);
+    tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
+  }
+
+  EXPECT_EQ(after, disassembly)
+      << "Was expecting the OpNop to have been removed.";
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools

+ 0 - 28
3rdparty/spirv-tools/test/opt/reduce_load_size_test.cpp

@@ -321,34 +321,6 @@ OpFunctionEnd
   SinglePassRunAndCheck<ReduceLoadSize>(test, test, true, false);
 }
 
-TEST_F(ReduceLoadSizeTest, extract_with_no_index) {
-  const std::string test =
-      R"(
-               OpCapability ImageGatherExtended
-               OpExtension ""
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "P�Ma'" %12 %17
-               OpExecutionMode %4 OriginUpperLeft
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-  %_struct_7 = OpTypeStruct %float %float
-%_ptr_Input__struct_7 = OpTypePointer Input %_struct_7
-%_ptr_Output__struct_7 = OpTypePointer Output %_struct_7
-         %12 = OpVariable %_ptr_Input__struct_7 Input
-         %17 = OpVariable %_ptr_Output__struct_7 Output
-          %4 = OpFunction %void DontInline|Pure|Const %3
-        %245 = OpLabel
-         %13 = OpLoad %_struct_7 %12
-         %33 = OpCompositeExtract %_struct_7 %13
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  auto result = SinglePassRunAndDisassemble<ReduceLoadSize>(test, true, true);
-  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
-}
-
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools

+ 0 - 31
3rdparty/spirv-tools/test/opt/scalar_replacement_test.cpp

@@ -1899,37 +1899,6 @@ TEST_F(ScalarReplacementTest, RelaxedPrecisionMemberDecoration) {
   SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
 }
 
-TEST_F(ScalarReplacementTest, ExtractWithNoIndex) {
-  const std::string text = R"(
-; CHECK: [[var1:%\w+]] = OpVariable %_ptr_Function_float Function
-; CHECK: [[var2:%\w+]] = OpVariable %_ptr_Function_float Function
-; CHECK: [[ld1:%\w+]] = OpLoad %float [[var1]]
-; CHECK: [[ld2:%\w+]] = OpLoad %float [[var2]]
-; CHECK: [[constr:%\w+]] = OpCompositeConstruct {{%\w+}} [[ld2]] [[ld1]]
-; CHECK: OpCompositeExtract {{%\w+}} [[constr]]
-OpCapability Shader
-OpExtension ""
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %1 "main"
-OpExecutionMode %1 OriginUpperLeft
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_struct_5 = OpTypeStruct %float %float
-%_ptr_Private__struct_5 = OpTypePointer Private %_struct_5
-%_ptr_Function__struct_5 = OpTypePointer Function %_struct_5
-%1 = OpFunction %void Inline %3
-%8 = OpLabel
-%9 = OpVariable %_ptr_Function__struct_5 Function
-%10 = OpLoad %_struct_5 %9
-%11 = OpCompositeExtract %_struct_5 %10
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
-}
-
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools

+ 0 - 68
3rdparty/spirv-tools/test/opt/vector_dce_test.cpp

@@ -1158,74 +1158,6 @@ OpFunctionEnd
   SinglePassRunAndCheck<VectorDCE>(text, text, true, true);
 }
 
-TEST_F(VectorDCETest, InsertWithNoIndices) {
-  const std::string text = R"(
-; CHECK: OpEntryPoint Fragment {{%\w+}} "PSMain" [[in1:%\w+]] [[in2:%\w+]] [[out:%\w+]]
-; CHECK: OpFunction
-; CHECK: [[ld:%\w+]] = OpLoad %v4float [[in2]]
-; CHECK: OpStore [[out]] [[ld]]
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %1 "PSMain" %2 %14 %3
-               OpExecutionMode %1 OriginUpperLeft
-       %void = OpTypeVoid
-          %5 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-          %2 = OpVariable %_ptr_Input_v4float Input
-          %14 = OpVariable %_ptr_Input_v4float Input
-          %3 = OpVariable %_ptr_Output_v4float Output
-          %1 = OpFunction %void None %5
-         %10 = OpLabel
-         %13 = OpLoad %v4float %14
-         %11 = OpLoad %v4float %2
-         %12 = OpCompositeInsert %v4float %13 %11
-               OpStore %3 %12
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SinglePassRunAndMatch<VectorDCE>(text, true);
-}
-
-TEST_F(VectorDCETest, ExtractWithNoIndices) {
-  const std::string text = R"(
-; CHECK: OpLoad %float
-; CHECK: [[ld:%\w+]] = OpLoad %v4float
-; CHECK: [[ex1:%\w+]] = OpCompositeExtract %v4float [[ld]]
-; CHECK: [[ex2:%\w+]] = OpCompositeExtract %float [[ex1]] 1
-; CHECK: OpStore {{%\w+}} [[ex2]]
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %1 "PSMain" %2 %14 %3
-               OpExecutionMode %1 OriginUpperLeft
-       %void = OpTypeVoid
-          %5 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%_ptr_Input_float = OpTypePointer Input %float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%_ptr_Output_float = OpTypePointer Output %float
-          %2 = OpVariable %_ptr_Input_v4float Input
-          %14 = OpVariable %_ptr_Input_float Input
-          %3 = OpVariable %_ptr_Output_float Output
-          %1 = OpFunction %void None %5
-         %10 = OpLabel
-         %13 = OpLoad %float %14
-         %11 = OpLoad %v4float %2
-         %12 = OpCompositeInsert %v4float %13 %11 0
-         %20 = OpCompositeExtract %v4float %12
-         %21 = OpCompositeExtract %float %20 1
-               OpStore %3 %21
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SinglePassRunAndMatch<VectorDCE>(text, true);
-}
-
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools

+ 1 - 0
3rdparty/spirv-tools/test/util/CMakeLists.txt

@@ -15,6 +15,7 @@
 add_spvtools_unittest(TARGET utils
   SRCS ilist_test.cpp
        bit_vector_test.cpp
+       bitutils_test.cpp
        small_vector_test.cpp
   LIBS SPIRV-Tools-opt
 )

+ 193 - 0
3rdparty/spirv-tools/test/util/bitutils_test.cpp

@@ -0,0 +1,193 @@
+// Copyright (c) 2019 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/util/bitutils.h"
+
+#include "gmock/gmock.h"
+
+namespace spvtools {
+namespace utils {
+namespace {
+
+using BitUtilsTest = ::testing::Test;
+
+TEST(BitUtilsTest, MutateBitsWholeWord) {
+  const uint32_t zero_u32 = 0;
+  const uint32_t max_u32 = ~0;
+
+  EXPECT_EQ(MutateBits(zero_u32, 0, 0, false), zero_u32);
+  EXPECT_EQ(MutateBits(max_u32, 0, 0, false), max_u32);
+  EXPECT_EQ(MutateBits(zero_u32, 0, 32, false), zero_u32);
+  EXPECT_EQ(MutateBits(zero_u32, 0, 32, true), max_u32);
+  EXPECT_EQ(MutateBits(max_u32, 0, 32, true), max_u32);
+  EXPECT_EQ(MutateBits(max_u32, 0, 32, false), zero_u32);
+}
+
+TEST(BitUtilsTest, MutateBitsLow) {
+  const uint32_t zero_u32 = 0;
+  const uint32_t one_u32 = 1;
+  const uint32_t max_u32 = ~0;
+
+  EXPECT_EQ(MutateBits(zero_u32, 0, 1, false), zero_u32);
+  EXPECT_EQ(MutateBits(zero_u32, 0, 1, true), one_u32);
+  EXPECT_EQ(MutateBits(max_u32, 0, 1, true), max_u32);
+  EXPECT_EQ(MutateBits(one_u32, 0, 32, false), zero_u32);
+  EXPECT_EQ(MutateBits(one_u32, 0, 1, true), one_u32);
+  EXPECT_EQ(MutateBits(one_u32, 0, 1, false), zero_u32);
+  EXPECT_EQ(MutateBits(zero_u32, 0, 3, true), uint32_t(7));
+  EXPECT_EQ(MutateBits(uint32_t(7), 0, 2, false), uint32_t(4));
+}
+
+TEST(BitUtilsTest, MutateBitsHigh) {
+  const uint8_t zero_u8 = 0;
+  const uint8_t one_u8 = 1;
+  const uint8_t max_u8 = 255;
+
+  EXPECT_EQ(MutateBits(zero_u8, 7, 0, true), zero_u8);
+  EXPECT_EQ(MutateBits(zero_u8, 7, 1, true), uint8_t(128));
+  EXPECT_EQ(MutateBits(one_u8, 7, 1, true), uint8_t(129));
+  EXPECT_EQ(MutateBits(max_u8, 7, 1, true), max_u8);
+  EXPECT_EQ(MutateBits(max_u8, 7, 1, false), uint8_t(127));
+  EXPECT_EQ(MutateBits(max_u8, 6, 2, true), max_u8);
+  EXPECT_EQ(MutateBits(max_u8, 6, 2, false), uint8_t(63));
+}
+
+TEST(BitUtilsTest, MutateBitsUint8Mid) {
+  const uint8_t zero_u8 = 0;
+  const uint8_t max_u8 = 255;
+
+  EXPECT_EQ(MutateBits(zero_u8, 1, 2, true), uint8_t(6));
+  EXPECT_EQ(MutateBits(max_u8, 1, 2, true), max_u8);
+  EXPECT_EQ(MutateBits(max_u8, 1, 2, false), uint8_t(0xF9));
+  EXPECT_EQ(MutateBits(zero_u8, 2, 3, true), uint8_t(0x1C));
+}
+
+TEST(BitUtilsTest, MutateBitsUint64Mid) {
+  const uint64_t zero_u64 = 0;
+  const uint64_t max_u64 = ~zero_u64;
+
+  EXPECT_EQ(MutateBits(zero_u64, 1, 2, true), uint64_t(6));
+  EXPECT_EQ(MutateBits(max_u64, 1, 2, true), max_u64);
+  EXPECT_EQ(MutateBits(max_u64, 1, 2, false), uint64_t(0xFFFFFFFFFFFFFFF9));
+  EXPECT_EQ(MutateBits(zero_u64, 2, 3, true), uint64_t(0x000000000000001C));
+  EXPECT_EQ(MutateBits(zero_u64, 2, 35, true), uint64_t(0x0000001FFFFFFFFC));
+  EXPECT_EQ(MutateBits(zero_u64, 36, 4, true), uint64_t(0x000000F000000000));
+  EXPECT_EQ(MutateBits(max_u64, 36, 4, false), uint64_t(0xFFFFFF0FFFFFFFFF));
+}
+
+TEST(BitUtilsTest, SetHighBitsUint32) {
+  const uint32_t zero_u32 = 0;
+  const uint32_t one_u32 = 1;
+  const uint32_t max_u32 = ~zero_u32;
+
+  EXPECT_EQ(SetHighBits(zero_u32, 0), zero_u32);
+  EXPECT_EQ(SetHighBits(zero_u32, 1), 0x80000000);
+  EXPECT_EQ(SetHighBits(one_u32, 1), 0x80000001);
+  EXPECT_EQ(SetHighBits(one_u32, 2), 0xC0000001);
+  EXPECT_EQ(SetHighBits(zero_u32, 31), 0xFFFFFFFE);
+  EXPECT_EQ(SetHighBits(zero_u32, 32), max_u32);
+  EXPECT_EQ(SetHighBits(max_u32, 32), max_u32);
+}
+
+TEST(BitUtilsTest, ClearHighBitsUint32) {
+  const uint32_t zero_u32 = 0;
+  const uint32_t one_u32 = 1;
+  const uint32_t max_u32 = ~zero_u32;
+
+  EXPECT_EQ(ClearHighBits(zero_u32, 0), zero_u32);
+  EXPECT_EQ(ClearHighBits(zero_u32, 1), zero_u32);
+  EXPECT_EQ(ClearHighBits(one_u32, 1), one_u32);
+  EXPECT_EQ(ClearHighBits(one_u32, 31), one_u32);
+  EXPECT_EQ(ClearHighBits(one_u32, 32), zero_u32);
+  EXPECT_EQ(ClearHighBits(max_u32, 0), max_u32);
+  EXPECT_EQ(ClearHighBits(max_u32, 1), 0x7FFFFFFF);
+  EXPECT_EQ(ClearHighBits(max_u32, 2), 0x3FFFFFFF);
+  EXPECT_EQ(ClearHighBits(max_u32, 31), one_u32);
+  EXPECT_EQ(ClearHighBits(max_u32, 32), zero_u32);
+}
+
+TEST(BitUtilsTest, IsBitSetAtPositionZero) {
+  const uint32_t zero_u32 = 0;
+  for (size_t i = 0; i != 32; ++i) {
+    EXPECT_FALSE(IsBitAtPositionSet(zero_u32, i));
+  }
+
+  const uint8_t zero_u8 = 0;
+  for (size_t i = 0; i != 8; ++i) {
+    EXPECT_FALSE(IsBitAtPositionSet(zero_u8, i));
+  }
+
+  const uint64_t zero_u64 = 0;
+  for (size_t i = 0; i != 64; ++i) {
+    EXPECT_FALSE(IsBitAtPositionSet(zero_u64, i));
+  }
+}
+
+TEST(BitUtilsTest, IsBitSetAtPositionOne) {
+  const uint32_t one_u32 = 1;
+  for (size_t i = 0; i != 32; ++i) {
+    if (i == 0) {
+      EXPECT_TRUE(IsBitAtPositionSet(one_u32, i));
+    } else {
+      EXPECT_FALSE(IsBitAtPositionSet(one_u32, i));
+    }
+  }
+
+  const uint32_t two_to_17_u32 = 1 << 17;
+  for (size_t i = 0; i != 32; ++i) {
+    if (i == 17) {
+      EXPECT_TRUE(IsBitAtPositionSet(two_to_17_u32, i));
+    } else {
+      EXPECT_FALSE(IsBitAtPositionSet(two_to_17_u32, i));
+    }
+  }
+
+  const uint8_t two_to_4_u8 = 1 << 4;
+  for (size_t i = 0; i != 8; ++i) {
+    if (i == 4) {
+      EXPECT_TRUE(IsBitAtPositionSet(two_to_4_u8, i));
+    } else {
+      EXPECT_FALSE(IsBitAtPositionSet(two_to_4_u8, i));
+    }
+  }
+
+  const uint64_t two_to_55_u64 = uint64_t(1) << 55;
+  for (size_t i = 0; i != 64; ++i) {
+    if (i == 55) {
+      EXPECT_TRUE(IsBitAtPositionSet(two_to_55_u64, i));
+    } else {
+      EXPECT_FALSE(IsBitAtPositionSet(two_to_55_u64, i));
+    }
+  }
+}
+
+TEST(BitUtilsTest, IsBitSetAtPositionAll) {
+  const uint32_t max_u32 = ~0;
+  for (size_t i = 0; i != 32; ++i) {
+    EXPECT_TRUE(IsBitAtPositionSet(max_u32, i));
+  }
+
+  const uint32_t max_u8 = ~uint8_t(0);
+  for (size_t i = 0; i != 8; ++i) {
+    EXPECT_TRUE(IsBitAtPositionSet(max_u8, i));
+  }
+
+  const uint64_t max_u64 = ~uint64_t(0);
+  for (size_t i = 0; i != 64; ++i) {
+    EXPECT_TRUE(IsBitAtPositionSet(max_u64, i));
+  }
+}
+}  // namespace
+}  // namespace utils
+}  // namespace spvtools

+ 39 - 23
3rdparty/spirv-tools/test/val/val_composites_test.cpp

@@ -648,7 +648,6 @@ TEST_F(ValidateComposites, CompositeExtractSuccess) {
 %val16 = OpCompositeExtract %f32 %struct 4 1000 1
 %val17 = OpCompositeExtract %f32 %struct 5 0
 %val18 = OpCompositeExtract %u32 %struct 5 1
-%val19 = OpCompositeExtract %big_struct %struct
 )";
 
   CompileSuccessfully(GenerateShaderCode(body));
@@ -767,6 +766,18 @@ TEST_F(ValidateComposites, CompositeExtractTooManyIndices) {
                         "indexes still remain to be traversed."));
 }
 
+TEST_F(ValidateComposites, CompositeExtractNoIndices) {
+  const std::string body = R"(
+%struct = OpLoad %big_struct %var_big_struct
+%val1 = OpCompositeExtract %big_struct %struct
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected at least one index to OpCompositeExtract"));
+}
+
 TEST_F(ValidateComposites, CompositeExtractWrongType1) {
   const std::string body = R"(
 %struct = OpLoad %big_struct %var_big_struct
@@ -861,7 +872,6 @@ TEST_F(ValidateComposites, CompositeInsertSuccess) {
 %val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 1000 1
 %val17 = OpCompositeInsert %big_struct %f32_3 %struct 5 0
 %val18 = OpCompositeInsert %big_struct %u32_3 %struct 5 1
-%val19 = OpCompositeInsert %big_struct %struct %struct
 )";
 
   CompileSuccessfully(GenerateShaderCode(body));
@@ -1157,9 +1167,8 @@ TEST_F(ValidateComposites, CompositeInsertWrongResultTypeBad) {
               HasSubstr("The Result Type must be the same as Composite type"));
 }
 
-// Valid: No Indexes were passed to OpCompositeExtract, and the Result Type is
-// the same as the Base Composite type.
-TEST_F(ValidateComposites, CompositeExtractNoIndexesGood) {
+// Invalid: No Indexes were passed to OpCompositeExtract.
+TEST_F(ValidateComposites, CompositeExtractNoIndices2) {
   std::ostringstream spirv;
   spirv << GetHeaderForTestsFromValId() << std::endl;
   spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
@@ -1167,29 +1176,32 @@ TEST_F(ValidateComposites, CompositeExtractNoIndexesGood) {
   spirv << R"(OpReturn
               OpFunctionEnd)";
   CompileSuccessfully(spirv.str());
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected at least one index to OpCompositeExtract, zero found"));
 }
 
-// Invalid: No Indexes were passed to OpCompositeExtract, but the Result Type is
-// different from the Base Composite type.
-TEST_F(ValidateComposites, CompositeExtractNoIndexesBad) {
+// Invalid: No Indexes were passed to OpCompositeExtract.
+TEST_F(ValidateComposites, CompositeExtractNoIndicesWrongResultType) {
   std::ostringstream spirv;
   spirv << GetHeaderForTestsFromValId() << std::endl;
   spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
-  spirv << "%float_entry = OpCompositeExtract  %float %matrix" << std::endl;
+  spirv << "%float_entry = OpCompositeExtract %float %matrix" << std::endl;
   spirv << R"(OpReturn
               OpFunctionEnd)";
   CompileSuccessfully(spirv.str());
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Result type (OpTypeFloat) does not match the type "
-                        "that results from indexing into the composite "
-                        "(OpTypeMatrix)."));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected at least one index to OpCompositeExtract, zero found"));
 }
 
-// Valid: No Indexes were passed to OpCompositeInsert, and the type of the
+// Invalid: No Indices were passed to OpCompositeInsert, and the type of the
 // Object<id> argument matches the Composite type.
-TEST_F(ValidateComposites, CompositeInsertMissingIndexesGood) {
+TEST_F(ValidateComposites, CompositeInsertMissingIndices) {
   std::ostringstream spirv;
   spirv << GetHeaderForTestsFromValId() << std::endl;
   spirv << "%matrix   = OpLoad %mat4x3 %my_matrix" << std::endl;
@@ -1199,12 +1211,16 @@ TEST_F(ValidateComposites, CompositeInsertMissingIndexesGood) {
               OpReturn
               OpFunctionEnd)";
   CompileSuccessfully(spirv.str());
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected at least one index to OpCompositeInsert, zero found"));
 }
 
-// Invalid: No Indexes were passed to OpCompositeInsert, but the type of the
+// Invalid: No Indices were passed to OpCompositeInsert, but the type of the
 // Object<id> argument does not match the Composite type.
-TEST_F(ValidateComposites, CompositeInsertMissingIndexesBad) {
+TEST_F(ValidateComposites, CompositeInsertMissingIndices2) {
   std::ostringstream spirv;
   spirv << GetHeaderForTestsFromValId() << std::endl;
   spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
@@ -1214,10 +1230,10 @@ TEST_F(ValidateComposites, CompositeInsertMissingIndexesBad) {
               OpFunctionEnd)";
   CompileSuccessfully(spirv.str());
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("The Object type (OpTypeInt) does not match the type "
-                        "that results from indexing into the Composite "
-                        "(OpTypeMatrix)."));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected at least one index to OpCompositeInsert, zero found"));
 }
 
 // Valid: Tests that we can index into Struct, Array, Matrix, and Vector!

+ 40 - 0
3rdparty/spirv-tools/test/val/val_derivatives_test.cpp

@@ -62,7 +62,17 @@ OpCapability DerivativeControl
 
 %f32vec4_ptr_input = OpTypePointer Input %f32vec4
 %f32vec4_var_input = OpVariable %f32vec4_ptr_input Input
+)";
+
+  if (capabilities_and_extensions.find("OpCapability Float16") !=
+      std::string::npos) {
+    ss << "%f16 = OpTypeFloat 16\n"
+       << "%f16vec4 = OpTypeVector %f16 4\n"
+       << "%f16_0 = OpConstantNull %f16\n"
+       << "%f16vec4_0 = OpConstantNull %f16vec4\n";
+  }
 
+  ss << R"(
 %main = OpFunction %void None %func
 %main_entry = OpLabel
 )";
@@ -150,6 +160,36 @@ TEST_F(ValidateDerivatives, OpDPdxWrongExecutionModel) {
                         "execution model: DPdx"));
 }
 
+using ValidateHalfDerivatives = spvtest::ValidateBase<std::string>;
+
+TEST_P(ValidateHalfDerivatives, ScalarFailure) {
+  const std::string op = GetParam();
+  const std::string body = "%val = " + op + " %f16 %f16_0\n";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability Float16\n").c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type component width must be 32 bits"));
+}
+
+TEST_P(ValidateHalfDerivatives, VectorFailure) {
+  const std::string op = GetParam();
+  const std::string body = "%val = " + op + " %f16vec4 %f16vec4_0\n";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability Float16\n").c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type component width must be 32 bits"));
+}
+
+INSTANTIATE_TEST_SUITE_P(HalfDerivatives, ValidateHalfDerivatives,
+                         ::testing::Values("OpDPdx", "OpDPdy", "OpFwidth",
+                                           "OpDPdxFine", "OpDPdyFine",
+                                           "OpFwidthFine", "OpDPdxCoarse",
+                                           "OpDPdyCoarse", "OpFwidthCoarse"));
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools

+ 1 - 1
3rdparty/spirv-tools/utils/update_build_version.py

@@ -119,7 +119,7 @@ def describe(directory):
             # clock time with enviornment variable SOURCE_DATE_EPOCH
             # containing a (presumably) fixed timestamp.
             timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
-            formatted = datetime.date.fromtimestamp(timestamp).isoformat()
+            formatted = datetime.datetime.utcfromtimestamp(timestamp).isoformat()
             return 'unknown hash, {}'.format(formatted)