Browse Source

Updated spirv-tools.

Бранимир Караџић 5 years ago
parent
commit
e605c022f2

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

@@ -1 +1 @@
-"v2020.3-dev", "SPIRV-Tools v2020.3-dev 9522e65f0f430a45a29053e2aeec768c2dbca075"
+"v2020.3-dev", "SPIRV-Tools v2020.3-dev 36e37dd5b53636024ccb44310450d4128dae0842"

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

@@ -68,17 +68,14 @@ namespace fuzz {
 template <typename T, typename PointerHashT, typename PointerEqualsT>
 class EquivalenceRelation {
  public:
-  // Merges the equivalence classes associated with |value1| and |value2|.
-  // If any of these values was not previously in the equivalence relation, it
-  // is added to the pool of values known to be in the relation.
+  // Requires that |value1| and |value2| are already registered in the
+  // equivalence relation.  Merges the equivalence classes associated with
+  // |value1| and |value2|.
   void MakeEquivalent(const T& value1, const T& value2) {
-    // Register each value if necessary.
-    for (auto value : {value1, value2}) {
-      if (!Exists(value)) {
-        // Register the value in the equivalence relation.
-        Register(value);
-      }
-    }
+    assert(Exists(value1) &&
+           "Precondition: value1 must already be registered.");
+    assert(Exists(value2) &&
+           "Precondition: value2 must already be registered.");
 
     // Look up canonical pointers to each of the values in the value pool.
     const T* value1_ptr = *value_set_.find(&value1);
@@ -105,7 +102,7 @@ class EquivalenceRelation {
   // Requires that |value| is not known to the equivalence relation. Registers
   // it in its own equivalence class and returns a pointer to the equivalence
   // class representative.
-  const T* Register(T& value) {
+  const T* Register(const T& value) {
     assert(!Exists(value));
 
     // This relies on T having a copy constructor.

+ 133 - 42
3rdparty/spirv-tools/source/fuzz/fact_manager.cpp

@@ -445,6 +445,15 @@ class FactManager::DataSynonymAndIdEquationFacts {
   // compute the closure only when a data synonym fact is *queried*.
   void ComputeClosureOfFacts(opt::IRContext* context) const;
 
+  // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets
+  // of equations that are known about them.
+  //
+  // This is a const method, despite the fact that it mutates the (mutable)
+  // set of facts about data descriptors because it is invoked in a lazy fashion
+  // when querying facts.
+  void MakeEquivalent(const protobufs::DataDescriptor& dd1,
+                      const protobufs::DataDescriptor& dd2) const;
+
   // Returns true if and only if |dd1| and |dd2| are valid data descriptors
   // whose associated data have the same type (modulo integer signedness).
   bool DataDescriptorsAreWellFormedAndComparable(
@@ -452,10 +461,11 @@ class FactManager::DataSynonymAndIdEquationFacts {
       const protobufs::DataDescriptor& dd2) const;
 
   // Requires that |lhs_dd| and every element of |rhs_dds| is present in the
-  // |synonymous_| equivalence relation and is its own representative.  Records
-  // the fact that the equation "|lhs_dd| |opcode| |rhs_dds|" holds, and adds
-  // any corollaries, in the form of data synonym or equation facts, that
-  // follow from this and other known facts.
+  // |synonymous_| equivalence relation, but is not necessarily its own
+  // representative.  Records the fact that the equation
+  // "|lhs_dd| |opcode| |rhs_dds_non_canonical|" holds, and adds any
+  // corollaries, in the form of data synonym or equation facts, that follow
+  // from this and other known facts.
   void AddEquationFactRecursive(
       const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
       const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
@@ -493,7 +503,12 @@ class FactManager::DataSynonymAndIdEquationFacts {
   // All data descriptors occurring in equations are required to be present in
   // the |synonymous_| equivalence relation, and to be their own representatives
   // in that relation.
-  std::unordered_map<
+  //
+  // It is mutable because a closure computation can be triggered from a const
+  // method, and when a closure computation detects that two data descriptors
+  // are equivalent it is necessary to merge the equation facts for those data
+  // descriptors.
+  mutable std::unordered_map<
       const protobufs::DataDescriptor*,
       std::unordered_set<Operation, OperationHash, OperationEquals>>
       id_equations_;
@@ -510,12 +525,10 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact(
     const protobufs::FactIdEquation& fact, opt::IRContext* context) {
   protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
 
-  // Register the LHS in the equivalence relation if needed, and get a pointer
-  // to its representative.
+  // Register the LHS in the equivalence relation if needed.
   if (!synonymous_.Exists(lhs_dd)) {
     synonymous_.Register(lhs_dd);
   }
-  const protobufs::DataDescriptor* lhs_dd_ptr = synonymous_.Find(&lhs_dd);
 
   // Get equivalence class representatives for all ids used on the RHS of the
   // equation.
@@ -529,10 +542,9 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact(
     }
     rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd));
   }
-  // We now have the equation in a form where it refers exclusively to
-  // equivalence class representatives.  Add it to our set of facts and work
-  // out any follow-on facts.
-  AddEquationFactRecursive(*lhs_dd_ptr, static_cast<SpvOp>(fact.opcode()),
+
+  // Now add the fact.
+  AddEquationFactRecursive(lhs_dd, static_cast<SpvOp>(fact.opcode()),
                            rhs_dd_ptrs, context);
 }
 
@@ -540,27 +552,27 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
     const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
     const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
     opt::IRContext* context) {
-  // Precondition: all data descriptors referenced in this equation must be
-  // equivalence class representatives - i.e. the equation must be in canonical
-  // form.
-  assert(synonymous_.Exists(lhs_dd));
-  assert(synonymous_.Find(&lhs_dd) == &lhs_dd);
+  assert(synonymous_.Exists(lhs_dd) &&
+         "The LHS must be known to the equivalence relation.");
   for (auto rhs_dd : rhs_dds) {
-    (void)(rhs_dd);  // Keep compilers happy in release mode.
-    assert(synonymous_.Exists(*rhs_dd));
-    assert(synonymous_.Find(rhs_dd) == rhs_dd);
+    // Keep release compilers happy.
+    (void)(rhs_dd);
+    assert(synonymous_.Exists(*rhs_dd) &&
+           "The RHS operands must be known to the equivalence relation.");
   }
 
-  if (id_equations_.count(&lhs_dd) == 0) {
+  auto lhs_dd_representative = synonymous_.Find(&lhs_dd);
+
+  if (id_equations_.count(lhs_dd_representative) == 0) {
     // We have not seen an equation with this LHS before, so associate the LHS
     // with an initially empty set.
     id_equations_.insert(
-        {&lhs_dd,
+        {lhs_dd_representative,
          std::unordered_set<Operation, OperationHash, OperationEquals>()});
   }
 
   {
-    auto existing_equations = id_equations_.find(&lhs_dd);
+    auto existing_equations = id_equations_.find(lhs_dd_representative);
     assert(existing_equations != id_equations_.end() &&
            "A set of operations should be present, even if empty.");
 
@@ -584,13 +596,15 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
           for (auto equation : existing_first_operand_equations->second) {
             if (equation.opcode == SpvOpISub) {
               // Equation form: "a = (d - e) + c"
-              if (equation.operands[1] == rhs_dds[1]) {
+              if (synonymous_.IsEquivalent(*equation.operands[1],
+                                           *rhs_dds[1])) {
                 // Equation form: "a = (d - c) + c"
                 // We can thus infer "a = d"
                 AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
                                             context);
               }
-              if (equation.operands[0] == rhs_dds[1]) {
+              if (synonymous_.IsEquivalent(*equation.operands[0],
+                                           *rhs_dds[1])) {
                 // Equation form: "a = (c - e) + c"
                 // We can thus infer "a = -e"
                 AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
@@ -606,7 +620,8 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
           for (auto equation : existing_second_operand_equations->second) {
             if (equation.opcode == SpvOpISub) {
               // Equation form: "a = b + (d - e)"
-              if (equation.operands[1] == rhs_dds[0]) {
+              if (synonymous_.IsEquivalent(*equation.operands[1],
+                                           *rhs_dds[0])) {
                 // Equation form: "a = b + (d - b)"
                 // We can thus infer "a = d"
                 AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
@@ -626,13 +641,15 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
           for (auto equation : existing_first_operand_equations->second) {
             if (equation.opcode == SpvOpIAdd) {
               // Equation form: "a = (d + e) - c"
-              if (equation.operands[0] == rhs_dds[1]) {
+              if (synonymous_.IsEquivalent(*equation.operands[0],
+                                           *rhs_dds[1])) {
                 // Equation form: "a = (c + e) - c"
                 // We can thus infer "a = e"
                 AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
                                             context);
               }
-              if (equation.operands[1] == rhs_dds[1]) {
+              if (synonymous_.IsEquivalent(*equation.operands[1],
+                                           *rhs_dds[1])) {
                 // Equation form: "a = (d + c) - c"
                 // We can thus infer "a = d"
                 AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
@@ -642,7 +659,8 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
 
             if (equation.opcode == SpvOpISub) {
               // Equation form: "a = (d - e) - c"
-              if (equation.operands[0] == rhs_dds[1]) {
+              if (synonymous_.IsEquivalent(*equation.operands[0],
+                                           *rhs_dds[1])) {
                 // Equation form: "a = (c - e) - c"
                 // We can thus infer "a = -e"
                 AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
@@ -659,13 +677,15 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
           for (auto equation : existing_second_operand_equations->second) {
             if (equation.opcode == SpvOpIAdd) {
               // Equation form: "a = b - (d + e)"
-              if (equation.operands[0] == rhs_dds[0]) {
+              if (synonymous_.IsEquivalent(*equation.operands[0],
+                                           *rhs_dds[0])) {
                 // Equation form: "a = b - (b + e)"
                 // We can thus infer "a = -e"
                 AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
                                          {equation.operands[1]}, context);
               }
-              if (equation.operands[1] == rhs_dds[0]) {
+              if (synonymous_.IsEquivalent(*equation.operands[1],
+                                           *rhs_dds[0])) {
                 // Equation form: "a = b - (d + b)"
                 // We can thus infer "a = -d"
                 AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
@@ -674,7 +694,8 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
             }
             if (equation.opcode == SpvOpISub) {
               // Equation form: "a = b - (d - e)"
-              if (equation.operands[0] == rhs_dds[0]) {
+              if (synonymous_.IsEquivalent(*equation.operands[0],
+                                           *rhs_dds[0])) {
                 // Equation form: "a = b - (b - e)"
                 // We can thus infer "a = e"
                 AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
@@ -712,12 +733,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
   assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2));
 
   // Record that the data descriptors provided in the fact are equivalent.
-  synonymous_.MakeEquivalent(dd1, dd2);
-  // As we have updated the equivalence relation, we might be able to deduce
-  // more facts by performing a closure computation, so we record that such a
-  // computation is required; it will be performed next time a method answering
-  // a data synonym fact-related question is invoked.
-  closure_computation_required_ = true;
+  MakeEquivalent(dd1, dd2);
 
   // We now check whether this is a synonym about composite objects.  If it is,
   // we can recursively add synonym facts about their associated sub-components.
@@ -1029,10 +1045,7 @@ void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
             // synonymous.
             assert(DataDescriptorsAreWellFormedAndComparable(
                 context, dd1_prefix, dd2_prefix));
-            synonymous_.MakeEquivalent(dd1_prefix, dd2_prefix);
-            // As we have added a new synonym fact, we might benefit from doing
-            // another pass over the equivalence relation.
-            closure_computation_required_ = true;
+            MakeEquivalent(dd1_prefix, dd2_prefix);
             // Now that we know this pair of data descriptors are synonymous,
             // there is no point recording how close they are to being
             // synonymous.
@@ -1044,6 +1057,84 @@ void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
   }
 }
 
+void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
+    const protobufs::DataDescriptor& dd1,
+    const protobufs::DataDescriptor& dd2) const {
+  // Register the data descriptors if they are not already known to the
+  // equivalence relation.
+  for (const auto& dd : {dd1, dd2}) {
+    if (!synonymous_.Exists(dd)) {
+      synonymous_.Register(dd);
+    }
+  }
+
+  if (synonymous_.IsEquivalent(dd1, dd2)) {
+    // The data descriptors are already known to be equivalent, so there is
+    // nothing to do.
+    return;
+  }
+
+  // We must make the data descriptors equivalent, and also make sure any
+  // equation facts known about their representatives are merged.
+
+  // Record the original equivalence class representatives of the data
+  // descriptors.
+  auto dd1_original_representative = synonymous_.Find(&dd1);
+  auto dd2_original_representative = synonymous_.Find(&dd2);
+
+  // Make the data descriptors equivalent.
+  synonymous_.MakeEquivalent(dd1, dd2);
+  // As we have updated the equivalence relation, we might be able to deduce
+  // more facts by performing a closure computation, so we record that such a
+  // computation is required.
+  closure_computation_required_ = true;
+
+  // At this point, exactly one of |dd1_original_representative| and
+  // |dd2_original_representative| will be the representative of the combined
+  // equivalence class.  We work out which one of them is still the class
+  // representative and which one is no longer the class representative.
+
+  auto still_representative = synonymous_.Find(dd1_original_representative) ==
+                                      dd1_original_representative
+                                  ? dd1_original_representative
+                                  : dd2_original_representative;
+  auto no_longer_representative =
+      still_representative == dd1_original_representative
+          ? dd2_original_representative
+          : dd1_original_representative;
+
+  assert(no_longer_representative != still_representative &&
+         "The current and former representatives cannot be the same.");
+
+  // We now need to add all equations about |no_longer_representative| to the
+  // set of equations known about |still_representative|.
+
+  // Get the equations associated with |no_longer_representative|.
+  auto no_longer_representative_id_equations =
+      id_equations_.find(no_longer_representative);
+  if (no_longer_representative_id_equations != id_equations_.end()) {
+    // There are some equations to transfer.  There might not yet be any
+    // equations about |still_representative|; create an empty set of equations
+    // if this is the case.
+    if (!id_equations_.count(still_representative)) {
+      id_equations_.insert(
+          {still_representative,
+           std::unordered_set<Operation, OperationHash, OperationEquals>()});
+    }
+    auto still_representative_id_equations =
+        id_equations_.find(still_representative);
+    assert(still_representative_id_equations != id_equations_.end() &&
+           "At this point there must be a set of equations.");
+    // Add all the equations known about |no_longer_representative| to the set
+    // of equations known about |still_representative|.
+    still_representative_id_equations->second.insert(
+        no_longer_representative_id_equations->second.begin(),
+        no_longer_representative_id_equations->second.end());
+  }
+  // Delete the no longer-relevant equations about |no_longer_representative|.
+  id_equations_.erase(no_longer_representative);
+}
+
 bool FactManager::DataSynonymAndIdEquationFacts::
     DataDescriptorsAreWellFormedAndComparable(
         opt::IRContext* context, const protobufs::DataDescriptor& dd1,

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

@@ -214,8 +214,9 @@ std::vector<uint32_t> FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments(
         result.push_back(fresh_variable_id);
 
         // Now bring the variable into existence.
-        if (type_instruction->GetSingleWordInOperand(0) ==
-            SpvStorageClassFunction) {
+        auto storage_class = static_cast<SpvStorageClass>(
+            type_instruction->GetSingleWordInOperand(0));
+        if (storage_class == SpvStorageClassFunction) {
           // Add a new zero-initialized local variable to the current
           // function, noting that its pointee value is irrelevant.
           ApplyTransformation(TransformationAddLocalVariable(
@@ -224,16 +225,19 @@ std::vector<uint32_t> FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments(
                   type_instruction->GetSingleWordInOperand(1)),
               true));
         } else {
-          assert(type_instruction->GetSingleWordInOperand(0) ==
-                     SpvStorageClassPrivate &&
-                 "Only Function and Private storage classes are "
+          assert((storage_class == SpvStorageClassPrivate ||
+                  storage_class == SpvStorageClassWorkgroup) &&
+                 "Only Function, Private and Workgroup storage classes are "
                  "supported at present.");
-          // Add a new zero-initialized global variable to the module,
-          // noting that its pointee value is irrelevant.
+          // Add a new global variable to the module, zero-initializing it if
+          // it has Private storage class, and noting that its pointee value is
+          // irrelevant.
           ApplyTransformation(TransformationAddGlobalVariable(
-              fresh_variable_id, arg_type_id,
-              FindOrCreateZeroConstant(
-                  type_instruction->GetSingleWordInOperand(1)),
+              fresh_variable_id, arg_type_id, storage_class,
+              storage_class == SpvStorageClassPrivate
+                  ? FindOrCreateZeroConstant(
+                        type_instruction->GetSingleWordInOperand(1))
+                  : 0,
               true));
         }
       } else {

+ 3 - 1
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp

@@ -66,9 +66,11 @@ void FuzzerPassAddGlobalVariables::Apply() {
           available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
               available_pointers_to_basic_type)];
     }
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3274):  We could
+    //  add new variables with Workgroup storage class in compute shaders.
     ApplyTransformation(TransformationAddGlobalVariable(
         GetFuzzerContext()->GetFreshId(), pointer_type_id,
-        FindOrCreateZeroConstant(basic_type), true));
+        SpvStorageClassPrivate, FindOrCreateZeroConstant(basic_type), true));
   }
 }
 

File diff suppressed because it is too large
+ 525 - 482
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp


+ 67 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h

@@ -66,6 +66,11 @@ class FuzzerPassDonateModules : public FuzzerPass {
       opt::IRContext* donor_ir_context,
       std::map<uint32_t, uint32_t>* original_id_to_donated_id);
 
+  // TODO comment
+  void HandleTypeOrValue(
+      const opt::Instruction& type_or_value,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id);
+
   // Assumes that |donor_ir_context| does not exhibit recursion.  Considers the
   // functions in |donor_ir_context|'s call graph in a reverse-topologically-
   // sorted order (leaves-to-root), adding each function to the recipient
@@ -77,6 +82,68 @@ class FuzzerPassDonateModules : public FuzzerPass {
                        std::map<uint32_t, uint32_t>* original_id_to_donated_id,
                        bool make_livesafe);
 
+  // During donation we will have to ignore some instructions, e.g. because they
+  // use opcodes that we cannot support or because they reference the ids of
+  // instructions that have not been donated.  This function encapsulates the
+  // logic for deciding which whether instruction |instruction| from
+  // |donor_ir_context| can be donated.
+  bool CanDonateInstruction(
+      opt::IRContext* donor_ir_context, const opt::Instruction& instruction,
+      const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
+      const std::set<uint32_t>& skipped_instructions) const;
+
+  // We treat the OpArrayLength instruction specially.  In the donor shader this
+  // instruction yields the length of a runtime array that is the final member
+  // of a struct.  During donation, we will have converted the runtime array
+  // type, and the associated struct field, into a fixed-size array.
+  //
+  // Instead of donating this instruction, we turn it into an OpCopyObject
+  // instruction that copies the size of the fixed-size array.
+  void HandleOpArrayLength(
+      const opt::Instruction& instruction,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+      std::vector<protobufs::Instruction>* donated_instructions) const;
+
+  // The instruction |instruction| is required to be an instruction that cannot
+  // be easily donated, either because it uses an unsupported opcode, has an
+  // unsupported result type, or uses id operands that could not be donated.
+  //
+  // If |instruction| generates a result id, the function attempts to add a
+  // substitute for |instruction| to |donated_instructions| that has the correct
+  // result type.  If this cannot be done, the instruction's result id is added
+  // to |skipped_instructions|.  The mapping from donor ids to recipient ids is
+  // managed by |original_id_to_donated_id|.
+  void HandleDifficultInstruction(
+      const opt::Instruction& instruction,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+      std::vector<protobufs::Instruction>* donated_instructions,
+      std::set<uint32_t>* skipped_instructions);
+
+  // Adds an instruction based in |instruction| to |donated_instructions| in a
+  // form ready for donation.  The original instruction comes from
+  // |donor_ir_context|, and |original_id_to_donated_id| maps ids from
+  // |donor_ir_context| to corresponding ids in the recipient module.
+  void PrepareInstructionForDonation(
+      const opt::Instruction& instruction, opt::IRContext* donor_ir_context,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+      std::vector<protobufs::Instruction>* donated_instructions);
+
+  // Requires that |donated_instructions| represents a prepared version of the
+  // instructions of |function_to_donate| (which comes from |donor_ir_context|)
+  // ready for donation, and |original_id_to_donated_id| maps ids from
+  // |donor_ir_context| to their corresponding ids in the recipient module.
+  //
+  // Adds a livesafe version of the function, based on |donated_instructions|,
+  // to the recipient module.
+  void AddLivesafeFunction(
+      const opt::Function& function_to_donate, opt::IRContext* donor_ir_context,
+      const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
+      const std::vector<protobufs::Instruction>& donated_instructions);
+
+  // Returns true if and only if |instruction| is a scalar, vector, matrix,
+  // array or struct; i.e. it is not an opaque type.
+  bool IsBasicType(const opt::Instruction& instruction) const;
+
   // Returns the ids of all functions in |context| in a topological order in
   // relation to the call graph of |context|, which is assumed to be recursion-
   // free.

+ 7 - 4
3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto

@@ -560,8 +560,9 @@ message TransformationAddGlobalUndef {
 
 message TransformationAddGlobalVariable {
 
-  // Adds a global variable of the given type to the module, with Private
-  // storage class and optionally with an initializer.
+  // Adds a global variable of the given type to the module, with Private or
+  // Workgroup storage class, and optionally (for the Private case) with an
+  // initializer.
 
   // Fresh id for the global variable
   uint32 fresh_id = 1;
@@ -569,13 +570,15 @@ message TransformationAddGlobalVariable {
   // The type of the global variable
   uint32 type_id = 2;
 
+  uint32 storage_class = 3;
+
   // Initial value of the variable
-  uint32 initializer_id = 3;
+  uint32 initializer_id = 4;
 
   // True if and only if the behaviour of the module should not depend on the
   // value of the variable, in which case stores to the variable can be
   // performed in an arbitrary fashion.
-  bool value_is_irrelevant = 4;
+  bool value_is_irrelevant = 5;
 
 }
 

+ 6 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp

@@ -897,6 +897,11 @@ uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
     case SpvOpTypeStruct: {
       return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
     }
+    case SpvOpTypeRuntimeArray:
+      assert(false &&
+             "GetBoundForCompositeIndex should not be invoked with an "
+             "OpTypeRuntimeArray, which does not have a static bound.");
+      return 0;
     default:
       assert(false && "Unknown composite type.");
       return 0;
@@ -909,6 +914,7 @@ opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
   uint32_t sub_object_type_id;
   switch (composite_type_inst.opcode()) {
     case SpvOpTypeArray:
+    case SpvOpTypeRuntimeArray:
       sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
       break;
     case SpvOpTypeMatrix:

+ 41 - 20
3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp

@@ -24,10 +24,11 @@ TransformationAddGlobalVariable::TransformationAddGlobalVariable(
     : message_(message) {}
 
 TransformationAddGlobalVariable::TransformationAddGlobalVariable(
-    uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id,
-    bool value_is_irrelevant) {
+    uint32_t fresh_id, uint32_t type_id, SpvStorageClass storage_class,
+    uint32_t initializer_id, bool value_is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
+  message_.set_storage_class(storage_class);
   message_.set_initializer_id(initializer_id);
   message_.set_value_is_irrelevant(value_is_irrelevant);
 }
@@ -38,6 +39,17 @@ bool TransformationAddGlobalVariable::IsApplicable(
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
+
+  // The storage class must be Private or Workgroup.
+  auto storage_class = static_cast<SpvStorageClass>(message_.storage_class());
+  switch (storage_class) {
+    case SpvStorageClassPrivate:
+    case SpvStorageClassWorkgroup:
+      break;
+    default:
+      assert(false && "Unsupported storage class.");
+      return false;
+  }
   // The type id must correspond to a type.
   auto type = ir_context->get_type_mgr()->GetType(message_.type_id());
   if (!type) {
@@ -48,23 +60,32 @@ bool TransformationAddGlobalVariable::IsApplicable(
   if (!pointer_type) {
     return false;
   }
-  // ... with Private storage class.
-  if (pointer_type->storage_class() != SpvStorageClassPrivate) {
+  // ... with the right storage class.
+  if (pointer_type->storage_class() != storage_class) {
     return false;
   }
-  // The initializer id must be the id of a constant.  Check this with the
-  // constant manager.
-  auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds(
-      {message_.initializer_id()});
-  if (constant_id.empty()) {
-    return false;
-  }
-  assert(constant_id.size() == 1 &&
-         "We asked for the constant associated with a single id; we should "
-         "get a single constant.");
-  // The type of the constant must match the pointee type of the pointer.
-  if (pointer_type->pointee_type() != constant_id[0]->type()) {
-    return false;
+  if (message_.initializer_id()) {
+    // An initializer is not allowed if the storage class is Workgroup.
+    if (storage_class == SpvStorageClassWorkgroup) {
+      assert(false &&
+             "By construction this transformation should not have an "
+             "initializer when Workgroup storage class is used.");
+      return false;
+    }
+    // The initializer id must be the id of a constant.  Check this with the
+    // constant manager.
+    auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds(
+        {message_.initializer_id()});
+    if (constant_id.empty()) {
+      return false;
+    }
+    assert(constant_id.size() == 1 &&
+           "We asked for the constant associated with a single id; we should "
+           "get a single constant.");
+    // The type of the constant must match the pointee type of the pointer.
+    if (pointer_type->pointee_type() != constant_id[0]->type()) {
+      return false;
+    }
   }
   return true;
 }
@@ -74,7 +95,7 @@ void TransformationAddGlobalVariable::Apply(
     TransformationContext* transformation_context) const {
   opt::Instruction::OperandList input_operands;
   input_operands.push_back(
-      {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}});
+      {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}});
   if (message_.initializer_id()) {
     input_operands.push_back(
         {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
@@ -84,7 +105,7 @@ void TransformationAddGlobalVariable::Apply(
       input_operands));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
-  if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(ir_context)) {
+  if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(ir_context)) {
     // Conservatively add this global to the interface of every entry point in
     // the module.  This means that the global is available for other
     // transformations to use.
@@ -117,7 +138,7 @@ protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const {
 }
 
 bool TransformationAddGlobalVariable::
-    PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
+    GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
         opt::IRContext* ir_context) {
   // TODO(afd): We capture the universal environments for which this requirement
   //  holds.  The check should be refined on demand for other target

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

@@ -29,22 +29,26 @@ class TransformationAddGlobalVariable : public Transformation {
       const protobufs::TransformationAddGlobalVariable& message);
 
   TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
+                                  SpvStorageClass storage_class,
                                   uint32_t initializer_id,
                                   bool value_is_irrelevant);
 
   // - |message_.fresh_id| must be fresh
-  // - |message_.type_id| must be the id of a pointer type with Private storage
-  //   class
-  // - |message_.initializer_id| must either be 0 or the id of a constant whose
+  // - |message_.type_id| must be the id of a pointer type with the same storage
+  //   class as |message_.storage_class|
+  // - |message_.storage_class| must be Private or Workgroup
+  // - |message_.initializer_id| must be 0 if |message_.storage_class| is
+  //   Workgroup, and otherwise may either be 0 or the id of a constant whose
   //   type is the pointee type of |message_.type_id|
   bool IsApplicable(
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
 
-  // Adds a global variable with Private storage class to the module, with type
-  // |message_.type_id| and either no initializer or |message_.initializer_id|
-  // as an initializer, depending on whether |message_.initializer_id| is 0.
-  // The global variable has result id |message_.fresh_id|.
+  // Adds a global variable with storage class |message_.storage_class| to the
+  // module, with type |message_.type_id| and either no initializer or
+  // |message_.initializer_id| as an initializer, depending on whether
+  // |message_.initializer_id| is 0.  The global variable has result id
+  // |message_.fresh_id|.
   //
   // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the
   // fact manager in |transformation_context|.
@@ -54,7 +58,10 @@ class TransformationAddGlobalVariable : public Transformation {
   protobufs::Transformation ToMessage() const override;
 
  private:
-  static bool PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
+  // Returns true if and only if the SPIR-V version being used requires that
+  // global variables accessed in the static call graph of an entry point need
+  // to be listed in that entry point's interface.
+  static bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
       opt::IRContext* ir_context);
 
   protobufs::TransformationAddGlobalVariable message_;

+ 72 - 18
3rdparty/spirv-tools/source/opt/eliminate_dead_members_pass.cpp

@@ -19,6 +19,7 @@
 
 namespace {
 const uint32_t kRemovedMember = 0xFFFFFFFF;
+const uint32_t kSpecConstOpOpcodeIdx = 0;
 }
 
 namespace spvtools {
@@ -40,7 +41,22 @@ void EliminateDeadMembersPass::FindLiveMembers() {
   // we have to mark them as fully used just to be safe.
   for (auto& inst : get_module()->types_values()) {
     if (inst.opcode() == SpvOpSpecConstantOp) {
-      MarkTypeAsFullyUsed(inst.type_id());
+      switch (inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
+        case SpvOpCompositeExtract:
+          MarkMembersAsLiveForExtract(&inst);
+          break;
+        case SpvOpCompositeInsert:
+          // Nothing specific to do.
+          break;
+        case SpvOpAccessChain:
+        case SpvOpInBoundsAccessChain:
+        case SpvOpPtrAccessChain:
+        case SpvOpInBoundsPtrAccessChain:
+          assert(false && "Not implemented yet.");
+          break;
+        default:
+          break;
+      }
     } else if (inst.opcode() == SpvOpVariable) {
       switch (inst.GetSingleWordInOperand(0)) {
         case SpvStorageClassInput:
@@ -153,13 +169,17 @@ void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory(
 
 void EliminateDeadMembersPass::MarkMembersAsLiveForExtract(
     const Instruction* inst) {
-  assert(inst->opcode() == SpvOpCompositeExtract);
+  assert(inst->opcode() == SpvOpCompositeExtract ||
+         (inst->opcode() == SpvOpSpecConstantOp &&
+          inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
+              SpvOpCompositeExtract));
 
-  uint32_t composite_id = inst->GetSingleWordInOperand(0);
+  uint32_t first_operand = (inst->opcode() == SpvOpSpecConstantOp ? 1 : 0);
+  uint32_t composite_id = inst->GetSingleWordInOperand(first_operand);
   Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
   uint32_t type_id = composite_inst->type_id();
 
-  for (uint32_t i = 1; i < inst->NumInOperands(); ++i) {
+  for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
     Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
     uint32_t member_idx = inst->GetSingleWordInOperand(i);
     switch (type_inst->opcode()) {
@@ -295,10 +315,22 @@ bool EliminateDeadMembersPass::RemoveDeadMembers() {
         modified |= UpdateOpArrayLength(inst);
         break;
       case SpvOpSpecConstantOp:
-        assert(false && "Not yet implemented.");
-        // with OpCompositeExtract, OpCompositeInsert
-        // For kernels: OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain,
-        // OpInBoundsPtrAccessChain
+        switch (inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
+          case SpvOpCompositeExtract:
+            modified |= UpdateCompsiteExtract(inst);
+            break;
+          case SpvOpCompositeInsert:
+            modified |= UpdateCompositeInsert(inst);
+            break;
+          case SpvOpAccessChain:
+          case SpvOpInBoundsAccessChain:
+          case SpvOpPtrAccessChain:
+          case SpvOpInBoundsPtrAccessChain:
+            assert(false && "Not implemented yet.");
+            break;
+          default:
+            break;
+        }
         break;
       default:
         break;
@@ -393,7 +425,8 @@ bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) {
 }
 
 bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) {
-  assert(inst->opcode() == SpvOpConstantComposite ||
+  assert(inst->opcode() == SpvOpSpecConstantComposite ||
+         inst->opcode() == SpvOpConstantComposite ||
          inst->opcode() == SpvOpCompositeConstruct);
   uint32_t type_id = inst->type_id();
 
@@ -506,14 +539,25 @@ uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id,
 }
 
 bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
-  uint32_t object_id = inst->GetSingleWordInOperand(0);
+  assert(inst->opcode() == SpvOpCompositeExtract ||
+         (inst->opcode() == SpvOpSpecConstantOp &&
+          inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
+              SpvOpCompositeExtract));
+
+  uint32_t first_operand = 0;
+  if (inst->opcode() == SpvOpSpecConstantOp) {
+    first_operand = 1;
+  }
+  uint32_t object_id = inst->GetSingleWordInOperand(first_operand);
   Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
   uint32_t type_id = object_inst->type_id();
 
   Instruction::OperandList new_operands;
   bool modified = false;
-  new_operands.emplace_back(inst->GetInOperand(0));
-  for (uint32_t i = 1; i < inst->NumInOperands(); ++i) {
+  for (uint32_t i = 0; i < first_operand + 1; i++) {
+    new_operands.emplace_back(inst->GetInOperand(i));
+  }
+  for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
     uint32_t member_idx = inst->GetSingleWordInOperand(i);
     uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
     assert(new_member_idx != kRemovedMember);
@@ -526,8 +570,6 @@ bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
     Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
     switch (type_inst->opcode()) {
       case SpvOpTypeStruct:
-        assert(i != 1 || (inst->opcode() != SpvOpPtrAccessChain &&
-                          inst->opcode() != SpvOpInBoundsPtrAccessChain));
         // The type will have already been rewriten, so use the new member
         // index.
         type_id = type_inst->GetSingleWordInOperand(new_member_idx);
@@ -552,15 +594,27 @@ bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
 }
 
 bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) {
-  uint32_t composite_id = inst->GetSingleWordInOperand(1);
+  assert(inst->opcode() == SpvOpCompositeInsert ||
+         (inst->opcode() == SpvOpSpecConstantOp &&
+          inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
+              SpvOpCompositeInsert));
+
+  uint32_t first_operand = 0;
+  if (inst->opcode() == SpvOpSpecConstantOp) {
+    first_operand = 1;
+  }
+
+  uint32_t composite_id = inst->GetSingleWordInOperand(first_operand + 1);
   Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
   uint32_t type_id = composite_inst->type_id();
 
   Instruction::OperandList new_operands;
   bool modified = false;
-  new_operands.emplace_back(inst->GetInOperand(0));
-  new_operands.emplace_back(inst->GetInOperand(1));
-  for (uint32_t i = 2; i < inst->NumInOperands(); ++i) {
+
+  for (uint32_t i = 0; i < first_operand + 2; ++i) {
+    new_operands.emplace_back(inst->GetInOperand(i));
+  }
+  for (uint32_t i = first_operand + 2; i < inst->NumInOperands(); ++i) {
     uint32_t member_idx = inst->GetSingleWordInOperand(i);
     uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
     if (new_member_idx == kRemovedMember) {

Some files were not shown because too many files changed in this diff