瀏覽代碼

Updated spirv-tools.

Бранимир Караџић 6 年之前
父節點
當前提交
7a2e51d748
共有 54 個文件被更改,包括 5909 次插入317 次删除
  1. 9 157
      3rdparty/spirv-tools/DEPS
  2. 11 0
      3rdparty/spirv-tools/README.md
  3. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  4. 6 0
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  5. 48 11
      3rdparty/spirv-tools/source/fuzz/fact_manager.cpp
  6. 4 2
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  7. 17 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
  8. 18 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
  9. 50 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
  10. 471 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
  11. 107 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h
  12. 46 8
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  13. 19 0
      3rdparty/spirv-tools/source/fuzz/replayer.cpp
  14. 61 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp
  15. 44 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.h
  16. 230 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
  17. 73 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h
  18. 82 3
      3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp
  19. 13 1
      3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h
  20. 1 0
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp
  21. 1 0
      3rdparty/spirv-tools/source/opt/common_uniform_elim_pass.cpp
  22. 1 0
      3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp
  23. 1 0
      3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp
  24. 1 0
      3rdparty/spirv-tools/source/opt/local_ssa_elim_pass.cpp
  25. 56 0
      3rdparty/spirv-tools/source/opt/upgrade_memory_model.cpp
  26. 15 3
      3rdparty/spirv-tools/source/opt/upgrade_memory_model.h
  27. 60 69
      3rdparty/spirv-tools/source/spirv_target_env.cpp
  28. 8 0
      3rdparty/spirv-tools/source/spirv_target_env.h
  29. 26 2
      3rdparty/spirv-tools/source/val/validate_atomics.cpp
  30. 78 0
      3rdparty/spirv-tools/source/val/validate_decorations.cpp
  31. 20 2
      3rdparty/spirv-tools/source/val/validate_memory.cpp
  32. 25 1
      3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp
  33. 13 0
      3rdparty/spirv-tools/source/val/validation_state.cpp
  34. 9 0
      3rdparty/spirv-tools/source/val/validation_state.h
  35. 5 1
      3rdparty/spirv-tools/test/fuzz/CMakeLists.txt
  36. 235 26
      3rdparty/spirv-tools/test/fuzz/fact_manager_test.cpp
  37. 393 0
      3rdparty/spirv-tools/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp
  38. 500 9
      3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp
  39. 218 0
      3rdparty/spirv-tools/test/fuzz/transformation_add_type_pointer_test.cpp
  40. 1486 0
      3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
  41. 84 0
      3rdparty/spirv-tools/test/fuzz/uniform_buffer_element_descriptor_test.cpp
  42. 199 0
      3rdparty/spirv-tools/test/opt/upgrade_memory_model_test.cpp
  43. 147 0
      3rdparty/spirv-tools/test/val/val_atomics_test.cpp
  44. 109 0
      3rdparty/spirv-tools/test/val/val_barriers_test.cpp
  45. 4 2
      3rdparty/spirv-tools/test/val/val_capability_test.cpp
  46. 308 0
      3rdparty/spirv-tools/test/val/val_decoration_test.cpp
  47. 266 0
      3rdparty/spirv-tools/test/val/val_memory_test.cpp
  48. 4 4
      3rdparty/spirv-tools/tools/as/as.cpp
  49. 1 1
      3rdparty/spirv-tools/tools/fuzz/fuzz.cpp
  50. 4 4
      3rdparty/spirv-tools/tools/link/linker.cpp
  51. 4 3
      3rdparty/spirv-tools/tools/opt/opt.cpp
  52. 4 6
      3rdparty/spirv-tools/tools/val/val.cpp
  53. 282 0
      3rdparty/spirv-tools/utils/git-sync-deps
  54. 31 0
      3rdparty/spirv-tools/utils/roll_deps.sh

+ 9 - 157
3rdparty/spirv-tools/DEPS

@@ -1,174 +1,26 @@
 use_relative_paths = True
 use_relative_paths = True
 
 
 vars = {
 vars = {
-  'chromium_git': 'https://chromium.googlesource.com',
   'github': 'https://github.com',
   'github': 'https://github.com',
 
 
-  'build_revision': '037f38ae0fe5e11b4f7c33b750fd7a1e9634a606',
-  'buildtools_revision': 'ab7b6a7b350dd15804c87c20ce78982811fdd76f',
-  'clang_revision': 'abe5e4f9dc0f1df848c7a0efa05256253e77a7b7',
-  'effcee_revision': '04b624799f5a9dbaf3fa1dbed2ba9dce2fc8dcf2',
-  'googletest_revision': '98a0d007d7092b72eea0e501bb9ad17908a1a036',
-  'testing_revision': '340252637e2e7c72c0901dcbeeacfff419e19b59',
-  're2_revision': '6cf8ccd82dbaab2668e9b13596c68183c9ecd13f',
-  'spirv_headers_revision': '8b911bd2ba37677037b38c9bd286c7c05701bcda',
+  'effcee_revision': 'b83b58d177b797edd1f94c5f10837f2cc2863f0a',
+  'googletest_revision': '2f42d769ad1b08742f7ccb5ad4dd357fc5ff248c',
+  're2_revision': '848dfb7e1d7ba641d598cb66f81590f3999a555a',
+  'spirv_headers_revision': 'de99d4d834aeb51dd9f099baa285bd44fd04bb3d',
 }
 }
 
 
 deps = {
 deps = {
-  "build":
-    Var('chromium_git') + "/chromium/src/build.git@" + Var('build_revision'),
-
-  'buildtools':
-      Var('chromium_git') + '/chromium/buildtools.git@' +
-          Var('buildtools_revision'),
-
-  'external/spirv-headers':
-      Var('github') +  '/KhronosGroup/SPIRV-Headers.git@' +
-          Var('spirv_headers_revision'),
+  'external/effcee':
+      Var('github') + '/google/effcee.git@' + Var('effcee_revision'),
 
 
   'external/googletest':
   'external/googletest':
       Var('github') + '/google/googletest.git@' + Var('googletest_revision'),
       Var('github') + '/google/googletest.git@' + Var('googletest_revision'),
 
 
-  'external/effcee':
-      Var('github') + '/google/effcee.git@' + Var('effcee_revision'),
-
   'external/re2':
   'external/re2':
       Var('github') + '/google/re2.git@' + Var('re2_revision'),
       Var('github') + '/google/re2.git@' + Var('re2_revision'),
 
 
-  'testing':
-      Var('chromium_git') + '/chromium/src/testing@' +
-          Var('testing_revision'),
-
-  'tools/clang':
-      Var('chromium_git') + '/chromium/src/tools/clang@' + Var('clang_revision')
+  'external/spirv-headers':
+      Var('github') +  '/KhronosGroup/SPIRV-Headers.git@' +
+          Var('spirv_headers_revision'),
 }
 }
 
 
-recursedeps = [
-  # buildtools provides clang_format, libc++, and libc++api
-  'buildtools',
-]
-
-hooks = [
-  {
-    'name': 'gn_win',
-    'action': [ 'download_from_google_storage',
-                '--no_resume',
-                '--platform=win32',
-                '--no_auth',
-                '--bucket', 'chromium-gn',
-                '-s', 'SPIRV-Tools/buildtools/win/gn.exe.sha1',
-    ],
-  },
-  {
-    'name': 'gn_mac',
-    'pattern': '.',
-    'action': [ 'download_from_google_storage',
-                '--no_resume',
-                '--platform=darwin',
-                '--no_auth',
-                '--bucket', 'chromium-gn',
-                '-s', 'SPIRV-Tools/buildtools/mac/gn.sha1',
-    ],
-  },
-  {
-    'name': 'gn_linux64',
-    'pattern': '.',
-    'action': [ 'download_from_google_storage',
-                '--no_resume',
-                '--platform=linux*',
-                '--no_auth',
-                '--bucket', 'chromium-gn',
-                '-s', 'SPIRV-Tools/buildtools/linux64/gn.sha1',
-    ],
-  },
-  # Pull clang-format binaries using checked-in hashes.
-  {
-    'name': 'clang_format_win',
-    'pattern': '.',
-    'action': [ 'download_from_google_storage',
-                '--no_resume',
-                '--platform=win32',
-                '--no_auth',
-                '--bucket', 'chromium-clang-format',
-                '-s', 'SPIRV-Tools/buildtools/win/clang-format.exe.sha1',
-    ],
-  },
-  {
-    'name': 'clang_format_mac',
-    'pattern': '.',
-    'action': [ 'download_from_google_storage',
-                '--no_resume',
-                '--platform=darwin',
-                '--no_auth',
-                '--bucket', 'chromium-clang-format',
-                '-s', 'SPIRV-Tools/buildtools/mac/clang-format.sha1',
-    ],
-  },
-  {
-    'name': 'clang_format_linux',
-    'pattern': '.',
-    'action': [ 'download_from_google_storage',
-                '--no_resume',
-                '--platform=linux*',
-                '--no_auth',
-                '--bucket', 'chromium-clang-format',
-                '-s', 'SPIRV-Tools/buildtools/linux64/clang-format.sha1',
-    ],
-  },
-  {
-    # Pull clang
-    'name': 'clang',
-    'pattern': '.',
-    'action': ['python',
-               'SPIRV-Tools/tools/clang/scripts/update.py'
-    ],
-  },
-  {
-    'name': 'sysroot_arm',
-    'pattern': '.',
-    'condition': 'checkout_linux and checkout_arm',
-    'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
-               '--arch=arm'],
-  },
-  {
-    'name': 'sysroot_arm64',
-    'pattern': '.',
-    'condition': 'checkout_linux and checkout_arm64',
-    'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
-               '--arch=arm64'],
-  },
-  {
-    'name': 'sysroot_x86',
-    'pattern': '.',
-    'condition': 'checkout_linux and (checkout_x86 or checkout_x64)',
-    'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
-               '--arch=x86'],
-  },
-  {
-    'name': 'sysroot_mips',
-    'pattern': '.',
-    'condition': 'checkout_linux and checkout_mips',
-    'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
-               '--arch=mips'],
-  },
-  {
-    'name': 'sysroot_x64',
-    'pattern': '.',
-    'condition': 'checkout_linux and checkout_x64',
-    'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
-               '--arch=x64'],
-  },
-  {
-    # Update the Windows toolchain if necessary.
-    'name': 'win_toolchain',
-    'pattern': '.',
-    'condition': 'checkout_win',
-    'action': ['python', 'SPIRV-Tools/build/vs_toolchain.py', 'update', '--force'],
-  },
-  {
-    # Update the Mac toolchain if necessary.
-    'name': 'mac_toolchain',
-    'pattern': '.',
-    'action': ['python', 'SPIRV-Tools/build/mac_toolchain.py'],
-  },
-]

+ 11 - 0
3rdparty/spirv-tools/README.md

@@ -270,6 +270,10 @@ mkdir build && cd build
 cmake [-G <platform-generator>] <spirv-dir>
 cmake [-G <platform-generator>] <spirv-dir>
 ```
 ```
 
 
+*Note*:
+The script `utils/git-sync-deps` can be used to checkout and/or update the
+contents of the repos under `external/` instead of manually maintaining them.
+
 Once the build files have been generated, build using your preferred
 Once the build files have been generated, build using your preferred
 development environment.
 development environment.
 
 
@@ -344,6 +348,13 @@ $ANDROID_NDK/ndk-build -C ../android_test     \
                       NDK_APP_OUT=`pwd`/app
                       NDK_APP_OUT=`pwd`/app
 ```
 ```
 
 
+### Updating DEPS
+Occasionally the entries in DEPS will need to be updated. This is done on demand
+when there is a request to do this, often due to downstream breakages. There is
+a script `utils/roll_deps.sh` provided, which will generate a patch with the
+updated DEPS values. This will still need to be tested in your checkout to
+confirm that there are no integration issues that need to be resolved.
+
 ## Library
 ## Library
 
 
 ### Usage
 ### Usage

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

@@ -1 +1 @@
-"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-47-g59983a60"
+"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-60-gdf86bb44"

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

@@ -32,6 +32,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass.h
         fuzzer_pass.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_useful_constructs.h
         fuzzer_pass_add_useful_constructs.h
+        fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_split_blocks.h
         fuzzer_pass_split_blocks.h
         fuzzer_util.h
         fuzzer_util.h
@@ -46,8 +47,10 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_type_boolean.h
         transformation_add_type_boolean.h
         transformation_add_type_float.h
         transformation_add_type_float.h
         transformation_add_type_int.h
         transformation_add_type_int.h
+        transformation_add_type_pointer.h
         transformation_move_block_down.h
         transformation_move_block_down.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_boolean_constant_with_constant_binary.h
+        transformation_replace_constant_with_uniform.h
         transformation_split_block.h
         transformation_split_block.h
         uniform_buffer_element_descriptor.h
         uniform_buffer_element_descriptor.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
@@ -58,6 +61,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass.cpp
         fuzzer_pass.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_useful_constructs.cpp
         fuzzer_pass_add_useful_constructs.cpp
+        fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_util.cpp
         fuzzer_util.cpp
@@ -71,8 +75,10 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_type_boolean.cpp
         transformation_add_type_boolean.cpp
         transformation_add_type_float.cpp
         transformation_add_type_float.cpp
         transformation_add_type_int.cpp
         transformation_add_type_int.cpp
+        transformation_add_type_pointer.cpp
         transformation_move_block_down.cpp
         transformation_move_block_down.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
+        transformation_replace_constant_with_uniform.cpp
         transformation_split_block.cpp
         transformation_split_block.cpp
         uniform_buffer_element_descriptor.cpp
         uniform_buffer_element_descriptor.cpp
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc

+ 48 - 11
3rdparty/spirv-tools/source/fuzz/fact_manager.cpp

@@ -64,6 +64,11 @@ struct FactManager::ConstantUniformFacts {
       const protobufs::FactConstantUniform& constant_uniform_fact,
       const protobufs::FactConstantUniform& constant_uniform_fact,
       uint32_t type_id) const;
       uint32_t type_id) const;
 
 
+  // Checks that the width of a floating-point constant is supported, and that
+  // the constant is finite.
+  bool FloatingPointValueIsSuitable(const protobufs::FactConstantUniform& fact,
+                                    uint32_t width) const;
+
   std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>
   std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>
       facts_and_type_ids;
       facts_and_type_ids;
 };
 };
@@ -169,22 +174,48 @@ FactManager::ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown()
   return result;
   return result;
 }
 }
 
 
-bool FactManager::ConstantUniformFacts::AddFact(
-    const protobufs::FactConstantUniform& fact, opt::IRContext* context) {
-  auto should_be_uniform_variable = context->get_def_use_mgr()->GetDef(
-      fact.uniform_buffer_element_descriptor().uniform_variable_id());
-  if (!should_be_uniform_variable) {
+bool FactManager::ConstantUniformFacts::FloatingPointValueIsSuitable(
+    const protobufs::FactConstantUniform& fact, uint32_t width) const {
+  const uint32_t kFloatWidth = 32;
+  const uint32_t kDoubleWidth = 64;
+  if (width != kFloatWidth && width != kDoubleWidth) {
+    // Only 32- and 64-bit floating-point types are handled.
     return false;
     return false;
   }
   }
-  if (SpvOpVariable != should_be_uniform_variable->opcode()) {
-    return false;
+  std::vector<uint32_t> words = GetConstantWords(fact);
+  if (width == 32) {
+    float value;
+    memcpy(&value, words.data(), sizeof(float));
+    if (!std::isfinite(value)) {
+      return false;
+    }
+  } else {
+    double value;
+    memcpy(&value, words.data(), sizeof(double));
+    if (!std::isfinite(value)) {
+      return false;
+    }
   }
   }
-  if (SpvStorageClassUniform !=
-      should_be_uniform_variable->GetSingleWordInOperand(0)) {
+  return true;
+}
+
+bool FactManager::ConstantUniformFacts::AddFact(
+    const protobufs::FactConstantUniform& fact, opt::IRContext* context) {
+  // Try to find a unique instruction that declares a variable such that the
+  // variable is decorated with the descriptor set and binding associated with
+  // the constant uniform fact.
+  opt::Instruction* uniform_variable = FindUniformVariable(
+      fact.uniform_buffer_element_descriptor(), context, true);
+
+  if (!uniform_variable) {
     return false;
     return false;
   }
   }
+
+  assert(SpvOpVariable == uniform_variable->opcode());
+  assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0));
+
   auto should_be_uniform_pointer_type =
   auto should_be_uniform_pointer_type =
-      context->get_type_mgr()->GetType(should_be_uniform_variable->type_id());
+      context->get_type_mgr()->GetType(uniform_variable->type_id());
   if (!should_be_uniform_pointer_type->AsPointer()) {
   if (!should_be_uniform_pointer_type->AsPointer()) {
     return false;
     return false;
   }
   }
@@ -193,7 +224,7 @@ bool FactManager::ConstantUniformFacts::AddFact(
     return false;
     return false;
   }
   }
   auto should_be_uniform_pointer_instruction =
   auto should_be_uniform_pointer_instruction =
-      context->get_def_use_mgr()->GetDef(should_be_uniform_variable->type_id());
+      context->get_def_use_mgr()->GetDef(uniform_variable->type_id());
   auto element_type =
   auto element_type =
       should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
       should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
 
 
@@ -236,6 +267,12 @@ bool FactManager::ConstantUniformFacts::AddFact(
   auto width = final_element_type->AsFloat()
   auto width = final_element_type->AsFloat()
                    ? final_element_type->AsFloat()->width()
                    ? final_element_type->AsFloat()->width()
                    : final_element_type->AsInteger()->width();
                    : final_element_type->AsInteger()->width();
+
+  if (final_element_type->AsFloat() &&
+      !FloatingPointValueIsSuitable(fact, width)) {
+    return false;
+  }
+
   auto required_words = (width + 32 - 1) / 32;
   auto required_words = (width + 32 - 1) / 32;
   if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
   if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
     return false;
     return false;

+ 4 - 2
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp

@@ -21,6 +21,7 @@
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
 #include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
 #include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
+#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@@ -113,8 +114,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
   FuzzerPassAddDeadBreaks(ir_context.get(), &fact_manager, &fuzzer_context,
   FuzzerPassAddDeadBreaks(ir_context.get(), &fact_manager, &fuzzer_context,
                           transformation_sequence_out)
                           transformation_sequence_out)
       .Apply();
       .Apply();
-
-  // TODO(afd) Various other passes will be added.
+  FuzzerPassObfuscateConstants(ir_context.get(), &fact_manager, &fuzzer_context,
+                               transformation_sequence_out)
+      .Apply();
 
 
   // Finally, give the blocks in the module a good shake-up.
   // Finally, give the blocks in the module a good shake-up.
   FuzzerPassPermuteBlocks(ir_context.get(), &fact_manager, &fuzzer_context,
   FuzzerPassPermuteBlocks(ir_context.get(), &fact_manager, &fuzzer_context,

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

@@ -14,6 +14,8 @@
 
 
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_context.h"
 
 
+#include <cmath>
+
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
 
 
@@ -24,8 +26,19 @@ namespace {
 
 
 const uint32_t kDefaultChanceOfAddingDeadBreak = 20;
 const uint32_t kDefaultChanceOfAddingDeadBreak = 20;
 const uint32_t kDefaultChanceOfMovingBlockDown = 25;
 const uint32_t kDefaultChanceOfMovingBlockDown = 25;
+const uint32_t kDefaultChanceOfObfuscatingConstant = 20;
 const uint32_t kDefaultChanceOfSplittingBlock = 20;
 const uint32_t kDefaultChanceOfSplittingBlock = 20;
 
 
+// Default functions for controlling how deep to go during recursive
+// generation/transformation. Keep them in alphabetical order.
+
+const std::function<bool(uint32_t, RandomGenerator*)>
+    kDefaultGoDeeperInConstantObfuscation =
+        [](uint32_t current_depth, RandomGenerator* random_generator) -> bool {
+  double chance = 1.0 / std::pow(3.0, static_cast<float>(current_depth + 1));
+  return random_generator->RandomDouble() < chance;
+};
+
 }  // namespace
 }  // namespace
 
 
 FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
 FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
@@ -34,7 +47,10 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       next_fresh_id_(min_fresh_id),
       next_fresh_id_(min_fresh_id),
       chance_of_adding_dead_break_(kDefaultChanceOfAddingDeadBreak),
       chance_of_adding_dead_break_(kDefaultChanceOfAddingDeadBreak),
       chance_of_moving_block_down_(kDefaultChanceOfMovingBlockDown),
       chance_of_moving_block_down_(kDefaultChanceOfMovingBlockDown),
-      chance_of_splitting_block_(kDefaultChanceOfSplittingBlock) {}
+      chance_of_obfuscating_constant_(kDefaultChanceOfObfuscatingConstant),
+      chance_of_splitting_block_(kDefaultChanceOfSplittingBlock),
+      go_deeper_in_constant_obfuscation_(
+          kDefaultGoDeeperInConstantObfuscation) {}
 
 
 FuzzerContext::~FuzzerContext() = default;
 FuzzerContext::~FuzzerContext() = default;
 
 

+ 18 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_context.h

@@ -15,6 +15,8 @@
 #ifndef SOURCE_FUZZ_FUZZER_CONTEXT_H_
 #ifndef SOURCE_FUZZ_FUZZER_CONTEXT_H_
 #define SOURCE_FUZZ_FUZZER_CONTEXT_H_
 #define SOURCE_FUZZ_FUZZER_CONTEXT_H_
 
 
+#include <functional>
+
 #include "source/fuzz/random_generator.h"
 #include "source/fuzz/random_generator.h"
 #include "source/opt/function.h"
 #include "source/opt/function.h"
 
 
@@ -43,8 +45,18 @@ class FuzzerContext {
   // Keep them in alphabetical order.
   // Keep them in alphabetical order.
   uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
   uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
   uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
   uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
+  uint32_t GetChanceOfObfuscatingConstant() {
+    return chance_of_obfuscating_constant_;
+  }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
 
 
+  // Probability distributions to control how deeply to recurse.
+  // Keep them in alphabetical order.
+  const std::function<bool(uint32_t, RandomGenerator*)>&
+  GoDeeperInConstantObfuscation() {
+    return go_deeper_in_constant_obfuscation_;
+  }
+
  private:
  private:
   // The source of randomness.
   // The source of randomness.
   RandomGenerator* random_generator_;
   RandomGenerator* random_generator_;
@@ -55,7 +67,13 @@ class FuzzerContext {
   // Keep them in alphabetical order.
   // Keep them in alphabetical order.
   uint32_t chance_of_adding_dead_break_;
   uint32_t chance_of_adding_dead_break_;
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_moving_block_down_;
+  uint32_t chance_of_obfuscating_constant_;
   uint32_t chance_of_splitting_block_;
   uint32_t chance_of_splitting_block_;
+
+  // Functions to determine with what probability to go deeper when generating
+  // or mutating constructs recursively.
+  const std::function<bool(uint32_t, RandomGenerator*)>&
+      go_deeper_in_constant_obfuscation_;
 };
 };
 
 
 }  // namespace fuzz
 }  // namespace fuzz

+ 50 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp

@@ -19,6 +19,7 @@
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_float.h"
 #include "source/fuzz/transformation_add_type_float.h"
 #include "source/fuzz/transformation_add_type_int.h"
 #include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
 
 
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
@@ -176,6 +177,55 @@ void FuzzerPassAddUsefulConstructs::Apply() {
   for (unsigned int& datum : uint_data) {
   for (unsigned int& datum : uint_data) {
     MaybeAddFloatConstant(32, {datum});
     MaybeAddFloatConstant(32, {datum});
   }
   }
+
+  // For every known-to-be-constant uniform, make sure we have instructions
+  // declaring:
+  // - a pointer type with uniform storage class, whose pointee type is the type
+  //   of the element
+  // - a signed integer constant for each index required to access the element
+  // - a constant for the constant value itself
+  for (auto& fact_and_type_id :
+       GetFactManager()->GetConstantUniformFactsAndTypes()) {
+    uint32_t element_type_id = fact_and_type_id.second;
+    assert(element_type_id);
+    auto element_type =
+        GetIRContext()->get_type_mgr()->GetType(element_type_id);
+    assert(element_type &&
+           "If the constant uniform fact is well-formed, the module must "
+           "already have a declaration of the type for the uniform element.");
+    opt::analysis::Pointer uniform_pointer(element_type,
+                                           SpvStorageClassUniform);
+    if (!GetIRContext()->get_type_mgr()->GetId(&uniform_pointer)) {
+      auto add_pointer = transformation::MakeTransformationAddTypePointer(
+          GetFuzzerContext()->GetFreshId(), SpvStorageClassUniform,
+          element_type_id);
+      assert(transformation::IsApplicable(add_pointer, GetIRContext(),
+                                          *GetFactManager()) &&
+             "Should be applicable by construction.");
+      transformation::Apply(add_pointer, GetIRContext(), GetFactManager());
+      *GetTransformations()->add_transformation()->mutable_add_type_pointer() =
+          add_pointer;
+    }
+    std::vector<uint32_t> words;
+    for (auto word : fact_and_type_id.first.constant_word()) {
+      words.push_back(word);
+    }
+    // We get the element type again as the type manager may have been
+    // invalidated since we last retrieved it.
+    element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id);
+    if (element_type->AsInteger()) {
+      MaybeAddIntConstant(element_type->AsInteger()->width(),
+                          element_type->AsInteger()->IsSigned(), words);
+    } else {
+      assert(element_type->AsFloat() &&
+             "Known uniform values must be integer or floating-point.");
+      MaybeAddFloatConstant(element_type->AsFloat()->width(), words);
+    }
+    for (auto index :
+         fact_and_type_id.first.uniform_buffer_element_descriptor().index()) {
+      MaybeAddIntConstant(32, true, {index});
+    }
+  }
 }
 }
 
 
 }  // namespace fuzz
 }  // namespace fuzz

+ 471 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp

@@ -0,0 +1,471 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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/fuzz/fuzzer_pass_obfuscate_constants.h"
+
+#include <cmath>
+
+#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
+#include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassObfuscateConstants::~FuzzerPassObfuscateConstants() = default;
+
+void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
+    uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
+    const std::vector<SpvOp>& greater_than_opcodes,
+    const std::vector<SpvOp>& less_than_opcodes, uint32_t constant_id_1,
+    uint32_t constant_id_2, bool first_constant_is_larger) {
+  auto bool_constant_opcode = GetIRContext()
+                                  ->get_def_use_mgr()
+                                  ->GetDef(bool_constant_use.id_of_interest())
+                                  ->opcode();
+  assert((bool_constant_opcode == SpvOpConstantFalse ||
+          bool_constant_opcode == SpvOpConstantTrue) &&
+         "Precondition: this must be a usage of a boolean constant.");
+
+  // Pick an opcode at random.  First randomly decide whether to generate
+  // a 'greater than' or 'less than' kind of opcode, and then select a
+  // random opcode from the resulting subset.
+  SpvOp comparison_opcode;
+  if (GetFuzzerContext()->GetRandomGenerator()->RandomBool()) {
+    comparison_opcode = greater_than_opcodes
+        [GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
+            static_cast<uint32_t>(greater_than_opcodes.size()))];
+  } else {
+    comparison_opcode = less_than_opcodes
+        [GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
+            static_cast<uint32_t>(less_than_opcodes.size()))];
+  }
+
+  // We now need to decide how to order constant_id_1 and constant_id_2 such
+  // that 'constant_id_1 comparison_opcode constant_id_2' evaluates to the
+  // boolean constant.
+  const bool is_greater_than_opcode =
+      std::find(greater_than_opcodes.begin(), greater_than_opcodes.end(),
+                comparison_opcode) != greater_than_opcodes.end();
+  uint32_t lhs_id;
+  uint32_t rhs_id;
+  if ((bool_constant_opcode == SpvOpConstantTrue &&
+       first_constant_is_larger == is_greater_than_opcode) ||
+      (bool_constant_opcode == SpvOpConstantFalse &&
+       first_constant_is_larger != is_greater_than_opcode)) {
+    lhs_id = constant_id_1;
+    rhs_id = constant_id_2;
+  } else {
+    lhs_id = constant_id_2;
+    rhs_id = constant_id_1;
+  }
+
+  // We can now make a transformation that will replace |bool_constant_use|
+  // with an expression of the form (written using infix notation):
+  // |lhs_id| |comparison_opcode| |rhs_id|
+  auto transformation = transformation::
+      MakeTransformationReplaceBooleanConstantWithConstantBinary(
+          bool_constant_use, lhs_id, rhs_id, comparison_opcode,
+          GetFuzzerContext()->GetFreshId());
+  // The transformation should be applicable by construction.
+  assert(transformation::IsApplicable(transformation, GetIRContext(),
+                                      *GetFactManager()));
+
+  // Applying this transformation yields a pointer to the new instruction that
+  // computes the result of the binary expression.
+  auto binary_operator_instruction =
+      transformation::Apply(transformation, GetIRContext(), GetFactManager());
+
+  // Add this transformation to the sequence of transformations that have been
+  // applied.
+  *GetTransformations()
+       ->add_transformation()
+       ->mutable_replace_boolean_constant_with_constant_binary() =
+      transformation;
+
+  // Having made a binary expression, there may now be opportunities to further
+  // obfuscate the constants used as the LHS and RHS of the expression (e.g. by
+  // replacing them with loads from known uniforms).
+  //
+  // We thus consider operands 0 and 1 (LHS and RHS in turn).
+  for (uint32_t index : {0u, 1u}) {
+    // We randomly decide, based on the current depth of obfuscation, whether
+    // to further obfuscate this operand.
+    if (GetFuzzerContext()->GoDeeperInConstantObfuscation()(
+            depth, GetFuzzerContext()->GetRandomGenerator())) {
+      auto in_operand_use = transformation::MakeIdUseDescriptor(
+          binary_operator_instruction->GetSingleWordInOperand(index),
+          binary_operator_instruction->opcode(), index,
+          binary_operator_instruction->result_id(), 0);
+      ObfuscateConstant(depth + 1, in_operand_use);
+    }
+  }
+}
+
+void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaFloatConstantPair(
+    uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
+    uint32_t float_constant_id_1, uint32_t float_constant_id_2) {
+  auto float_constant_1 = GetIRContext()
+                              ->get_constant_mgr()
+                              ->FindDeclaredConstant(float_constant_id_1)
+                              ->AsFloatConstant();
+  auto float_constant_2 = GetIRContext()
+                              ->get_constant_mgr()
+                              ->FindDeclaredConstant(float_constant_id_2)
+                              ->AsFloatConstant();
+  assert(float_constant_1->words() != float_constant_2->words() &&
+         "The constants should not be identical.");
+  assert(std::isfinite(float_constant_1->GetValueAsDouble()) &&
+         "The constants must be finite numbers.");
+  assert(std::isfinite(float_constant_2->GetValueAsDouble()) &&
+         "The constants must be finite numbers.");
+  bool first_constant_is_larger;
+  assert(float_constant_1->type()->AsFloat()->width() ==
+             float_constant_2->type()->AsFloat()->width() &&
+         "First and second floating-point constants must have the same width.");
+  if (float_constant_1->type()->AsFloat()->width() == 32) {
+    first_constant_is_larger =
+        float_constant_1->GetFloat() > float_constant_2->GetFloat();
+  } else {
+    assert(float_constant_1->type()->AsFloat()->width() == 64 &&
+           "Supported floating-point widths are 32 and 64.");
+    first_constant_is_larger =
+        float_constant_1->GetDouble() > float_constant_2->GetDouble();
+  }
+  std::vector<SpvOp> greater_than_opcodes{
+      SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
+      SpvOpFUnordGreaterThanEqual};
+  std::vector<SpvOp> less_than_opcodes{
+      SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
+      SpvOpFUnordGreaterThanEqual};
+
+  ObfuscateBoolConstantViaConstantPair(
+      depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
+      float_constant_id_1, float_constant_id_2, first_constant_is_larger);
+}
+
+void FuzzerPassObfuscateConstants::
+    ObfuscateBoolConstantViaSignedIntConstantPair(
+        uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
+        uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2) {
+  auto signed_int_constant_1 =
+      GetIRContext()
+          ->get_constant_mgr()
+          ->FindDeclaredConstant(signed_int_constant_id_1)
+          ->AsIntConstant();
+  auto signed_int_constant_2 =
+      GetIRContext()
+          ->get_constant_mgr()
+          ->FindDeclaredConstant(signed_int_constant_id_2)
+          ->AsIntConstant();
+  assert(signed_int_constant_1->words() != signed_int_constant_2->words() &&
+         "The constants should not be identical.");
+  bool first_constant_is_larger;
+  assert(signed_int_constant_1->type()->AsInteger()->width() ==
+             signed_int_constant_2->type()->AsInteger()->width() &&
+         "First and second floating-point constants must have the same width.");
+  assert(signed_int_constant_1->type()->AsInteger()->IsSigned());
+  assert(signed_int_constant_2->type()->AsInteger()->IsSigned());
+  if (signed_int_constant_1->type()->AsFloat()->width() == 32) {
+    first_constant_is_larger =
+        signed_int_constant_1->GetS32() > signed_int_constant_2->GetS32();
+  } else {
+    assert(signed_int_constant_1->type()->AsFloat()->width() == 64 &&
+           "Supported integer widths are 32 and 64.");
+    first_constant_is_larger =
+        signed_int_constant_1->GetS64() > signed_int_constant_2->GetS64();
+  }
+  std::vector<SpvOp> greater_than_opcodes{SpvOpSGreaterThan,
+                                          SpvOpSGreaterThanEqual};
+  std::vector<SpvOp> less_than_opcodes{SpvOpSLessThan, SpvOpSLessThanEqual};
+
+  ObfuscateBoolConstantViaConstantPair(
+      depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
+      signed_int_constant_id_1, signed_int_constant_id_2,
+      first_constant_is_larger);
+}
+
+void FuzzerPassObfuscateConstants::
+    ObfuscateBoolConstantViaUnsignedIntConstantPair(
+        uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
+        uint32_t unsigned_int_constant_id_1,
+        uint32_t unsigned_int_constant_id_2) {
+  auto unsigned_int_constant_1 =
+      GetIRContext()
+          ->get_constant_mgr()
+          ->FindDeclaredConstant(unsigned_int_constant_id_1)
+          ->AsIntConstant();
+  auto unsigned_int_constant_2 =
+      GetIRContext()
+          ->get_constant_mgr()
+          ->FindDeclaredConstant(unsigned_int_constant_id_2)
+          ->AsIntConstant();
+  assert(unsigned_int_constant_1->words() != unsigned_int_constant_2->words() &&
+         "The constants should not be identical.");
+  bool first_constant_is_larger;
+  assert(unsigned_int_constant_1->type()->AsInteger()->width() ==
+             unsigned_int_constant_2->type()->AsInteger()->width() &&
+         "First and second floating-point constants must have the same width.");
+  assert(!unsigned_int_constant_1->type()->AsInteger()->IsSigned());
+  assert(!unsigned_int_constant_2->type()->AsInteger()->IsSigned());
+  if (unsigned_int_constant_1->type()->AsFloat()->width() == 32) {
+    first_constant_is_larger =
+        unsigned_int_constant_1->GetU32() > unsigned_int_constant_2->GetU32();
+  } else {
+    assert(unsigned_int_constant_1->type()->AsFloat()->width() == 64 &&
+           "Supported integer widths are 32 and 64.");
+    first_constant_is_larger =
+        unsigned_int_constant_1->GetU64() > unsigned_int_constant_2->GetU64();
+  }
+  std::vector<SpvOp> greater_than_opcodes{SpvOpUGreaterThan,
+                                          SpvOpUGreaterThanEqual};
+  std::vector<SpvOp> less_than_opcodes{SpvOpULessThan, SpvOpULessThanEqual};
+
+  ObfuscateBoolConstantViaConstantPair(
+      depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
+      unsigned_int_constant_id_1, unsigned_int_constant_id_2,
+      first_constant_is_larger);
+}
+
+void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
+    uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
+  // We want to replace the boolean constant use with a binary expression over
+  // scalar constants, but only if we can then potentially replace the constants
+  // with uniforms of the same value.
+
+  auto available_types_with_uniforms =
+      GetFactManager()->GetTypesForWhichUniformValuesAreKnown();
+  if (available_types_with_uniforms.empty()) {
+    // Do not try to obfuscate if we do not have access to any uniform
+    // elements with known values.
+    return;
+  }
+  auto chosen_type_id = available_types_with_uniforms
+      [GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
+          static_cast<uint32_t>(available_types_with_uniforms.size()))];
+  auto available_constants =
+      GetFactManager()->GetConstantsAvailableFromUniformsForType(
+          GetIRContext(), chosen_type_id);
+  if (available_constants.size() == 1) {
+    // TODO(afd): for now we only obfuscate a boolean if there are at least
+    //  two constants available from uniforms, so that we can do a
+    //  comparison between them. It would be good to be able to do the
+    //  obfuscation even if there is only one such constant, if there is
+    //  also another regular constant available.
+    return;
+  }
+
+  // We know we have at least two known-to-be-constant uniforms of the chosen
+  // type.  Pick one of them at random.
+  auto constant_index_1 =
+      GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
+          static_cast<uint32_t>(available_constants.size()));
+  uint32_t constant_index_2;
+
+  // Now choose another one distinct from the first one.
+  do {
+    constant_index_2 = GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
+        static_cast<uint32_t>(available_constants.size()));
+  } while (constant_index_1 == constant_index_2);
+
+  auto constant_id_1 = available_constants[constant_index_1];
+  auto constant_id_2 = available_constants[constant_index_2];
+
+  assert(constant_id_1 != 0 && constant_id_2 != 0 &&
+         "We should not find an available constant with an id of 0.");
+
+  // Now perform the obfuscation, according to whether the type of the constants
+  // is float, signed int, or unsigned int.
+  auto chosen_type = GetIRContext()->get_type_mgr()->GetType(chosen_type_id);
+  if (chosen_type->AsFloat()) {
+    ObfuscateBoolConstantViaFloatConstantPair(depth, constant_use,
+                                              constant_id_1, constant_id_2);
+  } else {
+    assert(chosen_type->AsInteger() &&
+           "We should only have uniform facts about ints and floats.");
+    if (chosen_type->AsInteger()->IsSigned()) {
+      ObfuscateBoolConstantViaSignedIntConstantPair(
+          depth, constant_use, constant_id_1, constant_id_2);
+    } else {
+      ObfuscateBoolConstantViaUnsignedIntConstantPair(
+          depth, constant_use, constant_id_1, constant_id_2);
+    }
+  }
+}
+
+void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
+    uint32_t /*depth*/, const protobufs::IdUseDescriptor& constant_use) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2670): consider
+  //  additional ways to obfuscate scalar constants.
+
+  // Check whether we know that any uniforms are guaranteed to be equal to the
+  // scalar constant associated with |constant_use|.
+  auto uniform_descriptors = GetFactManager()->GetUniformDescriptorsForConstant(
+      GetIRContext(), constant_use.id_of_interest());
+  if (uniform_descriptors.empty()) {
+    // No relevant uniforms, so do not obfuscate.
+    return;
+  }
+
+  // Choose a random available uniform known to be equal to the constant.
+  protobufs::UniformBufferElementDescriptor uniform_descriptor =
+      uniform_descriptors
+          [GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
+              static_cast<uint32_t>(uniform_descriptors.size()))];
+  // Create, apply and record a transformation to replace the constant use with
+  // the result of a load from the chosen uniform.
+  auto transformation =
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
+          GetFuzzerContext()->GetFreshId());
+  // Transformation should be applicable by construction.
+  assert(transformation::IsApplicable(transformation, GetIRContext(),
+                                      *GetFactManager()));
+  transformation::Apply(transformation, GetIRContext(), GetFactManager());
+  *GetTransformations()
+       ->add_transformation()
+       ->mutable_replace_constant_with_uniform() = transformation;
+}
+
+void FuzzerPassObfuscateConstants::ObfuscateConstant(
+    uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
+  switch (GetIRContext()
+              ->get_def_use_mgr()
+              ->GetDef(constant_use.id_of_interest())
+              ->opcode()) {
+    case SpvOpConstantTrue:
+    case SpvOpConstantFalse:
+      ObfuscateBoolConstant(depth, constant_use);
+      break;
+    case SpvOpConstant:
+      ObfuscateScalarConstant(depth, constant_use);
+      break;
+    default:
+      assert(false && "The opcode should be one of the above.");
+      break;
+  }
+}
+
+void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse(
+    const opt::Instruction& inst, uint32_t in_operand_index,
+    uint32_t base_instruction_result_id,
+    const std::map<SpvOp, uint32_t>& skipped_opcode_count,
+    std::vector<protobufs::IdUseDescriptor>* constant_uses) {
+  if (inst.GetInOperand(in_operand_index).type != SPV_OPERAND_TYPE_ID) {
+    // The operand is not an id, so it cannot be a constant id.
+    return;
+  }
+  auto operand_id = inst.GetSingleWordInOperand(in_operand_index);
+  auto operand_definition =
+      GetIRContext()->get_def_use_mgr()->GetDef(operand_id);
+  switch (operand_definition->opcode()) {
+    case SpvOpConstantFalse:
+    case SpvOpConstantTrue:
+    case SpvOpConstant: {
+      // The operand is a constant id, so make an id use descriptor and record
+      // it.
+      protobufs::IdUseDescriptor id_use_descriptor;
+      id_use_descriptor.set_id_of_interest(operand_id);
+      id_use_descriptor.set_target_instruction_opcode(inst.opcode());
+      id_use_descriptor.set_in_operand_index(in_operand_index);
+      id_use_descriptor.set_base_instruction_result_id(
+          base_instruction_result_id);
+      id_use_descriptor.set_num_opcodes_to_ignore(
+          skipped_opcode_count.find(inst.opcode()) == skipped_opcode_count.end()
+              ? 0
+              : skipped_opcode_count.at(inst.opcode()));
+      constant_uses->push_back(id_use_descriptor);
+    } break;
+    default:
+      break;
+  }
+}
+
+void FuzzerPassObfuscateConstants::Apply() {
+  // First, gather up all the constant uses available in the module, by going
+  // through each block in each function.
+  std::vector<protobufs::IdUseDescriptor> constant_uses;
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      // For each constant use we encounter we are going to make an id use
+      // descriptor. An id use is described with respect to a base instruction;
+      // if there are instructions at the start of the block without result ids,
+      // the base instruction will have to be the block's label.
+      uint32_t base_instruction_result_id = block.id();
+
+      // An id use descriptor also records how many instructions of a particular
+      // opcode need to be skipped in order to find the instruction of interest
+      // from the base instruction. We maintain a mapping that records a skip
+      // count for each relevant opcode.
+      std::map<SpvOp, uint32_t> skipped_opcode_count;
+
+      // Go through each instruction in the block.
+      for (auto& inst : block) {
+        if (inst.HasResultId()) {
+          // The instruction has a result id, so can be used as the base
+          // instruction from now on, until another instruction with a result id
+          // is encountered.
+          base_instruction_result_id = inst.result_id();
+          // Opcode skip counts were with respect to the previous base
+          // instruction and are now irrelevant.
+          skipped_opcode_count.clear();
+        }
+
+        // Consider each operand of the instruction, and add a constant id use
+        // for the operand if relevant.
+        for (uint32_t in_operand_index = 0;
+             in_operand_index < inst.NumInOperands(); in_operand_index++) {
+          MaybeAddConstantIdUse(inst, in_operand_index,
+                                base_instruction_result_id,
+                                skipped_opcode_count, &constant_uses);
+        }
+
+        if (!inst.HasResultId()) {
+          // The instruction has no result id, so in order to identify future id
+          // uses for instructions with this opcode from the existing base
+          // instruction, we need to increase the skip count for this opcode.
+          skipped_opcode_count[inst.opcode()] =
+              skipped_opcode_count.find(inst.opcode()) ==
+                      skipped_opcode_count.end()
+                  ? 1
+                  : skipped_opcode_count[inst.opcode()] + 1;
+        }
+      }
+    }
+  }
+
+  // Go through the constant uses in a random order by repeatedly pulling out a
+  // constant use at a random index.
+  while (!constant_uses.empty()) {
+    auto index = GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
+        static_cast<uint32_t>(constant_uses.size()));
+    auto constant_use = std::move(constant_uses[index]);
+    constant_uses.erase(constant_uses.begin() + index);
+    // Decide probabilistically whether to skip or obfuscate this constant use.
+    if (GetFuzzerContext()->GetRandomGenerator()->RandomPercentage() >
+        GetFuzzerContext()->GetChanceOfObfuscatingConstant()) {
+      continue;
+    }
+    ObfuscateConstant(0, constant_use);
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 107 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h

@@ -0,0 +1,107 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_
+#define SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for turning uses of constants into more complex forms.
+// Examples include replacing 'true' with '42 < 52', and replacing '42' with
+// 'a.b.c' if 'a.b.c' is known to hold the value '42'.
+class FuzzerPassObfuscateConstants : public FuzzerPass {
+ public:
+  FuzzerPassObfuscateConstants(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassObfuscateConstants() override;
+
+  void Apply() override;
+
+ private:
+  // Applies 0 or more transformations to potentially obfuscate the constant
+  // use represented by |constant_use|.  The |depth| parameter controls how
+  // deeply obfuscation can recurse.
+  void ObfuscateConstant(uint32_t depth,
+                         const protobufs::IdUseDescriptor& constant_use);
+
+  // This method will try to turn |constant_use|, required to be a use of a
+  // boolean constant, into a binary expression on scalar constants, which may
+  // themselves be recursively obfuscated.
+  void ObfuscateBoolConstant(uint32_t depth,
+                             const protobufs::IdUseDescriptor& constant_use);
+
+  // This method will try to turn |constant_use|, required to be a use of a
+  // scalar constant, into the value loaded from a uniform known to have the
+  // same value as the constant (if one exists).
+  void ObfuscateScalarConstant(uint32_t depth,
+                               const protobufs::IdUseDescriptor& constant_use);
+
+  // Applies a transformation to replace the boolean constant usage represented
+  // by |bool_constant_use| with a binary expression involving
+  // |float_constant_id_1| and |float_constant_id_2|, which must not be equal
+  // to one another.  Possibly further obfuscates the uses of these float
+  // constants.  The |depth| parameter controls how deeply obfuscation can
+  // recurse.
+  void ObfuscateBoolConstantViaFloatConstantPair(
+      uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
+      uint32_t float_constant_id_1, uint32_t float_constant_id_2);
+
+  // Similar to the above, but for signed int constants.
+  void ObfuscateBoolConstantViaSignedIntConstantPair(
+      uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
+      uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2);
+
+  // Similar to the above, but for unsigned int constants.
+  void ObfuscateBoolConstantViaUnsignedIntConstantPair(
+      uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
+      uint32_t unsigned_int_constant_id_1, uint32_t unsigned_int_constant_id_2);
+
+  // A helper method to capture the common parts of the above methods.
+  // The method is used to obfuscate the boolean constant usage represented by
+  // |bool_constant_use| by replacing it with '|constant_id_1| OP
+  // |constant_id_2|', where 'OP' is chosen from either |greater_than_opcodes|
+  // or |less_than_opcodes|.
+  //
+  // The two constant ids must not represent the same value, and thus
+  // |greater_than_opcodes| may include 'greater than or equal' opcodes
+  // (similar for |less_than_opcodes|).
+  void ObfuscateBoolConstantViaConstantPair(
+      uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
+      const std::vector<SpvOp>& greater_than_opcodes,
+      const std::vector<SpvOp>& less_than_opcodes, uint32_t constant_id_1,
+      uint32_t constant_id_2, bool first_constant_is_larger);
+
+  // A helper method to determine whether input operand |in_operand_index| of
+  // |inst| is the id of a constant, and add an id use descriptor to
+  // |candidate_constant_uses| if so.  The other parameters are used for id use
+  // descriptor construction.
+  void MaybeAddConstantIdUse(
+      const opt::Instruction& inst, uint32_t in_operand_index,
+      uint32_t base_instruction_result_id,
+      const std::map<SpvOp, uint32_t>& skipped_opcode_count,
+      std::vector<protobufs::IdUseDescriptor>* constant_uses);
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // #define SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_

+ 46 - 8
3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto

@@ -64,8 +64,9 @@ message UniformBufferElementDescriptor {
   // is contained, and (b) a series of indices that need to be followed to get
   // is contained, and (b) a series of indices that need to be followed to get
   // to the element (via fields and array/vector indices).
   // to the element (via fields and array/vector indices).
   //
   //
-  // Example: suppose %42 is the id of a uniform variable, and that the uniform
-  // variable has the following type (using GLSL-like syntax):
+  // Example: suppose there is a uniform variable with descriptor set 7 and
+  // binding 9, and that the uniform variable has the following type (using
+  // GLSL-like syntax):
   //
   //
   // struct S {
   // struct S {
   //   float f;
   //   float f;
@@ -74,16 +75,17 @@ message UniformBufferElementDescriptor {
   // };
   // };
   //
   //
   // Then:
   // Then:
-  // - 42[0] describes the 'f' field.
-  // - 42[1,1] describes the y component of the 'g' field.
-  // - 42[2,7,3] describes the w component of element 7 of the 'h' field
+  // - (7, 9, [0]) describes the 'f' field.
+  // - (7, 9, [1,1]) describes the y component of the 'g' field.
+  // - (7, 9, [2,7,3]) describes the w component of element 7 of the 'h' field
 
 
-  // The result id of a uniform variable.
-  uint32 uniform_variable_id = 1;
+  // The descriptor set and binding associated with a uniform variable.
+  uint32 descriptor_set = 1;
+  uint32 binding = 2;
 
 
   // An ordered sequence of indices through composite structures in the
   // An ordered sequence of indices through composite structures in the
   // uniform buffer.
   // uniform buffer.
-  repeated uint32 index = 2;
+  repeated uint32 index = 3;
 
 
 }
 }
 
 
@@ -133,6 +135,8 @@ message Transformation {
     TransformationAddTypeInt add_type_int = 7;
     TransformationAddTypeInt add_type_int = 7;
     TransformationAddDeadBreak add_dead_break = 8;
     TransformationAddDeadBreak add_dead_break = 8;
     TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9;
     TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9;
+    TransformationAddTypePointer add_type_pointer = 10;
+    TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
     // Add additional option using the next available number.
     // Add additional option using the next available number.
   }
   }
 }
 }
@@ -222,6 +226,22 @@ message TransformationAddTypeInt {
 
 
 }
 }
 
 
+message TransformationAddTypePointer {
+
+  // Adds OpTypePointer to the module, with the given storage class and base
+  // type
+
+  // Id to be used for the type
+  uint32 fresh_id = 1;
+
+  // Pointer storage class
+  uint32 storage_class = 2;
+
+  // Id of the base type for the pointer
+  uint32 base_type_id = 3;
+
+}
+
 message TransformationMoveBlockDown {
 message TransformationMoveBlockDown {
 
 
   // A transformation that moves a basic block to be one position lower in
   // A transformation that moves a basic block to be one position lower in
@@ -229,6 +249,24 @@ message TransformationMoveBlockDown {
 
 
   // The id of the block to move down.
   // The id of the block to move down.
   uint32 block_id = 1;
   uint32 block_id = 1;
+}
+
+message TransformationReplaceConstantWithUniform {
+
+  // Replaces a use of a constant id with the the result of a load from an
+  // element of uniform buffer known to hold the same value as the constant
+
+  // A descriptor for the id we would like to replace
+  IdUseDescriptor id_use_descriptor = 1;
+
+  // Uniform descriptor to identify which uniform value to choose
+  UniformBufferElementDescriptor uniform_descriptor = 2;
+
+  // Id that will store the result of an access chain
+  uint32 fresh_id_for_access_chain = 3;
+
+  // Id that will store the result of a load
+  uint32 fresh_id_for_load = 4;
 
 
 }
 }
 
 

+ 19 - 0
3rdparty/spirv-tools/source/fuzz/replayer.cpp

@@ -24,8 +24,10 @@
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_float.h"
 #include "source/fuzz/transformation_add_type_float.h"
 #include "source/fuzz/transformation_add_type_int.h"
 #include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
 #include "source/fuzz/transformation_move_block_down.h"
 #include "source/fuzz/transformation_move_block_down.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
+#include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/transformation_split_block.h"
 #include "source/fuzz/transformation_split_block.h"
 #include "source/opt/build_module.h"
 #include "source/opt/build_module.h"
 #include "source/util/make_unique.h"
 #include "source/util/make_unique.h"
@@ -58,6 +60,9 @@ bool IsApplicable(const protobufs::Transformation& transformation,
     case protobufs::Transformation::TransformationCase::kAddTypeInt:
     case protobufs::Transformation::TransformationCase::kAddTypeInt:
       return transformation::IsApplicable(transformation.add_type_int(),
       return transformation::IsApplicable(transformation.add_type_int(),
                                           context, fact_manager);
                                           context, fact_manager);
+    case protobufs::Transformation::TransformationCase::kAddTypePointer:
+      return transformation::IsApplicable(transformation.add_type_pointer(),
+                                          context, fact_manager);
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
       return transformation::IsApplicable(transformation.move_block_down(),
       return transformation::IsApplicable(transformation.move_block_down(),
                                           context, fact_manager);
                                           context, fact_manager);
@@ -66,6 +71,11 @@ bool IsApplicable(const protobufs::Transformation& transformation,
       return transformation::IsApplicable(
       return transformation::IsApplicable(
           transformation.replace_boolean_constant_with_constant_binary(),
           transformation.replace_boolean_constant_with_constant_binary(),
           context, fact_manager);
           context, fact_manager);
+    case protobufs::Transformation::TransformationCase::
+        kReplaceConstantWithUniform:
+      return transformation::IsApplicable(
+          transformation.replace_constant_with_uniform(), context,
+          fact_manager);
     case protobufs::Transformation::TransformationCase::kSplitBlock:
     case protobufs::Transformation::TransformationCase::kSplitBlock:
       return transformation::IsApplicable(transformation.split_block(), context,
       return transformation::IsApplicable(transformation.split_block(), context,
                                           fact_manager);
                                           fact_manager);
@@ -107,6 +117,10 @@ void Apply(const protobufs::Transformation& transformation,
       transformation::Apply(transformation.add_type_int(), context,
       transformation::Apply(transformation.add_type_int(), context,
                             fact_manager);
                             fact_manager);
       break;
       break;
+    case protobufs::Transformation::TransformationCase::kAddTypePointer:
+      transformation::Apply(transformation.add_type_pointer(), context,
+                            fact_manager);
+      break;
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
       transformation::Apply(transformation.move_block_down(), context,
       transformation::Apply(transformation.move_block_down(), context,
                             fact_manager);
                             fact_manager);
@@ -117,6 +131,11 @@ void Apply(const protobufs::Transformation& transformation,
           transformation.replace_boolean_constant_with_constant_binary(),
           transformation.replace_boolean_constant_with_constant_binary(),
           context, fact_manager);
           context, fact_manager);
       break;
       break;
+    case protobufs::Transformation::TransformationCase::
+        kReplaceConstantWithUniform:
+      transformation::Apply(transformation.replace_constant_with_uniform(),
+                            context, fact_manager);
+      break;
     case protobufs::Transformation::TransformationCase::kSplitBlock:
     case protobufs::Transformation::TransformationCase::kSplitBlock:
       transformation::Apply(transformation.split_block(), context,
       transformation::Apply(transformation.split_block(), context,
                             fact_manager);
                             fact_manager);

+ 61 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp

@@ -0,0 +1,61 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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/fuzz/transformation_add_type_pointer.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace transformation {
+
+using opt::IRContext;
+
+bool IsApplicable(const protobufs::TransformationAddTypePointer& message,
+                  IRContext* context,
+                  const spvtools::fuzz::FactManager& /*unused*/) {
+  // The id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message.fresh_id())) {
+    return false;
+  }
+  // The base type must be known.
+  return context->get_type_mgr()->GetType(message.base_type_id()) != nullptr;
+}
+
+void Apply(const protobufs::TransformationAddTypePointer& message,
+           IRContext* context, spvtools::fuzz::FactManager* /*unused*/) {
+  // Add the pointer type.
+  opt::Instruction::OperandList in_operands = {
+      {SPV_OPERAND_TYPE_STORAGE_CLASS, {message.storage_class()}},
+      {SPV_OPERAND_TYPE_ID, {message.base_type_id()}}};
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypePointer, 0, message.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::TransformationAddTypePointer MakeTransformationAddTypePointer(
+    uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id) {
+  protobufs::TransformationAddTypePointer result;
+  result.set_fresh_id(fresh_id);
+  result.set_storage_class(storage_class);
+  result.set_base_type_id(base_type_id);
+  return result;
+}
+
+}  // namespace transformation
+}  // namespace fuzz
+}  // namespace spvtools

+ 44 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_pointer.h

@@ -0,0 +1,44 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace transformation {
+
+// - |message.fresh_id| must not be used by the module
+// - |message.base_type_id| must be the result id of an OpType[...] instruction
+bool IsApplicable(const protobufs::TransformationAddTypePointer& message,
+                  opt::IRContext* context, const FactManager& fact_manager);
+
+// Adds an OpTypePointer instruction with the given storage class and base type
+// to the module.
+void Apply(const protobufs::TransformationAddTypePointer& message,
+           opt::IRContext* context, FactManager* fact_manager);
+
+// Helper factory to create a transformation message.
+protobufs::TransformationAddTypePointer MakeTransformationAddTypePointer(
+    uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id);
+
+}  // namespace transformation
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_

+ 230 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp

@@ -0,0 +1,230 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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/fuzz/transformation_replace_constant_with_uniform.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace transformation {
+
+namespace {
+
+std::unique_ptr<opt::Instruction> MakeAccessChainInstruction(
+    const protobufs::TransformationReplaceConstantWithUniform& message,
+    spvtools::opt::IRContext* context, uint32_t constant_type_id) {
+  // The input operands for the access chain.
+  opt::Instruction::OperandList operands_for_access_chain;
+
+  opt::Instruction* uniform_variable =
+      FindUniformVariable(message.uniform_descriptor(), context, false);
+
+  // The first input operand is the id of the uniform variable.
+  operands_for_access_chain.push_back(
+      {SPV_OPERAND_TYPE_ID, {uniform_variable->result_id()}});
+
+  // The other input operands are the ids of the constants used to index into
+  // the uniform. The uniform buffer descriptor specifies a series of literals;
+  // for each we find the id of the instruction that defines it, and add these
+  // instruction ids as operands.
+  opt::analysis::Integer int_type(32, true);
+  auto registered_int_type =
+      context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
+  auto int_type_id = context->get_type_mgr()->GetId(&int_type);
+  for (auto index : message.uniform_descriptor().index()) {
+    opt::analysis::IntConstant int_constant(registered_int_type, {index});
+    auto constant_id = context->get_constant_mgr()->FindDeclaredConstant(
+        &int_constant, int_type_id);
+    operands_for_access_chain.push_back({SPV_OPERAND_TYPE_ID, {constant_id}});
+  }
+
+  // The type id for the access chain is a uniform pointer with base type
+  // matching the given constant id type.
+  auto type_and_pointer_type = context->get_type_mgr()->GetTypeAndPointerType(
+      constant_type_id, SpvStorageClassUniform);
+  assert(type_and_pointer_type.first != nullptr);
+  assert(type_and_pointer_type.second != nullptr);
+  auto pointer_to_uniform_constant_type_id =
+      context->get_type_mgr()->GetId(type_and_pointer_type.second.get());
+
+  return MakeUnique<opt::Instruction>(
+      context, SpvOpAccessChain, pointer_to_uniform_constant_type_id,
+      message.fresh_id_for_access_chain(), operands_for_access_chain);
+}
+
+std::unique_ptr<opt::Instruction> MakeLoadInstruction(
+    const protobufs::TransformationReplaceConstantWithUniform& message,
+    spvtools::opt::IRContext* context, uint32_t constant_type_id) {
+  opt::Instruction::OperandList operands_for_load = {
+      {SPV_OPERAND_TYPE_ID, {message.fresh_id_for_access_chain()}}};
+  return MakeUnique<opt::Instruction>(context, SpvOpLoad, constant_type_id,
+                                      message.fresh_id_for_load(),
+                                      operands_for_load);
+}
+
+}  // namespace
+
+bool IsApplicable(
+    const protobufs::TransformationReplaceConstantWithUniform& message,
+    spvtools::opt::IRContext* context,
+    const spvtools::fuzz::FactManager& fact_manager) {
+  // The following is really an invariant of the transformation rather than
+  // merely a requirement of the precondition.  We check it here since we cannot
+  // check it in the message constructor.
+  assert(message.fresh_id_for_access_chain() != message.fresh_id_for_load() &&
+         "Fresh ids for access chain and load result cannot be the same.");
+
+  // The ids for the access chain and load instructions must both be fresh.
+  if (!fuzzerutil::IsFreshId(context, message.fresh_id_for_access_chain())) {
+    return false;
+  }
+  if (!fuzzerutil::IsFreshId(context, message.fresh_id_for_load())) {
+    return false;
+  }
+
+  // The id specified in the id use descriptor must be that of a declared scalar
+  // constant.
+  auto declared_constant = context->get_constant_mgr()->FindDeclaredConstant(
+      message.id_use_descriptor().id_of_interest());
+  if (!declared_constant) {
+    return false;
+  }
+  if (!declared_constant->AsScalarConstant()) {
+    return false;
+  }
+
+  // The fact manager needs to believe that the uniform data element described
+  // by the uniform buffer element descriptor will hold a scalar value.
+  auto constant_id_associated_with_uniform =
+      fact_manager.GetConstantFromUniformDescriptor(
+          context, message.uniform_descriptor());
+  if (!constant_id_associated_with_uniform) {
+    return false;
+  }
+  auto constant_associated_with_uniform =
+      context->get_constant_mgr()->FindDeclaredConstant(
+          constant_id_associated_with_uniform);
+  assert(constant_associated_with_uniform &&
+         "The constant should be present in the module.");
+  if (!constant_associated_with_uniform->AsScalarConstant()) {
+    return false;
+  }
+
+  // The types and values of the scalar value held in the id specified by the id
+  // use descriptor and in the uniform data element specified by the uniform
+  // buffer element descriptor need to match on both type and value.
+  if (!declared_constant->type()->IsSame(
+          constant_associated_with_uniform->type())) {
+    return false;
+  }
+  if (declared_constant->AsScalarConstant()->words() !=
+      constant_associated_with_uniform->AsScalarConstant()->words()) {
+    return false;
+  }
+
+  // The id use descriptor must identify some instruction with respect to the
+  // module.
+  auto instruction_using_constant =
+      transformation::FindInstruction(message.id_use_descriptor(), context);
+  if (!instruction_using_constant) {
+    return false;
+  }
+
+  // The module needs to have a uniform pointer type suitable for indexing into
+  // the uniform variable, i.e. matching the type of the constant we wish to
+  // replace with a uniform.
+  opt::analysis::Pointer pointer_to_type_of_constant(declared_constant->type(),
+                                                     SpvStorageClassUniform);
+  if (!context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) {
+    return false;
+  }
+
+  // In order to index into the uniform, the module has got to contain the int32
+  // type, plus an OpConstant for each of the indices of interest.
+  opt::analysis::Integer int_type(32, true);
+  if (!context->get_type_mgr()->GetId(&int_type)) {
+    return false;
+  }
+  auto registered_int_type =
+      context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
+  auto int_type_id = context->get_type_mgr()->GetId(&int_type);
+  for (auto index : message.uniform_descriptor().index()) {
+    opt::analysis::IntConstant int_constant(registered_int_type, {index});
+    if (!context->get_constant_mgr()->FindDeclaredConstant(&int_constant,
+                                                           int_type_id)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void Apply(const protobufs::TransformationReplaceConstantWithUniform& message,
+           spvtools::opt::IRContext* context,
+           spvtools::fuzz::FactManager* /*unused*/) {
+  // Get the instruction that contains the id use we wish to replace.
+  auto instruction_containing_constant_use =
+      transformation::FindInstruction(message.id_use_descriptor(), context);
+  assert(instruction_containing_constant_use &&
+         "Precondition requires that the id use can be found.");
+  assert(instruction_containing_constant_use->GetSingleWordInOperand(
+             message.id_use_descriptor().in_operand_index()) ==
+             message.id_use_descriptor().id_of_interest() &&
+         "Does not appear to be a usage of the desired id.");
+
+  // The id of the type for the constant whose use we wish to replace.
+  auto constant_type_id =
+      context->get_def_use_mgr()
+          ->GetDef(message.id_use_descriptor().id_of_interest())
+          ->type_id();
+
+  // Add an access chain instruction to target the uniform element.
+  instruction_containing_constant_use->InsertBefore(
+      MakeAccessChainInstruction(message, context, constant_type_id));
+
+  // Add a load from this access chain.
+  instruction_containing_constant_use->InsertBefore(
+      MakeLoadInstruction(message, context, constant_type_id));
+
+  // Adjust the instruction containing the usage of the constant so that this
+  // usage refers instead to the result of the load.
+  instruction_containing_constant_use->SetInOperand(
+      message.id_use_descriptor().in_operand_index(),
+      {message.fresh_id_for_load()});
+
+  // Update the module id bound to reflect the new instructions.
+  fuzzerutil::UpdateModuleIdBound(context, message.fresh_id_for_load());
+  fuzzerutil::UpdateModuleIdBound(context, message.fresh_id_for_access_chain());
+
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::TransformationReplaceConstantWithUniform
+MakeTransformationReplaceConstantWithUniform(
+    protobufs::IdUseDescriptor id_use,
+    protobufs::UniformBufferElementDescriptor uniform_descriptor,
+    uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load) {
+  protobufs::TransformationReplaceConstantWithUniform result;
+  *result.mutable_id_use_descriptor() = std::move(id_use);
+  *result.mutable_uniform_descriptor() = std::move(uniform_descriptor);
+  result.set_fresh_id_for_access_chain(fresh_id_for_access_chain);
+  result.set_fresh_id_for_load(fresh_id_for_load);
+  return result;
+}
+
+}  // namespace transformation
+}  // namespace fuzz
+}  // namespace spvtools

+ 73 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h

@@ -0,0 +1,73 @@
+#include <utility>
+
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_
+#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace transformation {
+
+// - |fresh_id_for_access_chain| and |fresh_id_for_load| must be distinct fresh
+//   ids.
+// - |uniform_descriptor| specifies a result id and a list of integer literal
+//   indices.
+//   As an example, suppose |uniform_descriptor| is (18, [0, 1, 0])
+//   It is required that:
+//     - the result id (18 in our example) is the id of some uniform variable
+//     - the module contains an integer constant instruction corresponding to
+//       each of the literal indices; in our example there must thus be
+//       OpConstant instructions %A and %B say for each of 0 and 1
+//     - it is legitimate to index into the uniform variable using the
+//       sequence of indices; in our example this means indexing into %18 using
+//       the sequence %A %B %A
+//     - the module contains a uniform pointer type corresponding to the type
+//       of the uniform data element obtained by following these indices
+// - |id_use_descriptor| identifies the use of some id %C.  It is required that:
+//     - this use does indeed exist in the module
+//     - %C is an OpConstant
+//     - According to the fact manager, the uniform data element specified by
+//       |uniform_descriptor| holds a value with the same type and value as %C
+bool IsApplicable(
+    const protobufs::TransformationReplaceConstantWithUniform& message,
+    opt::IRContext* context, const FactManager& fact_manager);
+
+// - Introduces two new instructions:
+//   - An access chain targeting the uniform data element specified by
+//     |uniform_descriptor|, with result id |fresh_id_for_access_chain|
+//   - A load from this access chain, with id |fresh_id_for_load|
+// - Replaces the id use specified by |id_use_descriptor| with
+//   |fresh_id_for_load|
+void Apply(const protobufs::TransformationReplaceConstantWithUniform& message,
+           opt::IRContext* context, FactManager* fact_manager);
+
+// Helper factory to create a transformation message.
+protobufs::TransformationReplaceConstantWithUniform
+MakeTransformationReplaceConstantWithUniform(
+    protobufs::IdUseDescriptor id_use,
+    protobufs::UniformBufferElementDescriptor uniform_descriptor,
+    uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load);
+
+}  // namespace transformation
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_

+ 82 - 3
3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp

@@ -14,13 +14,17 @@
 
 
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 
 
+#include <source/opt/instruction.h>
+
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
 
 
 protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
 protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
-    uint32_t uniform_variable_id, std::vector<uint32_t>&& indices) {
+    uint32_t descriptor_set, uint32_t binding,
+    std::vector<uint32_t>&& indices) {
   protobufs::UniformBufferElementDescriptor result;
   protobufs::UniformBufferElementDescriptor result;
-  result.set_uniform_variable_id(uniform_variable_id);
+  result.set_descriptor_set(descriptor_set);
+  result.set_binding(binding);
   for (auto index : indices) {
   for (auto index : indices) {
     result.add_index(index);
     result.add_index(index);
   }
   }
@@ -30,10 +34,85 @@ protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
 bool UniformBufferElementDescriptorEquals::operator()(
 bool UniformBufferElementDescriptorEquals::operator()(
     const protobufs::UniformBufferElementDescriptor* first,
     const protobufs::UniformBufferElementDescriptor* first,
     const protobufs::UniformBufferElementDescriptor* second) const {
     const protobufs::UniformBufferElementDescriptor* second) const {
-  return first->uniform_variable_id() == second->uniform_variable_id() &&
+  return first->descriptor_set() == second->descriptor_set() &&
+         first->binding() == second->binding() &&
+         first->index().size() == second->index().size() &&
          std::equal(first->index().begin(), first->index().end(),
          std::equal(first->index().begin(), first->index().end(),
                     second->index().begin());
                     second->index().begin());
 }
 }
 
 
+opt::Instruction* FindUniformVariable(
+    const protobufs::UniformBufferElementDescriptor&
+        uniform_buffer_element_descriptor,
+    opt::IRContext* context, bool check_unique) {
+  opt::Instruction* result = nullptr;
+
+  for (auto& inst : context->types_values()) {
+    // Consider all global variables with uniform storage class.
+    if (inst.opcode() != SpvOpVariable) {
+      continue;
+    }
+    if (inst.GetSingleWordInOperand(0) != SpvStorageClassUniform) {
+      continue;
+    }
+
+    // Determine whether the variable is decorated with a descriptor set
+    // matching that in |uniform_buffer_element|.
+    bool descriptor_set_matches = false;
+    context->get_decoration_mgr()->ForEachDecoration(
+        inst.result_id(), SpvDecorationDescriptorSet,
+        [&descriptor_set_matches, &uniform_buffer_element_descriptor](
+            const opt::Instruction& decoration_inst) {
+          const uint32_t kDescriptorSetOperandIndex = 2;
+          if (decoration_inst.GetSingleWordInOperand(
+                  kDescriptorSetOperandIndex) ==
+              uniform_buffer_element_descriptor.descriptor_set()) {
+            descriptor_set_matches = true;
+          }
+        });
+    if (!descriptor_set_matches) {
+      // Descriptor set does not match.
+      continue;
+    }
+
+    // Determine whether the variable is decorated with a binding matching that
+    // in |uniform_buffer_element|.
+    bool binding_matches = false;
+    context->get_decoration_mgr()->ForEachDecoration(
+        inst.result_id(), SpvDecorationBinding,
+        [&binding_matches, &uniform_buffer_element_descriptor](
+            const opt::Instruction& decoration_inst) {
+          const uint32_t kBindingOperandIndex = 2;
+          if (decoration_inst.GetSingleWordInOperand(kBindingOperandIndex) ==
+              uniform_buffer_element_descriptor.binding()) {
+            binding_matches = true;
+          }
+        });
+    if (!binding_matches) {
+      // Binding does not match.
+      continue;
+    }
+
+    // This instruction is a uniform variable with the right descriptor set and
+    // binding.
+    if (!check_unique) {
+      // If we aren't checking uniqueness, return it.
+      return &inst;
+    }
+
+    if (result) {
+      // More than one uniform variable is decorated with the given descriptor
+      // set and binding. This means the fact is ambiguous.
+      return nullptr;
+    }
+    result = &inst;
+  }
+
+  // We get here either if no match was found, or if |check_unique| holds and
+  // exactly one match was found.
+  assert(result == nullptr || check_unique);
+  return result;
+}
+
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools

+ 13 - 1
3rdparty/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h

@@ -19,6 +19,8 @@
 #include <vector>
 #include <vector>
 
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/instruction.h"
+#include "source/opt/ir_context.h"
 
 
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
@@ -26,7 +28,7 @@ namespace fuzz {
 // Factory method to create a uniform buffer element descriptor message from an
 // Factory method to create a uniform buffer element descriptor message from an
 // id and list of indices.
 // id and list of indices.
 protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
 protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
-    uint32_t uniform_variable_id, std::vector<uint32_t>&& indices);
+    uint32_t descriptor_set, uint32_t binding, std::vector<uint32_t>&& indices);
 
 
 // Equality function for uniform buffer element descriptors.
 // Equality function for uniform buffer element descriptors.
 struct UniformBufferElementDescriptorEquals {
 struct UniformBufferElementDescriptorEquals {
@@ -35,6 +37,16 @@ struct UniformBufferElementDescriptorEquals {
       const protobufs::UniformBufferElementDescriptor* second) const;
       const protobufs::UniformBufferElementDescriptor* second) const;
 };
 };
 
 
+// Returns a pointer to an OpVariable in |context| that is decorated with the
+// descriptor set and binding associated with |uniform_buffer_element|.  Returns
+// nullptr if no such variable exists.  If multiple such variables exist, a
+// pointer to an arbitrary one of the associated instructions is returned if
+// |check_unique| is false, and nullptr is returned if |check_unique| is true.
+opt::Instruction* FindUniformVariable(
+    const protobufs::UniformBufferElementDescriptor&
+        uniform_buffer_element_descriptor,
+    opt::IRContext* context, bool check_unique);
+
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools
 
 

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

@@ -868,6 +868,7 @@ void AggressiveDCEPass::InitExtensions() {
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_hlsl_functionality1",
       "SPV_GOOGLE_hlsl_functionality1",
+      "SPV_GOOGLE_user_type",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_EXT_descriptor_indexing",
       "SPV_EXT_descriptor_indexing",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_fragment_shader_barycentric",

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

@@ -579,6 +579,7 @@ void CommonUniformElimPass::InitExtensions() {
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_hlsl_functionality1",
       "SPV_GOOGLE_hlsl_functionality1",
+      "SPV_GOOGLE_user_type",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_EXT_descriptor_indexing",
       "SPV_EXT_descriptor_indexing",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_fragment_shader_barycentric",

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

@@ -343,6 +343,7 @@ void LocalAccessChainConvertPass::InitExtensions() {
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_hlsl_functionality1",
       "SPV_GOOGLE_hlsl_functionality1",
+      "SPV_GOOGLE_user_type",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_EXT_descriptor_indexing",
       "SPV_EXT_descriptor_indexing",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_fragment_shader_barycentric",

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

@@ -246,6 +246,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_hlsl_functionality1",
       "SPV_GOOGLE_hlsl_functionality1",
+      "SPV_GOOGLE_user_type",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_EXT_descriptor_indexing",
       "SPV_EXT_descriptor_indexing",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_fragment_shader_barycentric",

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

@@ -95,6 +95,7 @@ void LocalMultiStoreElimPass::InitExtensions() {
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_AMD_gpu_shader_half_float_fetch",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_decorate_string",
       "SPV_GOOGLE_hlsl_functionality1",
       "SPV_GOOGLE_hlsl_functionality1",
+      "SPV_GOOGLE_user_type",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_EXT_descriptor_indexing",
       "SPV_EXT_descriptor_indexing",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_fragment_shader_barycentric",

+ 56 - 0
3rdparty/spirv-tools/source/opt/upgrade_memory_model.cpp

@@ -25,6 +25,12 @@ namespace spvtools {
 namespace opt {
 namespace opt {
 
 
 Pass::Status UpgradeMemoryModel::Process() {
 Pass::Status UpgradeMemoryModel::Process() {
+  // TODO: This pass needs changes to support cooperative matrices.
+  if (context()->get_feature_mgr()->HasCapability(
+          SpvCapabilityCooperativeMatrixNV)) {
+    return Pass::Status::SuccessWithoutChange;
+  }
+
   // Only update Logical GLSL450 to Logical VulkanKHR.
   // Only update Logical GLSL450 to Logical VulkanKHR.
   Instruction* memory_model = get_module()->GetMemoryModel();
   Instruction* memory_model = get_module()->GetMemoryModel();
   if (memory_model->GetSingleWordInOperand(0u) != SpvAddressingModelLogical ||
   if (memory_model->GetSingleWordInOperand(0u) != SpvAddressingModelLogical ||
@@ -110,6 +116,12 @@ void UpgradeMemoryModel::UpgradeInstructions() {
       }
       }
     });
     });
   }
   }
+
+  UpgradeMemoryAndImages();
+  UpgradeAtomics();
+}
+
+void UpgradeMemoryModel::UpgradeMemoryAndImages() {
   for (auto& func : *get_module()) {
   for (auto& func : *get_module()) {
     func.ForEachInst([this](Instruction* inst) {
     func.ForEachInst([this](Instruction* inst) {
       bool is_coherent = false;
       bool is_coherent = false;
@@ -239,6 +251,50 @@ void UpgradeMemoryModel::UpgradeInstructions() {
   }
   }
 }
 }
 
 
+void UpgradeMemoryModel::UpgradeAtomics() {
+  for (auto& func : *get_module()) {
+    func.ForEachInst([this](Instruction* inst) {
+      if (spvOpcodeIsAtomicOp(inst->opcode())) {
+        bool unused_coherent = false;
+        bool is_volatile = false;
+        SpvScope unused_scope = SpvScopeQueueFamilyKHR;
+        std::tie(unused_coherent, is_volatile, unused_scope) =
+            GetInstructionAttributes(inst->GetSingleWordInOperand(0));
+
+        UpgradeSemantics(inst, 2u, is_volatile);
+        if (inst->opcode() == SpvOpAtomicCompareExchange ||
+            inst->opcode() == SpvOpAtomicCompareExchangeWeak) {
+          UpgradeSemantics(inst, 3u, is_volatile);
+        }
+      }
+    });
+  }
+}
+
+void UpgradeMemoryModel::UpgradeSemantics(Instruction* inst,
+                                          uint32_t in_operand,
+                                          bool is_volatile) {
+  if (!is_volatile) return;
+
+  uint32_t semantics_id = inst->GetSingleWordInOperand(in_operand);
+  const analysis::Constant* constant =
+      context()->get_constant_mgr()->FindDeclaredConstant(semantics_id);
+  const analysis::Integer* type = constant->type()->AsInteger();
+  assert(type && type->width() == 32);
+  uint32_t value = 0;
+  if (type->IsSigned()) {
+    value = static_cast<uint32_t>(constant->GetS32());
+  } else {
+    value = constant->GetU32();
+  }
+
+  value |= SpvMemorySemanticsVolatileMask;
+  auto new_constant = context()->get_constant_mgr()->GetConstant(type, {value});
+  auto new_semantics =
+      context()->get_constant_mgr()->GetDefiningInstruction(new_constant);
+  inst->SetInOperand(in_operand, {new_semantics->result_id()});
+}
+
 std::tuple<bool, bool, SpvScope> UpgradeMemoryModel::GetInstructionAttributes(
 std::tuple<bool, bool, SpvScope> UpgradeMemoryModel::GetInstructionAttributes(
     uint32_t id) {
     uint32_t id) {
   // |id| is a pointer used in a memory/image instruction. Need to determine if
   // |id| is a pointer used in a memory/image instruction. Need to determine if

+ 15 - 3
3rdparty/spirv-tools/source/opt/upgrade_memory_model.h

@@ -57,12 +57,19 @@ class UpgradeMemoryModel : public Pass {
   // capability and extension.
   // capability and extension.
   void UpgradeMemoryModelInstruction();
   void UpgradeMemoryModelInstruction();
 
 
-  // Upgrades memory, image and barrier instructions.
+  // Upgrades memory, image and atomic instructions.
   // Memory and image instructions convert coherent and volatile decorations
   // Memory and image instructions convert coherent and volatile decorations
-  // into flags on the instruction. Barriers in tessellation shaders get the
-  // output storage semantic if appropriate.
+  // into flags on the instruction.
+  // Atomic memory semantics convert volatile decoration into flags on the
+  // instruction.
   void UpgradeInstructions();
   void UpgradeInstructions();
 
 
+  // Upgrades memory and image operands for instructions that have them.
+  void UpgradeMemoryAndImages();
+
+  // Adds the volatile memory semantic if necessary.
+  void UpgradeAtomics();
+
   // Returns whether |id| is coherent and/or volatile.
   // Returns whether |id| is coherent and/or volatile.
   std::tuple<bool, bool, SpvScope> GetInstructionAttributes(uint32_t id);
   std::tuple<bool, bool, SpvScope> GetInstructionAttributes(uint32_t id);
 
 
@@ -95,6 +102,11 @@ class UpgradeMemoryModel : public Pass {
                     bool is_volatile, OperationType operation_type,
                     bool is_volatile, OperationType operation_type,
                     InstructionType inst_type);
                     InstructionType inst_type);
 
 
+  // Modifies the semantics at |in_operand| of |inst| to include the volatile
+  // bit if |is_volatile| is true.
+  void UpgradeSemantics(Instruction* inst, uint32_t in_operand,
+                        bool is_volatile);
+
   // Returns the result id for a constant for |scope|.
   // Returns the result id for a constant for |scope|.
   uint32_t GetScopeConstant(SpvScope scope);
   uint32_t GetScopeConstant(SpvScope scope);
 
 

+ 60 - 69
3rdparty/spirv-tools/source/spirv_target_env.cpp

@@ -103,80 +103,45 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
   return SPV_SPIRV_VERSION_WORD(0, 0);
   return SPV_SPIRV_VERSION_WORD(0, 0);
 }
 }
 
 
+static const std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] = {
+    {"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4},
+    {"vulkan1.0", SPV_ENV_VULKAN_1_0},
+    {"vulkan1.1", SPV_ENV_VULKAN_1_1},
+    {"spv1.0", SPV_ENV_UNIVERSAL_1_0},
+    {"spv1.1", SPV_ENV_UNIVERSAL_1_1},
+    {"spv1.2", SPV_ENV_UNIVERSAL_1_2},
+    {"spv1.3", SPV_ENV_UNIVERSAL_1_3},
+    {"spv1.4", SPV_ENV_UNIVERSAL_1_4},
+    {"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2},
+    {"opencl1.2", SPV_ENV_OPENCL_1_2},
+    {"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0},
+    {"opencl2.0", SPV_ENV_OPENCL_2_0},
+    {"opencl2.1embedded", SPV_ENV_OPENCL_EMBEDDED_2_1},
+    {"opencl2.1", SPV_ENV_OPENCL_2_1},
+    {"opencl2.2embedded", SPV_ENV_OPENCL_EMBEDDED_2_2},
+    {"opencl2.2", SPV_ENV_OPENCL_2_2},
+    {"opengl4.0", SPV_ENV_OPENGL_4_0},
+    {"opengl4.1", SPV_ENV_OPENGL_4_1},
+    {"opengl4.2", SPV_ENV_OPENGL_4_2},
+    {"opengl4.3", SPV_ENV_OPENGL_4_3},
+    {"opengl4.5", SPV_ENV_OPENGL_4_5},
+    {"webgpu0", SPV_ENV_WEBGPU_0},
+};
+
 bool spvParseTargetEnv(const char* s, spv_target_env* env) {
 bool spvParseTargetEnv(const char* s, spv_target_env* env) {
   auto match = [s](const char* b) {
   auto match = [s](const char* b) {
     return s && (0 == strncmp(s, b, strlen(b)));
     return s && (0 == strncmp(s, b, strlen(b)));
   };
   };
-  if (match("vulkan1.1spv1.4")) {
-    if (env) *env = SPV_ENV_VULKAN_1_1_SPIRV_1_4;
-    return true;
-  } else if (match("vulkan1.0")) {
-    if (env) *env = SPV_ENV_VULKAN_1_0;
-    return true;
-  } else if (match("vulkan1.1")) {
-    if (env) *env = SPV_ENV_VULKAN_1_1;
-    return true;
-  } else if (match("spv1.0")) {
-    if (env) *env = SPV_ENV_UNIVERSAL_1_0;
-    return true;
-  } else if (match("spv1.1")) {
-    if (env) *env = SPV_ENV_UNIVERSAL_1_1;
-    return true;
-  } else if (match("spv1.2")) {
-    if (env) *env = SPV_ENV_UNIVERSAL_1_2;
-    return true;
-  } else if (match("spv1.3")) {
-    if (env) *env = SPV_ENV_UNIVERSAL_1_3;
-    return true;
-  } else if (match("spv1.4")) {
-    if (env) *env = SPV_ENV_UNIVERSAL_1_4;
-    return true;
-  } else if (match("opencl1.2embedded")) {
-    if (env) *env = SPV_ENV_OPENCL_EMBEDDED_1_2;
-    return true;
-  } else if (match("opencl1.2")) {
-    if (env) *env = SPV_ENV_OPENCL_1_2;
-    return true;
-  } else if (match("opencl2.0embedded")) {
-    if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_0;
-    return true;
-  } else if (match("opencl2.0")) {
-    if (env) *env = SPV_ENV_OPENCL_2_0;
-    return true;
-  } else if (match("opencl2.1embedded")) {
-    if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_1;
-    return true;
-  } else if (match("opencl2.1")) {
-    if (env) *env = SPV_ENV_OPENCL_2_1;
-    return true;
-  } else if (match("opencl2.2embedded")) {
-    if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_2;
-    return true;
-  } else if (match("opencl2.2")) {
-    if (env) *env = SPV_ENV_OPENCL_2_2;
-    return true;
-  } else if (match("opengl4.0")) {
-    if (env) *env = SPV_ENV_OPENGL_4_0;
-    return true;
-  } else if (match("opengl4.1")) {
-    if (env) *env = SPV_ENV_OPENGL_4_1;
-    return true;
-  } else if (match("opengl4.2")) {
-    if (env) *env = SPV_ENV_OPENGL_4_2;
-    return true;
-  } else if (match("opengl4.3")) {
-    if (env) *env = SPV_ENV_OPENGL_4_3;
-    return true;
-  } else if (match("opengl4.5")) {
-    if (env) *env = SPV_ENV_OPENGL_4_5;
-    return true;
-  } else if (match("webgpu0")) {
-    if (env) *env = SPV_ENV_WEBGPU_0;
-    return true;
-  } else {
-    if (env) *env = SPV_ENV_UNIVERSAL_1_0;
-    return false;
+  for (auto& name_env : spvTargetEnvNameMap) {
+    if (match(name_env.first)) {
+      if (env) {
+        *env = name_env.second;
+      }
+      return true;
+    }
   }
   }
+  if (env) *env = SPV_ENV_UNIVERSAL_1_0;
+  return false;
 }
 }
 
 
 bool spvIsVulkanEnv(spv_target_env env) {
 bool spvIsVulkanEnv(spv_target_env env) {
@@ -310,3 +275,29 @@ std::string spvLogStringForEnv(spv_target_env env) {
   }
   }
   return "Unknown";
   return "Unknown";
 }
 }
+
+std::string spvTargetEnvList(const int pad, const int wrap) {
+  std::string ret;
+  size_t max_line_len = wrap - pad;  // The first line isn't padded
+  std::string line;
+  std::string sep = "";
+
+  for (auto& name_env : spvTargetEnvNameMap) {
+    std::string word = sep + name_env.first;
+    if (line.length() + word.length() > max_line_len) {
+      // Adding one word wouldn't fit, commit the line in progress and
+      // start a new one.
+      ret += line + "\n";
+      line.assign(pad, ' ');
+      // The first line is done. The max length now comprises the
+      // padding.
+      max_line_len = wrap;
+    }
+    line += word;
+    sep = "|";
+  }
+
+  ret += line;
+
+  return ret;
+}

+ 8 - 0
3rdparty/spirv-tools/source/spirv_target_env.h

@@ -38,4 +38,12 @@ uint32_t spvVersionForTargetEnv(spv_target_env env);
 // environment, i.e. "Vulkan", "WebGPU", "OpenCL", etc.
 // environment, i.e. "Vulkan", "WebGPU", "OpenCL", etc.
 std::string spvLogStringForEnv(spv_target_env env);
 std::string spvLogStringForEnv(spv_target_env env);
 
 
+// Returns a formatted list of all SPIR-V target environment names that
+// can be parsed by spvParseTargetEnv.
+// |pad| is the number of space characters that the begining of each line
+//       except the first one will be padded with.
+// |wrap| is the max length of lines the user desires. Word-wrapping will
+//        occur to satisfy this limit.
+std::string spvTargetEnvList(const int pad, const int wrap);
+
 #endif  // SOURCE_SPIRV_TARGET_ENV_H_
 #endif  // SOURCE_SPIRV_TARGET_ENV_H_

+ 26 - 2
3rdparty/spirv-tools/source/val/validate_atomics.cpp

@@ -175,13 +175,37 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
         return error;
         return error;
       }
       }
 
 
-      if (auto error = ValidateMemorySemantics(_, inst, operand_index++))
+      const auto equal_semantics_index = operand_index++;
+      if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index))
         return error;
         return error;
 
 
       if (opcode == SpvOpAtomicCompareExchange ||
       if (opcode == SpvOpAtomicCompareExchange ||
           opcode == SpvOpAtomicCompareExchangeWeak) {
           opcode == SpvOpAtomicCompareExchangeWeak) {
-        if (auto error = ValidateMemorySemantics(_, inst, operand_index++))
+        const auto unequal_semantics_index = operand_index++;
+        if (auto error =
+                ValidateMemorySemantics(_, inst, unequal_semantics_index))
           return error;
           return error;
+
+        // Volatile bits must match for equal and unequal semantics. Previous
+        // checks guarantee they are 32-bit constants, but we need to recheck
+        // whether they are evaluatable constants.
+        bool is_int32 = false;
+        bool is_equal_const = false;
+        bool is_unequal_const = false;
+        uint32_t equal_value = 0;
+        uint32_t unequal_value = 0;
+        std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst(
+            inst->GetOperandAs<uint32_t>(equal_semantics_index));
+        std::tie(is_int32, is_unequal_const, unequal_value) =
+            _.EvalInt32IfConst(
+                inst->GetOperandAs<uint32_t>(unequal_semantics_index));
+        if (is_equal_const && is_unequal_const &&
+            ((equal_value & SpvMemorySemanticsVolatileMask) ^
+             (unequal_value & SpvMemorySemanticsVolatileMask))) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "Volatile mask setting must match for Equal and Unequal "
+                    "memory semantics";
+        }
       }
       }
 
 
       if (opcode == SpvOpAtomicStore) {
       if (opcode == SpvOpAtomicStore) {

+ 78 - 0
3rdparty/spirv-tools/source/val/validate_decorations.cpp

@@ -1393,6 +1393,81 @@ spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate,
          << spvOpcodeString(inst.opcode());
          << spvOpcodeString(inst.opcode());
 }
 }
 
 
+// Returns SPV_SUCCESS if validation rules are satisfied for the Component
+// decoration.  Otherwise emits a diagnostic and returns something other than
+// SPV_SUCCESS.
+spv_result_t CheckComponentDecoration(ValidationState_t& vstate,
+                                      const Instruction& inst,
+                                      const Decoration& decoration) {
+  assert(inst.id() && "Parser ensures the target of the decoration has an ID");
+
+  uint32_t type_id;
+  if (decoration.struct_member_index() == Decoration::kInvalidMember) {
+    // The target must be a memory object declaration.
+    const auto opcode = inst.opcode();
+    if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+             << "Target of Component decoration must be a memory object "
+                "declaration (a variable or a function parameter)";
+    }
+
+    // Only valid for the Input and Output Storage Classes.
+    const auto storage_class = opcode == SpvOpVariable
+                                   ? inst.GetOperandAs<SpvStorageClass>(2)
+                                   : SpvStorageClassMax;
+    if (storage_class != SpvStorageClassInput &&
+        storage_class != SpvStorageClassOutput &&
+        storage_class != SpvStorageClassMax) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+             << "Target of Component decoration is invalid: must point to a "
+                "Storage Class of Input(1) or Output(3). Found Storage "
+                "Class "
+             << storage_class;
+    }
+
+    type_id = inst.type_id();
+    if (vstate.IsPointerType(type_id)) {
+      const auto pointer = vstate.FindDef(type_id);
+      type_id = pointer->GetOperandAs<uint32_t>(2);
+    }
+  } else {
+    if (inst.opcode() != SpvOpTypeStruct) {
+      return vstate.diag(SPV_ERROR_INVALID_DATA, &inst)
+             << "Attempted to get underlying data type via member index for "
+                "non-struct type.";
+    }
+    type_id = inst.word(decoration.struct_member_index() + 2);
+  }
+
+  if (spvIsVulkanEnv(vstate.context()->target_env)) {
+    if (!vstate.IsIntScalarOrVectorType(type_id) &&
+        !vstate.IsFloatScalarOrVectorType(type_id)) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+             << "Component decoration specified for type "
+             << vstate.getIdName(type_id) << " that is not a scalar or vector";
+    }
+
+    // For 16-, and 32-bit types, it is invalid if this sequence of components
+    // gets larger than 3.
+    const auto bit_width = vstate.GetBitWidth(type_id);
+    if (bit_width == 16 || bit_width == 32) {
+      assert(decoration.params().size() == 1 &&
+             "Grammar ensures Component has one parameter");
+
+      const auto component = decoration.params()[0];
+      const auto last_component = component + vstate.GetDimension(type_id) - 1;
+      if (last_component > 3) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+               << "Sequence of components starting with " << component
+               << " and ending with " << last_component
+               << " gets larger than 3";
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
   {                                             \
   {                                             \
     spv_result_t e##LINE = (X);                 \
     spv_result_t e##LINE = (X);                 \
@@ -1421,6 +1496,9 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
 
 
     for (const auto& decoration : decorations) {
     for (const auto& decoration : decorations) {
       switch (decoration.dec_type()) {
       switch (decoration.dec_type()) {
+        case SpvDecorationComponent:
+          PASS_OR_BAIL(CheckComponentDecoration(vstate, *inst, decoration));
+          break;
         case SpvDecorationFPRoundingMode:
         case SpvDecorationFPRoundingMode:
           if (is_shader)
           if (is_shader)
             PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst));
             PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst));

+ 20 - 2
3rdparty/spirv-tools/source/val/validate_memory.cpp

@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
-#include "source/val/validate.h"
-
 #include <algorithm>
 #include <algorithm>
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
@@ -21,6 +19,7 @@
 #include "source/opcode.h"
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validate_scopes.h"
 #include "source/val/validate_scopes.h"
 #include "source/val/validation_state.h"
 #include "source/val/validation_state.h"
 
 
@@ -809,6 +808,25 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
              << "OpStore Pointer <id> '" << _.getIdName(pointer_id)
              << "OpStore Pointer <id> '" << _.getIdName(pointer_id)
              << "' storage class is read-only";
              << "' storage class is read-only";
     }
     }
+
+    if (spvIsVulkanEnv(_.context()->target_env) &&
+        storage_class == SpvStorageClassUniform) {
+      auto base_ptr = _.TracePointer(pointer);
+      if (base_ptr->opcode() == SpvOpVariable) {
+        // If it's not a variable a different check should catch the problem.
+        auto base_type = _.FindDef(base_ptr->GetOperandAs<uint32_t>(0));
+        // Get the pointed-to type.
+        base_type = _.FindDef(base_type->GetOperandAs<uint32_t>(2u));
+        if (base_type->opcode() == SpvOpTypeArray ||
+            base_type->opcode() == SpvOpTypeRuntimeArray) {
+          base_type = _.FindDef(base_type->GetOperandAs<uint32_t>(1u));
+        }
+        if (_.HasDecoration(base_type->id(), SpvDecorationBlock)) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In the Vulkan environment, cannot store to Uniform Blocks";
+        }
+      }
+    }
   }
   }
 
 
   const auto object_index = 1;
   const auto object_index = 1;

+ 25 - 1
3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp

@@ -39,11 +39,20 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
   }
   }
 
 
   if (!is_const_int32) {
   if (!is_const_int32) {
-    if (_.HasCapability(SpvCapabilityShader)) {
+    if (_.HasCapability(SpvCapabilityShader) &&
+        !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Memory Semantics ids must be OpConstant when Shader "
              << "Memory Semantics ids must be OpConstant when Shader "
                 "capability is present";
                 "capability is present";
     }
     }
+
+    if (_.HasCapability(SpvCapabilityShader) &&
+        _.HasCapability(SpvCapabilityCooperativeMatrixNV) &&
+        !spvOpcodeIsConstant(_.GetIdOpcode(id))) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Memory Semantics must be a constant instruction when "
+                "CooperativeMatrixNV capability is present";
+    }
     return SPV_SUCCESS;
     return SPV_SUCCESS;
   }
   }
 
 
@@ -127,6 +136,21 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
            << "VulkanMemoryModelKHR";
            << "VulkanMemoryModelKHR";
   }
   }
 
 
+  if (value & SpvMemorySemanticsVolatileMask) {
+    if (!_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << spvOpcodeString(opcode)
+             << ": Memory Semantics Volatile requires capability "
+                "VulkanMemoryModelKHR";
+    }
+
+    if (!spvOpcodeIsAtomicOp(inst->opcode())) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Memory Semantics Volatile can only be used with atomic "
+                "instructions";
+    }
+  }
+
   if (value & SpvMemorySemanticsUniformMemoryMask &&
   if (value & SpvMemorySemanticsUniformMemoryMask &&
       !_.HasCapability(SpvCapabilityShader)) {
       !_.HasCapability(SpvCapabilityShader)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
     return _.diag(SPV_ERROR_INVALID_DATA, inst)

+ 13 - 0
3rdparty/spirv-tools/source/val/validation_state.cpp

@@ -1213,5 +1213,18 @@ bool ValidationState_t::LogicallyMatch(const Instruction* lhs,
   return false;
   return false;
 }
 }
 
 
+const Instruction* ValidationState_t::TracePointer(
+    const Instruction* inst) const {
+  auto base_ptr = inst;
+  while (base_ptr->opcode() == SpvOpAccessChain ||
+         base_ptr->opcode() == SpvOpInBoundsAccessChain ||
+         base_ptr->opcode() == SpvOpPtrAccessChain ||
+         base_ptr->opcode() == SpvOpInBoundsPtrAccessChain ||
+         base_ptr->opcode() == SpvOpCopyObject) {
+    base_ptr = FindDef(base_ptr->GetOperandAs<uint32_t>(2u));
+  }
+  return base_ptr;
+}
+
 }  // namespace val
 }  // namespace val
 }  // namespace spvtools
 }  // namespace spvtools

+ 9 - 0
3rdparty/spirv-tools/source/val/validation_state.h

@@ -680,6 +680,15 @@ class ValidationState_t {
   bool LogicallyMatch(const Instruction* lhs, const Instruction* rhs,
   bool LogicallyMatch(const Instruction* lhs, const Instruction* rhs,
                       bool check_decorations);
                       bool check_decorations);
 
 
+  // Traces |inst| to find a single base pointer. Returns the base pointer.
+  // Will trace through the following instructions:
+  // * OpAccessChain
+  // * OpInBoundsAccessChain
+  // * OpPtrAccessChain
+  // * OpInBoundsPtrAccessChain
+  // * OpCopyObject
+  const Instruction* TracePointer(const Instruction* inst) const;
+
  private:
  private:
   ValidationState_t(const ValidationState_t&);
   ValidationState_t(const ValidationState_t&);
 
 

+ 5 - 1
3rdparty/spirv-tools/test/fuzz/CMakeLists.txt

@@ -20,15 +20,19 @@ if (${SPIRV_BUILD_FUZZER})
           fuzzer_replayer_test.cpp
           fuzzer_replayer_test.cpp
           fact_manager_test.cpp
           fact_manager_test.cpp
           fuzz_test_util.cpp
           fuzz_test_util.cpp
+          fuzzer_pass_add_useful_constructs_test.cpp
           transformation_add_constant_boolean_test.cpp
           transformation_add_constant_boolean_test.cpp
           transformation_add_constant_scalar_test.cpp
           transformation_add_constant_scalar_test.cpp
           transformation_add_dead_break_test.cpp
           transformation_add_dead_break_test.cpp
           transformation_add_type_boolean_test.cpp
           transformation_add_type_boolean_test.cpp
           transformation_add_type_float_test.cpp
           transformation_add_type_float_test.cpp
           transformation_add_type_int_test.cpp
           transformation_add_type_int_test.cpp
+          transformation_add_type_pointer_test.cpp
           transformation_move_block_down_test.cpp
           transformation_move_block_down_test.cpp
           transformation_replace_boolean_constant_with_constant_binary_test.cpp
           transformation_replace_boolean_constant_with_constant_binary_test.cpp
-          transformation_split_block_test.cpp)
+          transformation_replace_constant_with_uniform_test.cpp
+          transformation_split_block_test.cpp
+          uniform_buffer_element_descriptor_test.cpp)
 
 
   add_spvtools_unittest(TARGET fuzz
   add_spvtools_unittest(TARGET fuzz
         SRCS ${SOURCES}
         SRCS ${SOURCES}

+ 235 - 26
3rdparty/spirv-tools/test/fuzz/fact_manager_test.cpp

@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
+#include <limits>
+
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 #include "test/fuzz/fuzz_test_util.h"
@@ -56,6 +58,44 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
                OpExecutionMode %4 OriginUpperLeft
                OpExecutionMode %4 OriginUpperLeft
                OpSource GLSL 450
                OpSource GLSL 450
                OpName %4 "main"
                OpName %4 "main"
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %200 DescriptorSet 0
+               OpDecorate %200 Binding 1
+               OpDecorate %300 DescriptorSet 0
+               OpDecorate %300 Binding 2
+               OpDecorate %400 DescriptorSet 0
+               OpDecorate %400 Binding 3
+               OpDecorate %500 DescriptorSet 0
+               OpDecorate %500 Binding 4
+               OpDecorate %600 DescriptorSet 0
+               OpDecorate %600 Binding 5
+               OpDecorate %700 DescriptorSet 0
+               OpDecorate %700 Binding 6
+               OpDecorate %800 DescriptorSet 1
+               OpDecorate %800 Binding 0
+               OpDecorate %900 DescriptorSet 1
+               OpDecorate %900 Binding 1
+               OpDecorate %1000 DescriptorSet 1
+               OpDecorate %1000 Binding 2
+               OpDecorate %1100 DescriptorSet 1
+               OpDecorate %1100 Binding 3
+               OpDecorate %1200 DescriptorSet 1
+               OpDecorate %1200 Binding 4
+               OpDecorate %1300 DescriptorSet 1
+               OpDecorate %1300 Binding 5
+               OpDecorate %1400 DescriptorSet 1
+               OpDecorate %1400 Binding 6
+               OpDecorate %1500 DescriptorSet 2
+               OpDecorate %1500 Binding 0
+               OpDecorate %1600 DescriptorSet 2
+               OpDecorate %1600 Binding 1
+               OpDecorate %1700 DescriptorSet 2
+               OpDecorate %1700 Binding 2
+               OpDecorate %1800 DescriptorSet 2
+               OpDecorate %1800 Binding 3
+               OpDecorate %1900 DescriptorSet 2
+               OpDecorate %1900 Binding 4
           %2 = OpTypeVoid
           %2 = OpTypeVoid
           %3 = OpTypeFunction %2
           %3 = OpTypeFunction %2
          %10 = OpTypeInt 32 0
          %10 = OpTypeInt 32 0
@@ -229,94 +269,98 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
                                                             type_uint32_id)
                                                             type_uint32_id)
                   .empty());
                   .empty());
 
 
+  // In the comments that follow we write v[...][...] to refer to uniform
+  // variable v indexed with some given indices, when in practice v is
+  // identified via a (descriptor set, binding) pair.
+
   // 100[2][3] == int(1)
   // 100[2][3] == int(1)
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
-                            MakeUniformBufferElementDescriptor(100, {2, 3})));
+                            MakeUniformBufferElementDescriptor(0, 0, {2, 3})));
 
 
   // 200[1][2][3] == int(1)
   // 200[1][2][3] == int(1)
   ASSERT_TRUE(
   ASSERT_TRUE(
       AddFactHelper(&fact_manager, context.get(), {1},
       AddFactHelper(&fact_manager, context.get(), {1},
-                    MakeUniformBufferElementDescriptor(200, {1, 2, 3})));
+                    MakeUniformBufferElementDescriptor(0, 1, {1, 2, 3})));
 
 
   // 300[1][0][2][3] == int(1)
   // 300[1][0][2][3] == int(1)
   ASSERT_TRUE(
   ASSERT_TRUE(
       AddFactHelper(&fact_manager, context.get(), {1},
       AddFactHelper(&fact_manager, context.get(), {1},
-                    MakeUniformBufferElementDescriptor(300, {1, 0, 2, 3})));
+                    MakeUniformBufferElementDescriptor(0, 2, {1, 0, 2, 3})));
 
 
   // 400[2][3] = int32_min
   // 400[2][3] = int32_min
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]},
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]},
-                            MakeUniformBufferElementDescriptor(400, {2, 3})));
+                            MakeUniformBufferElementDescriptor(0, 3, {2, 3})));
 
 
   // 500[1][2][3] = int32_min
   // 500[1][2][3] = int32_min
   ASSERT_TRUE(
   ASSERT_TRUE(
       AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]},
       AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]},
-                    MakeUniformBufferElementDescriptor(500, {1, 2, 3})));
+                    MakeUniformBufferElementDescriptor(0, 4, {1, 2, 3})));
 
 
   // 600[1][2][3] = int64_max
   // 600[1][2][3] = int64_max
   ASSERT_TRUE(AddFactHelper(
   ASSERT_TRUE(AddFactHelper(
       &fact_manager, context.get(), {buffer_int64_max[0], buffer_int64_max[1]},
       &fact_manager, context.get(), {buffer_int64_max[0], buffer_int64_max[1]},
-      MakeUniformBufferElementDescriptor(600, {1, 2, 3})));
+      MakeUniformBufferElementDescriptor(0, 5, {1, 2, 3})));
 
 
   // 700[1][1] = int64_max
   // 700[1][1] = int64_max
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
                             {buffer_int64_max[0], buffer_int64_max[1]},
                             {buffer_int64_max[0], buffer_int64_max[1]},
-                            MakeUniformBufferElementDescriptor(700, {1, 1})));
+                            MakeUniformBufferElementDescriptor(0, 6, {1, 1})));
 
 
   // 800[2][3] = uint(1)
   // 800[2][3] = uint(1)
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
-                            MakeUniformBufferElementDescriptor(800, {2, 3})));
+                            MakeUniformBufferElementDescriptor(1, 0, {2, 3})));
 
 
   // 900[1][2][3] = uint(1)
   // 900[1][2][3] = uint(1)
   ASSERT_TRUE(
   ASSERT_TRUE(
       AddFactHelper(&fact_manager, context.get(), {1},
       AddFactHelper(&fact_manager, context.get(), {1},
-                    MakeUniformBufferElementDescriptor(900, {1, 2, 3})));
+                    MakeUniformBufferElementDescriptor(1, 1, {1, 2, 3})));
 
 
   // 1000[1][0][2][3] = uint(1)
   // 1000[1][0][2][3] = uint(1)
   ASSERT_TRUE(
   ASSERT_TRUE(
       AddFactHelper(&fact_manager, context.get(), {1},
       AddFactHelper(&fact_manager, context.get(), {1},
-                    MakeUniformBufferElementDescriptor(1000, {1, 0, 2, 3})));
+                    MakeUniformBufferElementDescriptor(1, 2, {1, 0, 2, 3})));
 
 
   // 1100[0] = uint64(1)
   // 1100[0] = uint64(1)
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
                             {buffer_uint64_1[0], buffer_uint64_1[1]},
                             {buffer_uint64_1[0], buffer_uint64_1[1]},
-                            MakeUniformBufferElementDescriptor(1100, {0})));
+                            MakeUniformBufferElementDescriptor(1, 3, {0})));
 
 
   // 1200[0][0] = uint64_max
   // 1200[0][0] = uint64_max
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
                             {buffer_uint64_max[0], buffer_uint64_max[1]},
                             {buffer_uint64_max[0], buffer_uint64_max[1]},
-                            MakeUniformBufferElementDescriptor(1200, {0, 0})));
+                            MakeUniformBufferElementDescriptor(1, 4, {0, 0})));
 
 
   // 1300[1][0] = uint64_max
   // 1300[1][0] = uint64_max
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
                             {buffer_uint64_max[0], buffer_uint64_max[1]},
                             {buffer_uint64_max[0], buffer_uint64_max[1]},
-                            MakeUniformBufferElementDescriptor(1300, {1, 0})));
+                            MakeUniformBufferElementDescriptor(1, 5, {1, 0})));
 
 
   // 1400[6] = float(10.0)
   // 1400[6] = float(10.0)
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
-                            MakeUniformBufferElementDescriptor(1400, {6})));
+                            MakeUniformBufferElementDescriptor(1, 6, {6})));
 
 
   // 1500[7] = float(10.0)
   // 1500[7] = float(10.0)
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
-                            MakeUniformBufferElementDescriptor(1500, {7})));
+                            MakeUniformBufferElementDescriptor(2, 0, {7})));
 
 
   // 1600[9][9] = float(10.0)
   // 1600[9][9] = float(10.0)
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
-                            MakeUniformBufferElementDescriptor(1600, {9, 9})));
+                            MakeUniformBufferElementDescriptor(2, 1, {9, 9})));
 
 
   // 1700[9][9][1] = double(10.0)
   // 1700[9][9][1] = double(10.0)
   ASSERT_TRUE(AddFactHelper(
   ASSERT_TRUE(AddFactHelper(
       &fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]},
       &fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]},
-      MakeUniformBufferElementDescriptor(1700, {9, 9, 1})));
+      MakeUniformBufferElementDescriptor(2, 2, {9, 9, 1})));
 
 
   // 1800[9][9][2] = double(10.0)
   // 1800[9][9][2] = double(10.0)
   ASSERT_TRUE(AddFactHelper(
   ASSERT_TRUE(AddFactHelper(
       &fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]},
       &fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]},
-      MakeUniformBufferElementDescriptor(1800, {9, 9, 2})));
+      MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2})));
 
 
   // 1900[0][0][0][0][0] = double(20.0)
   // 1900[0][0][0][0][0] = double(20.0)
   ASSERT_TRUE(AddFactHelper(
   ASSERT_TRUE(AddFactHelper(
       &fact_manager, context.get(), {buffer_double_20[0], buffer_double_20[1]},
       &fact_manager, context.get(), {buffer_double_20[0], buffer_double_20[1]},
-      MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0})));
+      MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0})));
 
 
   opt::Instruction::OperandList operands = {
   opt::Instruction::OperandList operands = {
       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}};
       {SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}};
@@ -427,12 +471,12 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
           context.get(), double_constant_ids[0]);
           context.get(), double_constant_ids[0]);
   ASSERT_EQ(2, descriptors_for_double_10.size());
   ASSERT_EQ(2, descriptors_for_double_10.size());
   {
   {
-    auto temp = MakeUniformBufferElementDescriptor(1700, {9, 9, 1});
+    auto temp = MakeUniformBufferElementDescriptor(2, 2, {9, 9, 1});
     ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
     ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
         &temp, &descriptors_for_double_10[0]));
         &temp, &descriptors_for_double_10[0]));
   }
   }
   {
   {
-    auto temp = MakeUniformBufferElementDescriptor(1800, {9, 9, 2});
+    auto temp = MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2});
     ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
     ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
         &temp, &descriptors_for_double_10[1]));
         &temp, &descriptors_for_double_10[1]));
   }
   }
@@ -441,17 +485,17 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
           context.get(), double_constant_ids[1]);
           context.get(), double_constant_ids[1]);
   ASSERT_EQ(1, descriptors_for_double_20.size());
   ASSERT_EQ(1, descriptors_for_double_20.size());
   {
   {
-    auto temp = MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0});
+    auto temp = MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0});
     ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
     ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
         &temp, &descriptors_for_double_20[0]));
         &temp, &descriptors_for_double_20[0]));
   }
   }
 
 
   auto constant_1_id = fact_manager.GetConstantFromUniformDescriptor(
   auto constant_1_id = fact_manager.GetConstantFromUniformDescriptor(
-      context.get(), MakeUniformBufferElementDescriptor(1800, {9, 9, 2}));
+      context.get(), MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2}));
   ASSERT_TRUE(constant_1_id);
   ASSERT_TRUE(constant_1_id);
 
 
   auto constant_2_id = fact_manager.GetConstantFromUniformDescriptor(
   auto constant_2_id = fact_manager.GetConstantFromUniformDescriptor(
-      context.get(), MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0}));
+      context.get(), MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0}));
   ASSERT_TRUE(constant_2_id);
   ASSERT_TRUE(constant_2_id);
 
 
   ASSERT_EQ(double_constant_ids[0], constant_1_id);
   ASSERT_EQ(double_constant_ids[0], constant_1_id);
@@ -503,9 +547,9 @@ TEST(FactManagerTest, TwoConstantsWithSameValue) {
   FactManager fact_manager;
   FactManager fact_manager;
 
 
   auto uniform_buffer_element_descriptor =
   auto uniform_buffer_element_descriptor =
-      MakeUniformBufferElementDescriptor(12, {0});
+      MakeUniformBufferElementDescriptor(0, 0, {0});
 
 
-  // 12[0] = int(1)
+  // (0, 0, [0]) = int(1)
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
   ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
                             uniform_buffer_element_descriptor));
                             uniform_buffer_element_descriptor));
   auto constants =
   auto constants =
@@ -529,6 +573,171 @@ TEST(FactManagerTest, TwoConstantsWithSameValue) {
   }
   }
 }
 }
 
 
+TEST(FactManagerTest, NonFiniteFactsAreNotValid) {
+  std::string shader = R"(
+               OpCapability Shader
+               OpCapability Float64
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %7 "buf"
+               OpMemberName %7 0 "f"
+               OpMemberName %7 1 "d"
+               OpName %9 ""
+               OpMemberDecorate %7 0 Offset 0
+               OpMemberDecorate %7 1 Offset 8
+               OpDecorate %7 Block
+               OpDecorate %9 DescriptorSet 0
+               OpDecorate %9 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+         %10 = OpTypeFloat 64
+          %7 = OpTypeStruct %6 %10
+          %8 = OpTypePointer Uniform %7
+          %9 = OpVariable %8 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  auto uniform_buffer_element_descriptor_f =
+      MakeUniformBufferElementDescriptor(0, 0, {0});
+
+  auto uniform_buffer_element_descriptor_d =
+      MakeUniformBufferElementDescriptor(0, 0, {1});
+
+  if (std::numeric_limits<float>::has_infinity) {
+    // f == +inf
+    float positive_infinity_float = std::numeric_limits<float>::infinity();
+    uint32_t words[1];
+    memcpy(words, &positive_infinity_float, sizeof(float));
+    ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {words[0]},
+                               uniform_buffer_element_descriptor_f));
+    // f == -inf
+    float negative_infinity_float = std::numeric_limits<float>::infinity();
+    memcpy(words, &negative_infinity_float, sizeof(float));
+    ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {words[0]},
+                               uniform_buffer_element_descriptor_f));
+  }
+
+  if (std::numeric_limits<float>::has_quiet_NaN) {
+    // f == NaN
+    float quiet_nan_float = std::numeric_limits<float>::quiet_NaN();
+    uint32_t words[1];
+    memcpy(words, &quiet_nan_float, sizeof(float));
+    ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {words[0]},
+                               uniform_buffer_element_descriptor_f));
+  }
+
+  if (std::numeric_limits<double>::has_infinity) {
+    // d == +inf
+    double positive_infinity_double = std::numeric_limits<double>::infinity();
+    uint32_t words[2];
+    memcpy(words, &positive_infinity_double, sizeof(double));
+    ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(),
+                               {words[0], words[1]},
+                               uniform_buffer_element_descriptor_d));
+    // d == -inf
+    double negative_infinity_double = -std::numeric_limits<double>::infinity();
+    memcpy(words, &negative_infinity_double, sizeof(double));
+    ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(),
+                               {words[0], words[1]},
+                               uniform_buffer_element_descriptor_d));
+  }
+
+  if (std::numeric_limits<double>::has_quiet_NaN) {
+    // d == NaN
+    double quiet_nan_double = std::numeric_limits<double>::quiet_NaN();
+    uint32_t words[2];
+    memcpy(words, &quiet_nan_double, sizeof(double));
+    ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(),
+                               {words[0], words[1]},
+                               uniform_buffer_element_descriptor_d));
+  }
+}
+
+TEST(FactManagerTest, AmbiguousFact) {
+  //  This test came from the following GLSL:
+  //
+  // #version 310 es
+  //
+  // precision highp float;
+  //
+  // layout(set = 0, binding = 0) uniform buf {
+  //   float f;
+  // };
+  //
+  // layout(set = 0, binding = 0) uniform buf2 {
+  //   float g;
+  // };
+  //
+  // void main() {
+  //
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %7 "buf"
+               OpMemberName %7 0 "f"
+               OpName %9 ""
+               OpName %10 "buf2"
+               OpMemberName %10 0 "g"
+               OpName %12 ""
+               OpMemberDecorate %7 0 Offset 0
+               OpDecorate %7 Block
+               OpDecorate %9 DescriptorSet 0
+               OpDecorate %9 Binding 0
+               OpMemberDecorate %10 0 Offset 0
+               OpDecorate %10 Block
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeStruct %6
+          %8 = OpTypePointer Uniform %7
+          %9 = OpVariable %8 Uniform
+         %10 = OpTypeStruct %6
+         %11 = OpTypePointer Uniform %10
+         %12 = OpVariable %11 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  auto uniform_buffer_element_descriptor =
+      MakeUniformBufferElementDescriptor(0, 0, {0});
+
+  // The fact cannot be added because it is ambiguous: there are two uniforms
+  // with descriptor set 0 and binding 0.
+  ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {1},
+                             uniform_buffer_element_descriptor));
+}
+
 }  // namespace
 }  // namespace
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools

+ 393 - 0
3rdparty/spirv-tools/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp

@@ -0,0 +1,393 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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/fuzz/fuzzer_pass_add_useful_constructs.h"
+#include "source/fuzz/pseudo_random_generator.h"
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+bool AddFactHelper(
+    FactManager* fact_manager, opt::IRContext* context, uint32_t word,
+    const protobufs::UniformBufferElementDescriptor& descriptor) {
+  protobufs::FactConstantUniform constant_uniform_fact;
+  constant_uniform_fact.add_constant_word(word);
+  *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() =
+      descriptor;
+  protobufs::Fact fact;
+  *fact.mutable_constant_uniform_fact() = constant_uniform_fact;
+  return fact_manager->AddFact(fact, context);
+}
+
+TEST(FuzzerPassAddUsefulConstructsTest, CheckBasicStuffIsAdded) {
+  // The SPIR-V came from the following empty GLSL shader:
+  //
+  // #version 450
+  //
+  // void main()
+  // {
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
+                                     &fuzzer_context, &transformation_sequence);
+  pass.Apply();
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  std::string after = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+        %100 = OpTypeBool
+        %101 = OpTypeInt 32 1
+        %102 = OpTypeInt 32 0
+        %103 = OpTypeFloat 32
+        %104 = OpConstantTrue %100
+        %105 = OpConstantFalse %100
+        %106 = OpConstant %101 0
+        %107 = OpConstant %101 1
+        %108 = OpConstant %102 0
+        %109 = OpConstant %102 1
+        %110 = OpConstant %103 0
+        %111 = OpConstant %103 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after, context.get()));
+}
+
+TEST(FuzzerPassAddUsefulConstructsTest,
+     CheckTypesIndicesAndConstantsAddedForUniformFacts) {
+  // The SPIR-V came from the following GLSL shader:
+  //
+  // #version 450
+  //
+  // struct S {
+  //   int x;
+  //   float y;
+  //   int z;
+  //   int w;
+  // };
+  //
+  // uniform buf {
+  //   S s;
+  //   uint w[10];
+  // };
+  //
+  // void main() {
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %8 "S"
+               OpMemberName %8 0 "x"
+               OpMemberName %8 1 "y"
+               OpMemberName %8 2 "z"
+               OpMemberName %8 3 "w"
+               OpName %12 "buf"
+               OpMemberName %12 0 "s"
+               OpMemberName %12 1 "w"
+               OpName %14 ""
+               OpMemberDecorate %8 0 Offset 0
+               OpMemberDecorate %8 1 Offset 4
+               OpMemberDecorate %8 2 Offset 8
+               OpMemberDecorate %8 3 Offset 12
+               OpDecorate %11 ArrayStride 16
+               OpMemberDecorate %12 0 Offset 0
+               OpMemberDecorate %12 1 Offset 16
+               OpDecorate %12 Block
+               OpDecorate %14 DescriptorSet 0
+               OpDecorate %14 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeStruct %6 %7 %6 %6
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 10
+         %11 = OpTypeArray %9 %10
+         %12 = OpTypeStruct %8 %11
+         %13 = OpTypePointer Uniform %12
+         %14 = OpVariable %13 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+  protobufs::TransformationSequence transformation_sequence;
+
+  // Add some uniform facts.
+
+  // buf.s.x == 200
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200,
+                            MakeUniformBufferElementDescriptor(0, 0, {0, 0})));
+
+  // buf.s.y == 0.5
+  const float float_value = 0.5;
+  uint32_t float_value_as_uint;
+  memcpy(&float_value_as_uint, &float_value, sizeof(float_value));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_value_as_uint,
+                            MakeUniformBufferElementDescriptor(0, 0, {0, 1})));
+
+  // buf.s.z == 300
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 300,
+                            MakeUniformBufferElementDescriptor(0, 0, {0, 2})));
+
+  // buf.s.w == 400
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 400,
+                            MakeUniformBufferElementDescriptor(0, 0, {0, 3})));
+
+  // buf.w[6] = 22
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22,
+                            MakeUniformBufferElementDescriptor(0, 0, {1, 6})));
+
+  // buf.w[8] = 23
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 23,
+                            MakeUniformBufferElementDescriptor(0, 0, {1, 8})));
+
+  // Assert some things about the module that are not true prior to adding the
+  // pass
+
+  {
+    // No uniform int pointer
+    opt::analysis::Integer temp_type_signed_int(32, true);
+    opt::analysis::Integer* registered_type_signed_int =
+        context->get_type_mgr()
+            ->GetRegisteredType(&temp_type_signed_int)
+            ->AsInteger();
+    opt::analysis::Pointer type_pointer_uniform_signed_int(
+        registered_type_signed_int, SpvStorageClassUniform);
+    ASSERT_EQ(0,
+              context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
+
+    // No uniform uint pointer
+    opt::analysis::Integer temp_type_unsigned_int(32, false);
+    opt::analysis::Integer* registered_type_unsigned_int =
+        context->get_type_mgr()
+            ->GetRegisteredType(&temp_type_unsigned_int)
+            ->AsInteger();
+    opt::analysis::Pointer type_pointer_uniform_unsigned_int(
+        registered_type_unsigned_int, SpvStorageClassUniform);
+    ASSERT_EQ(
+        0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
+
+    // No uniform float pointer
+    opt::analysis::Float temp_type_float(32);
+    opt::analysis::Float* registered_type_float =
+        context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
+    opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
+                                                      SpvStorageClassUniform);
+    ASSERT_EQ(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
+
+    // No int constants 200, 300 nor 400
+    opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
+                                                {200});
+    opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
+                                                {300});
+    opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
+                                                {400});
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_200));
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_300));
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_400));
+
+    // No float constant 0.5
+    opt::analysis::FloatConstant float_constant_zero_point_five(
+        registered_type_float, {float_value_as_uint});
+    ASSERT_EQ(nullptr, context->get_constant_mgr()->FindConstant(
+                           &float_constant_zero_point_five));
+
+    // No uint constant 22
+    opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
+                                                {22});
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&uint_constant_22));
+
+    // No uint constant 23
+    opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
+                                                {23});
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&uint_constant_23));
+
+    // No int constants 0, 1, 2, 3, 6, 8
+    opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
+    opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
+    opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
+    opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
+    opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
+    opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_0));
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_1));
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_2));
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_3));
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_6));
+    ASSERT_EQ(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_8));
+  }
+
+  FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
+                                     &fuzzer_context, &transformation_sequence);
+  pass.Apply();
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Now assert some things about the module that should be true following the
+  // pass.
+
+  // We reconstruct all necessary types and constants to guard against the type
+  // and constant managers for the module having been invalidated.
+
+  {
+    // Uniform int pointer now present
+    opt::analysis::Integer temp_type_signed_int(32, true);
+    opt::analysis::Integer* registered_type_signed_int =
+        context->get_type_mgr()
+            ->GetRegisteredType(&temp_type_signed_int)
+            ->AsInteger();
+    opt::analysis::Pointer type_pointer_uniform_signed_int(
+        registered_type_signed_int, SpvStorageClassUniform);
+    ASSERT_NE(0,
+              context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
+
+    // Uniform uint pointer now present
+    opt::analysis::Integer temp_type_unsigned_int(32, false);
+    opt::analysis::Integer* registered_type_unsigned_int =
+        context->get_type_mgr()
+            ->GetRegisteredType(&temp_type_unsigned_int)
+            ->AsInteger();
+    opt::analysis::Pointer type_pointer_uniform_unsigned_int(
+        registered_type_unsigned_int, SpvStorageClassUniform);
+    ASSERT_NE(
+        0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
+
+    // Uniform float pointer now present
+    opt::analysis::Float temp_type_float(32);
+    opt::analysis::Float* registered_type_float =
+        context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
+    opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
+                                                      SpvStorageClassUniform);
+    ASSERT_NE(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
+
+    // int constants 200, 300, 400 now present
+    opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
+                                                {200});
+    opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
+                                                {300});
+    opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
+                                                {400});
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_200));
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_300));
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_400));
+
+    // float constant 0.5 now present
+    opt::analysis::FloatConstant float_constant_zero_point_five(
+        registered_type_float, {float_value_as_uint});
+    ASSERT_NE(nullptr, context->get_constant_mgr()->FindConstant(
+                           &float_constant_zero_point_five));
+
+    // uint constant 22 now present
+    opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
+                                                {22});
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&uint_constant_22));
+
+    // uint constant 23 now present
+    opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
+                                                {23});
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&uint_constant_23));
+
+    // int constants 0, 1, 2, 3, 6, 8 now present
+    opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
+    opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
+    opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
+    opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
+    opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
+    opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_0));
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_1));
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_2));
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_3));
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_6));
+    ASSERT_NE(nullptr,
+              context->get_constant_mgr()->FindConstant(&int_constant_8));
+  }
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools

+ 500 - 9
3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp

@@ -14,6 +14,7 @@
 
 
 #include "source/fuzz/fuzzer.h"
 #include "source/fuzz/fuzzer.h"
 #include "source/fuzz/replayer.h"
 #include "source/fuzz/replayer.h"
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 
 namespace spvtools {
 namespace spvtools {
@@ -25,8 +26,9 @@ namespace {
 // the binary produced after each fuzzer run is valid, and that replaying
 // the binary produced after each fuzzer run is valid, and that replaying
 // the transformations that were applied during fuzzing leads to an
 // the transformations that were applied during fuzzing leads to an
 // identical binary.
 // identical binary.
-void RunFuzzerAndReplayer(const std::string& shader, uint32_t initial_seed,
-                          uint32_t num_runs) {
+void RunFuzzerAndReplayer(const std::string& shader,
+                          const protobufs::FactSequence& initial_facts,
+                          uint32_t initial_seed, uint32_t num_runs) {
   const auto env = SPV_ENV_UNIVERSAL_1_3;
   const auto env = SPV_ENV_UNIVERSAL_1_3;
 
 
   std::vector<uint32_t> binary_in;
   std::vector<uint32_t> binary_in;
@@ -35,23 +37,27 @@ void RunFuzzerAndReplayer(const std::string& shader, uint32_t initial_seed,
   ASSERT_TRUE(t.Validate(binary_in));
   ASSERT_TRUE(t.Validate(binary_in));
 
 
   for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
   for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
-    protobufs::FactSequence initial_facts;
     std::vector<uint32_t> fuzzer_binary_out;
     std::vector<uint32_t> fuzzer_binary_out;
     protobufs::TransformationSequence fuzzer_transformation_sequence_out;
     protobufs::TransformationSequence fuzzer_transformation_sequence_out;
     spvtools::FuzzerOptions fuzzer_options;
     spvtools::FuzzerOptions fuzzer_options;
     spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
     spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
 
 
     Fuzzer fuzzer(env);
     Fuzzer fuzzer(env);
-    fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
-               &fuzzer_transformation_sequence_out, fuzzer_options);
+    auto fuzzer_result_status =
+        fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
+                   &fuzzer_transformation_sequence_out, fuzzer_options);
+    ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
     ASSERT_TRUE(t.Validate(fuzzer_binary_out));
     ASSERT_TRUE(t.Validate(fuzzer_binary_out));
 
 
     std::vector<uint32_t> replayer_binary_out;
     std::vector<uint32_t> replayer_binary_out;
     protobufs::TransformationSequence replayer_transformation_sequence_out;
     protobufs::TransformationSequence replayer_transformation_sequence_out;
 
 
     Replayer replayer(env);
     Replayer replayer(env);
-    replayer.Run(binary_in, initial_facts, fuzzer_transformation_sequence_out,
-                 &replayer_binary_out, &replayer_transformation_sequence_out);
+    auto replayer_result_status = replayer.Run(
+        binary_in, initial_facts, fuzzer_transformation_sequence_out,
+        &replayer_binary_out, &replayer_transformation_sequence_out);
+    ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
+              replayer_result_status);
 
 
     // After replaying the transformations applied by the fuzzer, exactly those
     // After replaying the transformations applied by the fuzzer, exactly those
     // transformations should have been applied, and the binary resulting from
     // transformations should have been applied, and the binary resulting from
@@ -233,7 +239,7 @@ TEST(FuzzerReplayerTest, Miscellaneous1) {
 
 
   // Do 10 fuzzer runs, starting from an initial seed of 0 (seed value chosen
   // Do 10 fuzzer runs, starting from an initial seed of 0 (seed value chosen
   // arbitrarily).
   // arbitrarily).
-  RunFuzzerAndReplayer(shader, 0, 10);
+  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, 10);
 }
 }
 
 
 TEST(FuzzerReplayerTest, Miscellaneous2) {
 TEST(FuzzerReplayerTest, Miscellaneous2) {
@@ -478,7 +484,492 @@ TEST(FuzzerReplayerTest, Miscellaneous2) {
 
 
   // Do 10 fuzzer runs, starting from an initial seed of 10 (seed value chosen
   // Do 10 fuzzer runs, starting from an initial seed of 10 (seed value chosen
   // arbitrarily).
   // arbitrarily).
-  RunFuzzerAndReplayer(shader, 10, 10);
+  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, 10);
+}
+
+TEST(FuzzerReplayerTest, Miscellaneous3) {
+  // The SPIR-V came from this GLSL, which was then optimized using spirv-opt
+  // with the -O argument:
+  //
+  // #version 310 es
+  //
+  // precision highp float;
+  //
+  // layout(location = 0) out vec4 _GLF_color;
+  //
+  // layout(set = 0, binding = 0) uniform buf0 {
+  //  vec2 resolution;
+  // };
+  // void main(void)
+  // {
+  //  float A[50];
+  //  for(
+  //      int i = 0;
+  //      i < 200;
+  //      i ++
+  //  )
+  //   {
+  //    if(i >= int(resolution.x))
+  //     {
+  //      break;
+  //     }
+  //    if((4 * (i / 4)) == i)
+  //     {
+  //      A[i / 4] = float(i);
+  //     }
+  //   }
+  //  for(
+  //      int i = 0;
+  //      i < 50;
+  //      i ++
+  //  )
+  //   {
+  //    if(i < int(gl_FragCoord.x))
+  //     {
+  //      break;
+  //     }
+  //    if(i > 0)
+  //     {
+  //      A[i] += A[i - 1];
+  //     }
+  //   }
+  //  if(int(gl_FragCoord.x) < 20)
+  //   {
+  //    _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0);
+  //   }
+  //  else
+  //   if(int(gl_FragCoord.x) < 40)
+  //    {
+  //     _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0);
+  //    }
+  //   else
+  //    if(int(gl_FragCoord.x) < 60)
+  //     {
+  //      _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y,
+  //      1.0, 1.0);
+  //     }
+  //    else
+  //     if(int(gl_FragCoord.x) < 80)
+  //      {
+  //       _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y,
+  //       1.0, 1.0);
+  //      }
+  //     else
+  //      if(int(gl_FragCoord.x) < 100)
+  //       {
+  //        _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y,
+  //        1.0, 1.0);
+  //       }
+  //      else
+  //       if(int(gl_FragCoord.x) < 120)
+  //        {
+  //         _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y,
+  //         1.0, 1.0);
+  //        }
+  //       else
+  //        if(int(gl_FragCoord.x) < 140)
+  //         {
+  //          _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y,
+  //          1.0, 1.0);
+  //         }
+  //        else
+  //         if(int(gl_FragCoord.x) < 160)
+  //          {
+  //           _GLF_color = vec4(A[35] / resolution.x, A[39] /
+  //           resolution.y, 1.0, 1.0);
+  //          }
+  //         else
+  //          if(int(gl_FragCoord.x) < 180)
+  //           {
+  //            _GLF_color = vec4(A[40] / resolution.x, A[44] /
+  //            resolution.y, 1.0, 1.0);
+  //           }
+  //          else
+  //           if(int(gl_FragCoord.x) < 180)
+  //            {
+  //             _GLF_color = vec4(A[45] / resolution.x, A[49] /
+  //             resolution.y, 1.0, 1.0);
+  //            }
+  //           else
+  //            {
+  //             discard;
+  //            }
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %68 %100
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %22 "buf0"
+               OpMemberName %22 0 "resolution"
+               OpName %24 ""
+               OpName %46 "A"
+               OpName %68 "gl_FragCoord"
+               OpName %100 "_GLF_color"
+               OpMemberDecorate %22 0 Offset 0
+               OpDecorate %22 Block
+               OpDecorate %24 DescriptorSet 0
+               OpDecorate %24 Binding 0
+               OpDecorate %37 RelaxedPrecision
+               OpDecorate %38 RelaxedPrecision
+               OpDecorate %55 RelaxedPrecision
+               OpDecorate %68 BuiltIn FragCoord
+               OpDecorate %83 RelaxedPrecision
+               OpDecorate %91 RelaxedPrecision
+               OpDecorate %100 Location 0
+               OpDecorate %302 RelaxedPrecision
+               OpDecorate %304 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 200
+         %17 = OpTypeBool
+         %20 = OpTypeFloat 32
+         %21 = OpTypeVector %20 2
+         %22 = OpTypeStruct %21
+         %23 = OpTypePointer Uniform %22
+         %24 = OpVariable %23 Uniform
+         %25 = OpTypeInt 32 0
+         %26 = OpConstant %25 0
+         %27 = OpTypePointer Uniform %20
+         %35 = OpConstant %6 4
+         %43 = OpConstant %25 50
+         %44 = OpTypeArray %20 %43
+         %45 = OpTypePointer Function %44
+         %51 = OpTypePointer Function %20
+         %54 = OpConstant %6 1
+         %63 = OpConstant %6 50
+         %66 = OpTypeVector %20 4
+         %67 = OpTypePointer Input %66
+         %68 = OpVariable %67 Input
+         %69 = OpTypePointer Input %20
+         %95 = OpConstant %6 20
+         %99 = OpTypePointer Output %66
+        %100 = OpVariable %99 Output
+        %108 = OpConstant %25 1
+        %112 = OpConstant %20 1
+        %118 = OpConstant %6 40
+        %122 = OpConstant %6 5
+        %128 = OpConstant %6 9
+        %139 = OpConstant %6 60
+        %143 = OpConstant %6 10
+        %149 = OpConstant %6 14
+        %160 = OpConstant %6 80
+        %164 = OpConstant %6 15
+        %170 = OpConstant %6 19
+        %181 = OpConstant %6 100
+        %190 = OpConstant %6 24
+        %201 = OpConstant %6 120
+        %205 = OpConstant %6 25
+        %211 = OpConstant %6 29
+        %222 = OpConstant %6 140
+        %226 = OpConstant %6 30
+        %232 = OpConstant %6 34
+        %243 = OpConstant %6 160
+        %247 = OpConstant %6 35
+        %253 = OpConstant %6 39
+        %264 = OpConstant %6 180
+        %273 = OpConstant %6 44
+        %287 = OpConstant %6 45
+        %293 = OpConstant %6 49
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %46 = OpVariable %45 Function
+               OpBranch %10
+         %10 = OpLabel
+        %302 = OpPhi %6 %9 %5 %55 %42
+         %18 = OpSLessThan %17 %302 %16
+               OpLoopMerge %12 %42 None
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+         %28 = OpAccessChain %27 %24 %9 %26
+         %29 = OpLoad %20 %28
+         %30 = OpConvertFToS %6 %29
+         %31 = OpSGreaterThanEqual %17 %302 %30
+               OpSelectionMerge %33 None
+               OpBranchConditional %31 %32 %33
+         %32 = OpLabel
+               OpBranch %12
+         %33 = OpLabel
+         %37 = OpSDiv %6 %302 %35
+         %38 = OpIMul %6 %35 %37
+         %40 = OpIEqual %17 %38 %302
+               OpSelectionMerge %42 None
+               OpBranchConditional %40 %41 %42
+         %41 = OpLabel
+         %50 = OpConvertSToF %20 %302
+         %52 = OpAccessChain %51 %46 %37
+               OpStore %52 %50
+               OpBranch %42
+         %42 = OpLabel
+         %55 = OpIAdd %6 %302 %54
+               OpBranch %10
+         %12 = OpLabel
+               OpBranch %57
+         %57 = OpLabel
+        %304 = OpPhi %6 %9 %12 %91 %80
+         %64 = OpSLessThan %17 %304 %63
+               OpLoopMerge %59 %80 None
+               OpBranchConditional %64 %58 %59
+         %58 = OpLabel
+         %70 = OpAccessChain %69 %68 %26
+         %71 = OpLoad %20 %70
+         %72 = OpConvertFToS %6 %71
+         %73 = OpSLessThan %17 %304 %72
+               OpSelectionMerge %75 None
+               OpBranchConditional %73 %74 %75
+         %74 = OpLabel
+               OpBranch %59
+         %75 = OpLabel
+         %78 = OpSGreaterThan %17 %304 %9
+               OpSelectionMerge %80 None
+               OpBranchConditional %78 %79 %80
+         %79 = OpLabel
+         %83 = OpISub %6 %304 %54
+         %84 = OpAccessChain %51 %46 %83
+         %85 = OpLoad %20 %84
+         %86 = OpAccessChain %51 %46 %304
+         %87 = OpLoad %20 %86
+         %88 = OpFAdd %20 %87 %85
+               OpStore %86 %88
+               OpBranch %80
+         %80 = OpLabel
+         %91 = OpIAdd %6 %304 %54
+               OpBranch %57
+         %59 = OpLabel
+         %92 = OpAccessChain %69 %68 %26
+         %93 = OpLoad %20 %92
+         %94 = OpConvertFToS %6 %93
+         %96 = OpSLessThan %17 %94 %95
+               OpSelectionMerge %98 None
+               OpBranchConditional %96 %97 %114
+         %97 = OpLabel
+        %101 = OpAccessChain %51 %46 %9
+        %102 = OpLoad %20 %101
+        %103 = OpAccessChain %27 %24 %9 %26
+        %104 = OpLoad %20 %103
+        %105 = OpFDiv %20 %102 %104
+        %106 = OpAccessChain %51 %46 %35
+        %107 = OpLoad %20 %106
+        %109 = OpAccessChain %27 %24 %9 %108
+        %110 = OpLoad %20 %109
+        %111 = OpFDiv %20 %107 %110
+        %113 = OpCompositeConstruct %66 %105 %111 %112 %112
+               OpStore %100 %113
+               OpBranch %98
+        %114 = OpLabel
+        %119 = OpSLessThan %17 %94 %118
+               OpSelectionMerge %121 None
+               OpBranchConditional %119 %120 %135
+        %120 = OpLabel
+        %123 = OpAccessChain %51 %46 %122
+        %124 = OpLoad %20 %123
+        %125 = OpAccessChain %27 %24 %9 %26
+        %126 = OpLoad %20 %125
+        %127 = OpFDiv %20 %124 %126
+        %129 = OpAccessChain %51 %46 %128
+        %130 = OpLoad %20 %129
+        %131 = OpAccessChain %27 %24 %9 %108
+        %132 = OpLoad %20 %131
+        %133 = OpFDiv %20 %130 %132
+        %134 = OpCompositeConstruct %66 %127 %133 %112 %112
+               OpStore %100 %134
+               OpBranch %121
+        %135 = OpLabel
+        %140 = OpSLessThan %17 %94 %139
+               OpSelectionMerge %142 None
+               OpBranchConditional %140 %141 %156
+        %141 = OpLabel
+        %144 = OpAccessChain %51 %46 %143
+        %145 = OpLoad %20 %144
+        %146 = OpAccessChain %27 %24 %9 %26
+        %147 = OpLoad %20 %146
+        %148 = OpFDiv %20 %145 %147
+        %150 = OpAccessChain %51 %46 %149
+        %151 = OpLoad %20 %150
+        %152 = OpAccessChain %27 %24 %9 %108
+        %153 = OpLoad %20 %152
+        %154 = OpFDiv %20 %151 %153
+        %155 = OpCompositeConstruct %66 %148 %154 %112 %112
+               OpStore %100 %155
+               OpBranch %142
+        %156 = OpLabel
+        %161 = OpSLessThan %17 %94 %160
+               OpSelectionMerge %163 None
+               OpBranchConditional %161 %162 %177
+        %162 = OpLabel
+        %165 = OpAccessChain %51 %46 %164
+        %166 = OpLoad %20 %165
+        %167 = OpAccessChain %27 %24 %9 %26
+        %168 = OpLoad %20 %167
+        %169 = OpFDiv %20 %166 %168
+        %171 = OpAccessChain %51 %46 %170
+        %172 = OpLoad %20 %171
+        %173 = OpAccessChain %27 %24 %9 %108
+        %174 = OpLoad %20 %173
+        %175 = OpFDiv %20 %172 %174
+        %176 = OpCompositeConstruct %66 %169 %175 %112 %112
+               OpStore %100 %176
+               OpBranch %163
+        %177 = OpLabel
+        %182 = OpSLessThan %17 %94 %181
+               OpSelectionMerge %184 None
+               OpBranchConditional %182 %183 %197
+        %183 = OpLabel
+        %185 = OpAccessChain %51 %46 %95
+        %186 = OpLoad %20 %185
+        %187 = OpAccessChain %27 %24 %9 %26
+        %188 = OpLoad %20 %187
+        %189 = OpFDiv %20 %186 %188
+        %191 = OpAccessChain %51 %46 %190
+        %192 = OpLoad %20 %191
+        %193 = OpAccessChain %27 %24 %9 %108
+        %194 = OpLoad %20 %193
+        %195 = OpFDiv %20 %192 %194
+        %196 = OpCompositeConstruct %66 %189 %195 %112 %112
+               OpStore %100 %196
+               OpBranch %184
+        %197 = OpLabel
+        %202 = OpSLessThan %17 %94 %201
+               OpSelectionMerge %204 None
+               OpBranchConditional %202 %203 %218
+        %203 = OpLabel
+        %206 = OpAccessChain %51 %46 %205
+        %207 = OpLoad %20 %206
+        %208 = OpAccessChain %27 %24 %9 %26
+        %209 = OpLoad %20 %208
+        %210 = OpFDiv %20 %207 %209
+        %212 = OpAccessChain %51 %46 %211
+        %213 = OpLoad %20 %212
+        %214 = OpAccessChain %27 %24 %9 %108
+        %215 = OpLoad %20 %214
+        %216 = OpFDiv %20 %213 %215
+        %217 = OpCompositeConstruct %66 %210 %216 %112 %112
+               OpStore %100 %217
+               OpBranch %204
+        %218 = OpLabel
+        %223 = OpSLessThan %17 %94 %222
+               OpSelectionMerge %225 None
+               OpBranchConditional %223 %224 %239
+        %224 = OpLabel
+        %227 = OpAccessChain %51 %46 %226
+        %228 = OpLoad %20 %227
+        %229 = OpAccessChain %27 %24 %9 %26
+        %230 = OpLoad %20 %229
+        %231 = OpFDiv %20 %228 %230
+        %233 = OpAccessChain %51 %46 %232
+        %234 = OpLoad %20 %233
+        %235 = OpAccessChain %27 %24 %9 %108
+        %236 = OpLoad %20 %235
+        %237 = OpFDiv %20 %234 %236
+        %238 = OpCompositeConstruct %66 %231 %237 %112 %112
+               OpStore %100 %238
+               OpBranch %225
+        %239 = OpLabel
+        %244 = OpSLessThan %17 %94 %243
+               OpSelectionMerge %246 None
+               OpBranchConditional %244 %245 %260
+        %245 = OpLabel
+        %248 = OpAccessChain %51 %46 %247
+        %249 = OpLoad %20 %248
+        %250 = OpAccessChain %27 %24 %9 %26
+        %251 = OpLoad %20 %250
+        %252 = OpFDiv %20 %249 %251
+        %254 = OpAccessChain %51 %46 %253
+        %255 = OpLoad %20 %254
+        %256 = OpAccessChain %27 %24 %9 %108
+        %257 = OpLoad %20 %256
+        %258 = OpFDiv %20 %255 %257
+        %259 = OpCompositeConstruct %66 %252 %258 %112 %112
+               OpStore %100 %259
+               OpBranch %246
+        %260 = OpLabel
+        %265 = OpSLessThan %17 %94 %264
+               OpSelectionMerge %267 None
+               OpBranchConditional %265 %266 %280
+        %266 = OpLabel
+        %268 = OpAccessChain %51 %46 %118
+        %269 = OpLoad %20 %268
+        %270 = OpAccessChain %27 %24 %9 %26
+        %271 = OpLoad %20 %270
+        %272 = OpFDiv %20 %269 %271
+        %274 = OpAccessChain %51 %46 %273
+        %275 = OpLoad %20 %274
+        %276 = OpAccessChain %27 %24 %9 %108
+        %277 = OpLoad %20 %276
+        %278 = OpFDiv %20 %275 %277
+        %279 = OpCompositeConstruct %66 %272 %278 %112 %112
+               OpStore %100 %279
+               OpBranch %267
+        %280 = OpLabel
+               OpSelectionMerge %285 None
+               OpBranchConditional %265 %285 %300
+        %285 = OpLabel
+        %288 = OpAccessChain %51 %46 %287
+        %289 = OpLoad %20 %288
+        %290 = OpAccessChain %27 %24 %9 %26
+        %291 = OpLoad %20 %290
+        %292 = OpFDiv %20 %289 %291
+        %294 = OpAccessChain %51 %46 %293
+        %295 = OpLoad %20 %294
+        %296 = OpAccessChain %27 %24 %9 %108
+        %297 = OpLoad %20 %296
+        %298 = OpFDiv %20 %295 %297
+        %299 = OpCompositeConstruct %66 %292 %298 %112 %112
+               OpStore %100 %299
+               OpBranch %267
+        %300 = OpLabel
+               OpKill
+        %267 = OpLabel
+               OpBranch %246
+        %246 = OpLabel
+               OpBranch %225
+        %225 = OpLabel
+               OpBranch %204
+        %204 = OpLabel
+               OpBranch %184
+        %184 = OpLabel
+               OpBranch %163
+        %163 = OpLabel
+               OpBranch %142
+        %142 = OpLabel
+               OpBranch %121
+        %121 = OpLabel
+               OpBranch %98
+         %98 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  // Add the facts "resolution.x == 250" and "resolution.y == 100".
+  protobufs::FactSequence facts;
+  {
+    protobufs::FactConstantUniform resolution_x_eq_250;
+    *resolution_x_eq_250.mutable_uniform_buffer_element_descriptor() =
+        MakeUniformBufferElementDescriptor(0, 0, {0, 0});
+    *resolution_x_eq_250.mutable_constant_word()->Add() = 250;
+    protobufs::Fact temp;
+    *temp.mutable_constant_uniform_fact() = resolution_x_eq_250;
+    *facts.mutable_fact()->Add() = temp;
+  }
+  {
+    protobufs::FactConstantUniform resolution_y_eq_100;
+    *resolution_y_eq_100.mutable_uniform_buffer_element_descriptor() =
+        MakeUniformBufferElementDescriptor(0, 0, {0, 1});
+    *resolution_y_eq_100.mutable_constant_word()->Add() = 100;
+    protobufs::Fact temp;
+    *temp.mutable_constant_uniform_fact() = resolution_y_eq_100;
+    *facts.mutable_fact()->Add() = temp;
+  }
+
+  // Do 10 fuzzer runs, starting from an initial seed of 94 (seed value chosen
+  // arbitrarily).
+  RunFuzzerAndReplayer(shader, facts, 94, 10);
 }
 }
 
 
 }  // namespace
 }  // namespace

+ 218 - 0
3rdparty/spirv-tools/test/fuzz/transformation_add_type_pointer_test.cpp

@@ -0,0 +1,218 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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/fuzz/transformation_add_type_pointer.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddTypePointerTest, BasicTest) {
+  // The SPIR-V was obtained from this GLSL:
+  //
+  // #version 450
+  //
+  // int x;
+  // float y;
+  // vec2 z;
+  //
+  // struct T {
+  //   int a, b;
+  // };
+  //
+  // struct S {
+  //   T t;
+  //   int u;
+  // };
+  //
+  // void main() {
+  //   S myS = S(T(1, 2), 3);
+  //   myS.u = x;
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %7 "T"
+               OpMemberName %7 0 "a"
+               OpMemberName %7 1 "b"
+               OpName %8 "S"
+               OpMemberName %8 0 "t"
+               OpMemberName %8 1 "u"
+               OpName %10 "myS"
+               OpName %17 "x"
+               OpName %23 "y"
+               OpName %26 "z"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeStruct %6 %6
+          %8 = OpTypeStruct %7 %6
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %6 1
+         %12 = OpConstant %6 2
+         %13 = OpConstantComposite %7 %11 %12
+         %14 = OpConstant %6 3
+         %15 = OpConstantComposite %8 %13 %14
+         %16 = OpTypePointer Private %6
+         %17 = OpVariable %16 Private
+         %19 = OpTypePointer Function %6
+         %21 = OpTypeFloat 32
+         %22 = OpTypePointer Private %21
+         %23 = OpVariable %22 Private
+         %24 = OpTypeVector %21 2
+         %25 = OpTypePointer Private %24
+         %26 = OpVariable %25 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %10 = OpVariable %9 Function
+               OpStore %10 %15
+         %18 = OpLoad %6 %17
+         %20 = OpAccessChain %19 %10 %11
+               OpStore %20 %18
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_type_id_does_not_exist =
+      transformation::MakeTransformationAddTypePointer(
+          100, SpvStorageClassFunction, 101);
+  auto bad_type_id_is_not_type =
+      transformation::MakeTransformationAddTypePointer(
+          100, SpvStorageClassFunction, 23);
+  auto bad_result_id_is_not_fresh =
+      transformation::MakeTransformationAddTypePointer(
+          17, SpvStorageClassFunction, 21);
+
+  auto good_new_private_pointer_to_t =
+      transformation::MakeTransformationAddTypePointer(
+          101, SpvStorageClassPrivate, 7);
+  auto good_new_uniform_pointer_to_t =
+      transformation::MakeTransformationAddTypePointer(
+          102, SpvStorageClassUniform, 7);
+  auto good_another_function_pointer_to_s =
+      transformation::MakeTransformationAddTypePointer(
+          103, SpvStorageClassFunction, 8);
+  auto good_new_uniform_pointer_to_s =
+      transformation::MakeTransformationAddTypePointer(
+          104, SpvStorageClassUniform, 8);
+  auto good_another_private_pointer_to_float =
+      transformation::MakeTransformationAddTypePointer(
+          105, SpvStorageClassPrivate, 21);
+  auto good_new_private_pointer_to_private_pointer_to_float =
+      transformation::MakeTransformationAddTypePointer(
+          106, SpvStorageClassPrivate, 105);
+  auto good_new_uniform_pointer_to_vec2 =
+      transformation::MakeTransformationAddTypePointer(
+          107, SpvStorageClassUniform, 24);
+  auto good_new_private_pointer_to_uniform_pointer_to_vec2 =
+      transformation::MakeTransformationAddTypePointer(
+          108, SpvStorageClassPrivate, 107);
+
+  ASSERT_FALSE(transformation::IsApplicable(bad_type_id_does_not_exist,
+                                            context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(bad_type_id_is_not_type,
+                                            context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(bad_result_id_is_not_fresh,
+                                            context.get(), fact_manager));
+
+  for (auto& transformation :
+       {good_new_private_pointer_to_t, good_new_uniform_pointer_to_t,
+        good_another_function_pointer_to_s, good_new_uniform_pointer_to_s,
+        good_another_private_pointer_to_float,
+        good_new_private_pointer_to_private_pointer_to_float,
+        good_new_uniform_pointer_to_vec2,
+        good_new_private_pointer_to_uniform_pointer_to_vec2}) {
+    ASSERT_TRUE(transformation::IsApplicable(transformation, context.get(),
+                                             fact_manager));
+    transformation::Apply(transformation, context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %7 "T"
+               OpMemberName %7 0 "a"
+               OpMemberName %7 1 "b"
+               OpName %8 "S"
+               OpMemberName %8 0 "t"
+               OpMemberName %8 1 "u"
+               OpName %10 "myS"
+               OpName %17 "x"
+               OpName %23 "y"
+               OpName %26 "z"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeStruct %6 %6
+          %8 = OpTypeStruct %7 %6
+          %9 = OpTypePointer Function %8
+         %11 = OpConstant %6 1
+         %12 = OpConstant %6 2
+         %13 = OpConstantComposite %7 %11 %12
+         %14 = OpConstant %6 3
+         %15 = OpConstantComposite %8 %13 %14
+         %16 = OpTypePointer Private %6
+         %17 = OpVariable %16 Private
+         %19 = OpTypePointer Function %6
+         %21 = OpTypeFloat 32
+         %22 = OpTypePointer Private %21
+         %23 = OpVariable %22 Private
+         %24 = OpTypeVector %21 2
+         %25 = OpTypePointer Private %24
+         %26 = OpVariable %25 Private
+        %101 = OpTypePointer Private %7
+        %102 = OpTypePointer Uniform %7
+        %103 = OpTypePointer Function %8
+        %104 = OpTypePointer Uniform %8
+        %105 = OpTypePointer Private %21
+        %106 = OpTypePointer Private %105
+        %107 = OpTypePointer Uniform %24
+        %108 = OpTypePointer Private %107
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %10 = OpVariable %9 Function
+               OpStore %10 %15
+         %18 = OpLoad %6 %17
+         %20 = OpAccessChain %19 %10 %11
+               OpStore %20 %18
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools

+ 1486 - 0
3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp

@@ -0,0 +1,1486 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+bool AddFactHelper(
+    FactManager* fact_manager, opt::IRContext* context, uint32_t word,
+    const protobufs::UniformBufferElementDescriptor& descriptor) {
+  protobufs::FactConstantUniform constant_uniform_fact;
+  constant_uniform_fact.add_constant_word(word);
+  *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() =
+      descriptor;
+  protobufs::Fact fact;
+  *fact.mutable_constant_uniform_fact() = constant_uniform_fact;
+  return fact_manager->AddFact(fact, context);
+}
+
+TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
+  // This test came from the following GLSL:
+  //
+  // #version 450
+  //
+  // uniform blockname {
+  //   int a;
+  //   int b;
+  //   int c;
+  // };
+  //
+  // void main()
+  // {
+  //   int x;
+  //   x = 1;
+  //   x = x + 2;
+  //   x = 3 + x;
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %16 "blockname"
+               OpMemberName %16 0 "a"
+               OpMemberName %16 1 "b"
+               OpMemberName %16 2 "c"
+               OpName %18 ""
+               OpMemberDecorate %16 0 Offset 0
+               OpMemberDecorate %16 1 Offset 4
+               OpMemberDecorate %16 2 Offset 8
+               OpDecorate %16 Block
+               OpDecorate %18 DescriptorSet 0
+               OpDecorate %18 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %50 = OpConstant %6 0
+          %9 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %14 = OpConstant %6 3
+         %16 = OpTypeStruct %6 %6 %6
+         %17 = OpTypePointer Uniform %16
+         %51 = OpTypePointer Uniform %6
+         %18 = OpVariable %17 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+         %10 = OpLoad %6 %8
+         %12 = OpIAdd %6 %10 %11
+               OpStore %8 %12
+         %13 = OpLoad %6 %8
+         %15 = OpIAdd %6 %14 %13
+               OpStore %8 %15
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  protobufs::UniformBufferElementDescriptor blockname_a =
+      MakeUniformBufferElementDescriptor(0, 0, {0});
+  protobufs::UniformBufferElementDescriptor blockname_b =
+      MakeUniformBufferElementDescriptor(0, 0, {1});
+  protobufs::UniformBufferElementDescriptor blockname_c =
+      MakeUniformBufferElementDescriptor(0, 0, {2});
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, blockname_a));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, blockname_b));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, blockname_c));
+
+  // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively.
+  protobufs::IdUseDescriptor use_of_9_in_store =
+      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+  protobufs::IdUseDescriptor use_of_11_in_add =
+      transformation::MakeIdUseDescriptor(11, SpvOpIAdd, 1, 12, 0);
+  protobufs::IdUseDescriptor use_of_14_in_add =
+      transformation::MakeIdUseDescriptor(14, SpvOpIAdd, 0, 15, 0);
+
+  // These transformations work: they match the facts.
+  auto transformation_use_of_9_in_store =
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_9_in_store, blockname_a, 100, 101);
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_9_in_store,
+                                           context.get(), fact_manager));
+  auto transformation_use_of_11_in_add =
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_11_in_add, blockname_b, 102, 103);
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_11_in_add,
+                                           context.get(), fact_manager));
+  auto transformation_use_of_14_in_add =
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_14_in_add, blockname_c, 104, 105);
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_14_in_add,
+                                           context.get(), fact_manager));
+
+  // The transformations are not applicable if we change which uniforms are
+  // applied to which constants.
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_9_in_store, blockname_b, 101, 102),
+      context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_11_in_add, blockname_c, 101, 102),
+      context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_14_in_add, blockname_a, 101, 102),
+      context.get(), fact_manager));
+
+  // The following transformations do not apply because the uniform descriptors
+  // are not sensible.
+  protobufs::UniformBufferElementDescriptor nonsense_uniform_descriptor1 =
+      MakeUniformBufferElementDescriptor(1, 2, {0});
+  protobufs::UniformBufferElementDescriptor nonsense_uniform_descriptor2 =
+      MakeUniformBufferElementDescriptor(0, 0, {5});
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_9_in_store, nonsense_uniform_descriptor1, 101, 102),
+      context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_9_in_store, nonsense_uniform_descriptor2, 101, 102),
+      context.get(), fact_manager));
+
+  // The following transformation does not apply because the id descriptor is
+  // not sensible.
+  protobufs::IdUseDescriptor nonsense_id_use_descriptor =
+      transformation::MakeIdUseDescriptor(9, SpvOpIAdd, 0, 15, 0);
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          nonsense_id_use_descriptor, blockname_a, 101, 102),
+      context.get(), fact_manager));
+
+  // The following transformations do not apply because the ids are not fresh.
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_11_in_add, blockname_b, 15, 103),
+      context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_11_in_add, blockname_b, 102, 15),
+      context.get(), fact_manager));
+
+  // Apply the use of 9 in a store.
+  transformation::Apply(transformation_use_of_9_in_store, context.get(),
+                        &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string after_replacing_use_of_9_in_store = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %16 "blockname"
+               OpMemberName %16 0 "a"
+               OpMemberName %16 1 "b"
+               OpMemberName %16 2 "c"
+               OpName %18 ""
+               OpMemberDecorate %16 0 Offset 0
+               OpMemberDecorate %16 1 Offset 4
+               OpMemberDecorate %16 2 Offset 8
+               OpDecorate %16 Block
+               OpDecorate %18 DescriptorSet 0
+               OpDecorate %18 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %50 = OpConstant %6 0
+          %9 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %14 = OpConstant %6 3
+         %16 = OpTypeStruct %6 %6 %6
+         %17 = OpTypePointer Uniform %16
+         %51 = OpTypePointer Uniform %6
+         %18 = OpVariable %17 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+        %100 = OpAccessChain %51 %18 %50
+        %101 = OpLoad %6 %100
+               OpStore %8 %101
+         %10 = OpLoad %6 %8
+         %12 = OpIAdd %6 %10 %11
+               OpStore %8 %12
+         %13 = OpLoad %6 %8
+         %15 = OpIAdd %6 %14 %13
+               OpStore %8 %15
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_replacing_use_of_9_in_store, context.get()));
+
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_11_in_add,
+                                           context.get(), fact_manager));
+  // Apply the use of 11 in an add.
+  transformation::Apply(transformation_use_of_11_in_add, context.get(),
+                        &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string after_replacing_use_of_11_in_add = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %16 "blockname"
+               OpMemberName %16 0 "a"
+               OpMemberName %16 1 "b"
+               OpMemberName %16 2 "c"
+               OpName %18 ""
+               OpMemberDecorate %16 0 Offset 0
+               OpMemberDecorate %16 1 Offset 4
+               OpMemberDecorate %16 2 Offset 8
+               OpDecorate %16 Block
+               OpDecorate %18 DescriptorSet 0
+               OpDecorate %18 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %50 = OpConstant %6 0
+          %9 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %14 = OpConstant %6 3
+         %16 = OpTypeStruct %6 %6 %6
+         %17 = OpTypePointer Uniform %16
+         %51 = OpTypePointer Uniform %6
+         %18 = OpVariable %17 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+        %100 = OpAccessChain %51 %18 %50
+        %101 = OpLoad %6 %100
+               OpStore %8 %101
+         %10 = OpLoad %6 %8
+        %102 = OpAccessChain %51 %18 %9
+        %103 = OpLoad %6 %102
+         %12 = OpIAdd %6 %10 %103
+               OpStore %8 %12
+         %13 = OpLoad %6 %8
+         %15 = OpIAdd %6 %14 %13
+               OpStore %8 %15
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_replacing_use_of_11_in_add, context.get()));
+
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_14_in_add,
+                                           context.get(), fact_manager));
+  // Apply the use of 15 in an add.
+  transformation::Apply(transformation_use_of_14_in_add, context.get(),
+                        &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  std::string after_replacing_use_of_14_in_add = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %16 "blockname"
+               OpMemberName %16 0 "a"
+               OpMemberName %16 1 "b"
+               OpMemberName %16 2 "c"
+               OpName %18 ""
+               OpMemberDecorate %16 0 Offset 0
+               OpMemberDecorate %16 1 Offset 4
+               OpMemberDecorate %16 2 Offset 8
+               OpDecorate %16 Block
+               OpDecorate %18 DescriptorSet 0
+               OpDecorate %18 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+         %50 = OpConstant %6 0
+          %9 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %14 = OpConstant %6 3
+         %16 = OpTypeStruct %6 %6 %6
+         %17 = OpTypePointer Uniform %16
+         %51 = OpTypePointer Uniform %6
+         %18 = OpVariable %17 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+        %100 = OpAccessChain %51 %18 %50
+        %101 = OpLoad %6 %100
+               OpStore %8 %101
+         %10 = OpLoad %6 %8
+        %102 = OpAccessChain %51 %18 %9
+        %103 = OpLoad %6 %102
+         %12 = OpIAdd %6 %10 %103
+               OpStore %8 %12
+         %13 = OpLoad %6 %8
+        %104 = OpAccessChain %51 %18 %11
+        %105 = OpLoad %6 %104
+         %15 = OpIAdd %6 %105 %13
+               OpStore %8 %15
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_replacing_use_of_14_in_add, context.get()));
+}
+
+TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) {
+  // This test came from the following GLSL:
+  //
+  // #version 450
+  //
+  // struct U {
+  //   int x; // == 4
+  // };
+  //
+  // struct T {
+  //   int x; // == 3
+  //   U y;
+  // };
+  //
+  // struct S {
+  //   T x;
+  //   int y; // == 2
+  // };
+  //
+  // uniform blockname {
+  //   int x; // == 1
+  //   S y;
+  // };
+  //
+  // void foo(int a) { }
+  //
+  // void main()
+  // {
+  //   int x;
+  //   x = 1;
+  //   x = x + 2;
+  //   x = 3 + x;
+  //   foo(4);
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %10 "foo(i1;"
+               OpName %9 "a"
+               OpName %12 "x"
+               OpName %21 "param"
+               OpName %23 "U"
+               OpMemberName %23 0 "x"
+               OpName %24 "T"
+               OpMemberName %24 0 "x"
+               OpMemberName %24 1 "y"
+               OpName %25 "S"
+               OpMemberName %25 0 "x"
+               OpMemberName %25 1 "y"
+               OpName %26 "blockname"
+               OpMemberName %26 0 "x"
+               OpMemberName %26 1 "y"
+               OpName %28 ""
+               OpMemberDecorate %23 0 Offset 0
+               OpMemberDecorate %24 0 Offset 0
+               OpMemberDecorate %24 1 Offset 16
+               OpMemberDecorate %25 0 Offset 0
+               OpMemberDecorate %25 1 Offset 32
+               OpMemberDecorate %26 0 Offset 0
+               OpMemberDecorate %26 1 Offset 16
+               OpDecorate %26 Block
+               OpDecorate %28 DescriptorSet 0
+               OpDecorate %28 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %50 = OpConstant %6 0
+         %13 = OpConstant %6 1
+         %15 = OpConstant %6 2
+         %17 = OpConstant %6 3
+         %20 = OpConstant %6 4
+         %23 = OpTypeStruct %6
+         %24 = OpTypeStruct %6 %23
+         %25 = OpTypeStruct %24 %6
+         %26 = OpTypeStruct %6 %25
+         %27 = OpTypePointer Uniform %26
+         %51 = OpTypePointer Uniform %6
+         %28 = OpVariable %27 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpVariable %7 Function
+         %21 = OpVariable %7 Function
+               OpStore %12 %13
+         %14 = OpLoad %6 %12
+         %16 = OpIAdd %6 %14 %15
+               OpStore %12 %16
+         %18 = OpLoad %6 %12
+         %19 = OpIAdd %6 %17 %18
+               OpStore %12 %19
+               OpStore %21 %20
+         %22 = OpFunctionCall %2 %10 %21
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  protobufs::UniformBufferElementDescriptor blockname_1 =
+      MakeUniformBufferElementDescriptor(0, 0, {0});
+  protobufs::UniformBufferElementDescriptor blockname_2 =
+      MakeUniformBufferElementDescriptor(0, 0, {1, 1});
+  protobufs::UniformBufferElementDescriptor blockname_3 =
+      MakeUniformBufferElementDescriptor(0, 0, {1, 0, 0});
+  protobufs::UniformBufferElementDescriptor blockname_4 =
+      MakeUniformBufferElementDescriptor(0, 0, {1, 0, 1, 0});
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, blockname_1));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, blockname_2));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, blockname_3));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 4, blockname_4));
+
+  // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively.
+  protobufs::IdUseDescriptor use_of_13_in_store =
+      transformation::MakeIdUseDescriptor(13, SpvOpStore, 1, 21, 0);
+  protobufs::IdUseDescriptor use_of_15_in_add =
+      transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 1, 16, 0);
+  protobufs::IdUseDescriptor use_of_17_in_add =
+      transformation::MakeIdUseDescriptor(17, SpvOpIAdd, 0, 19, 0);
+  protobufs::IdUseDescriptor use_of_20_in_store =
+      transformation::MakeIdUseDescriptor(20, SpvOpStore, 1, 19, 1);
+
+  // These transformations work: they match the facts.
+  auto transformation_use_of_13_in_store =
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_13_in_store, blockname_1, 100, 101);
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_13_in_store,
+                                           context.get(), fact_manager));
+  auto transformation_use_of_15_in_add =
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_15_in_add, blockname_2, 102, 103);
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_15_in_add,
+                                           context.get(), fact_manager));
+  auto transformation_use_of_17_in_add =
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_17_in_add, blockname_3, 104, 105);
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add,
+                                           context.get(), fact_manager));
+  auto transformation_use_of_20_in_store =
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_20_in_store, blockname_4, 106, 107);
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
+                                           context.get(), fact_manager));
+
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_13_in_store,
+                                           context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_15_in_add,
+                                           context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add,
+                                           context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
+                                           context.get(), fact_manager));
+
+  transformation::Apply(transformation_use_of_13_in_store, context.get(),
+                        &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store,
+                                            context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_15_in_add,
+                                           context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add,
+                                           context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
+                                           context.get(), fact_manager));
+
+  transformation::Apply(transformation_use_of_15_in_add, context.get(),
+                        &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store,
+                                            context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_15_in_add,
+                                            context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add,
+                                           context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
+                                           context.get(), fact_manager));
+
+  transformation::Apply(transformation_use_of_17_in_add, context.get(),
+                        &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store,
+                                            context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_15_in_add,
+                                            context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_17_in_add,
+                                            context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
+                                           context.get(), fact_manager));
+
+  transformation::Apply(transformation_use_of_20_in_store, context.get(),
+                        &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store,
+                                            context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_15_in_add,
+                                            context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_17_in_add,
+                                            context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_20_in_store,
+                                            context.get(), fact_manager));
+
+  std::string after = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %10 "foo(i1;"
+               OpName %9 "a"
+               OpName %12 "x"
+               OpName %21 "param"
+               OpName %23 "U"
+               OpMemberName %23 0 "x"
+               OpName %24 "T"
+               OpMemberName %24 0 "x"
+               OpMemberName %24 1 "y"
+               OpName %25 "S"
+               OpMemberName %25 0 "x"
+               OpMemberName %25 1 "y"
+               OpName %26 "blockname"
+               OpMemberName %26 0 "x"
+               OpMemberName %26 1 "y"
+               OpName %28 ""
+               OpMemberDecorate %23 0 Offset 0
+               OpMemberDecorate %24 0 Offset 0
+               OpMemberDecorate %24 1 Offset 16
+               OpMemberDecorate %25 0 Offset 0
+               OpMemberDecorate %25 1 Offset 32
+               OpMemberDecorate %26 0 Offset 0
+               OpMemberDecorate %26 1 Offset 16
+               OpDecorate %26 Block
+               OpDecorate %28 DescriptorSet 0
+               OpDecorate %28 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %2 %7
+         %50 = OpConstant %6 0
+         %13 = OpConstant %6 1
+         %15 = OpConstant %6 2
+         %17 = OpConstant %6 3
+         %20 = OpConstant %6 4
+         %23 = OpTypeStruct %6
+         %24 = OpTypeStruct %6 %23
+         %25 = OpTypeStruct %24 %6
+         %26 = OpTypeStruct %6 %25
+         %27 = OpTypePointer Uniform %26
+         %51 = OpTypePointer Uniform %6
+         %28 = OpVariable %27 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpVariable %7 Function
+         %21 = OpVariable %7 Function
+        %100 = OpAccessChain %51 %28 %50
+        %101 = OpLoad %6 %100
+               OpStore %12 %101
+         %14 = OpLoad %6 %12
+        %102 = OpAccessChain %51 %28 %13 %13
+        %103 = OpLoad %6 %102
+         %16 = OpIAdd %6 %14 %103
+               OpStore %12 %16
+         %18 = OpLoad %6 %12
+        %104 = OpAccessChain %51 %28 %13 %50 %50
+        %105 = OpLoad %6 %104
+         %19 = OpIAdd %6 %105 %18
+               OpStore %12 %19
+        %106 = OpAccessChain %51 %28 %13 %50 %13 %50
+        %107 = OpLoad %6 %106
+               OpStore %21 %107
+         %22 = OpFunctionCall %2 %10 %21
+               OpReturn
+               OpFunctionEnd
+         %10 = OpFunction %2 None %8
+          %9 = OpFunctionParameter %7
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after, context.get()));
+}
+
+TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) {
+  // This test came from the following GLSL:
+  //
+  // #version 450
+  //
+  // uniform blockname {
+  //   int x; // == 0
+  // };
+  //
+  // void main()
+  // {
+  //   int a;
+  //   a = 0;
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "blockname"
+               OpMemberName %10 0 "x"
+               OpName %12 ""
+               OpMemberDecorate %10 0 Offset 0
+               OpDecorate %10 Block
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %10 = OpTypeStruct %6
+         %11 = OpTypePointer Uniform %10
+         %12 = OpVariable %11 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  protobufs::UniformBufferElementDescriptor blockname_0 =
+      MakeUniformBufferElementDescriptor(0, 0, {0});
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 0, blockname_0));
+
+  // The constant id is 9 for 0.
+  protobufs::IdUseDescriptor use_of_9_in_store =
+      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+
+  // This transformation is not available because no uniform pointer to integer
+  // type is present:
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_9_in_store, blockname_0, 100, 101),
+      context.get(), fact_manager));
+}
+
+TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) {
+  // This test came from the following GLSL:
+  //
+  // #version 450
+  //
+  // uniform blockname {
+  //   int x; // == 0
+  //   int y; // == 9
+  // };
+  //
+  // void main()
+  // {
+  //   int a;
+  //   a = 9;
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "blockname"
+               OpMemberName %10 0 "x"
+               OpMemberName %10 1 "y"
+               OpName %12 ""
+               OpMemberDecorate %10 0 Offset 0
+               OpMemberDecorate %10 1 Offset 4
+               OpDecorate %10 Block
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 9
+         %10 = OpTypeStruct %6 %6
+         %11 = OpTypePointer Uniform %10
+         %50 = OpTypePointer Uniform %6
+         %12 = OpVariable %11 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  protobufs::UniformBufferElementDescriptor blockname_0 =
+      MakeUniformBufferElementDescriptor(0, 0, {0});
+  protobufs::UniformBufferElementDescriptor blockname_9 =
+      MakeUniformBufferElementDescriptor(0, 0, {1});
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 9, blockname_9));
+
+  // The constant id is 9 for 9.
+  protobufs::IdUseDescriptor use_of_9_in_store =
+      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+
+  // This transformation is not available because no constant is present for the
+  // index 1 required to index into the uniform buffer:
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_9_in_store, blockname_9, 100, 101),
+      context.get(), fact_manager));
+}
+
+TEST(TransformationReplaceConstantWithUniformTest,
+     NoIntTypePresentToEnableIndexing) {
+  // This test came from the following GLSL:
+  //
+  // #version 450
+  //
+  // uniform blockname {
+  //   float f; // == 9
+  // };
+  //
+  // void main()
+  // {
+  //   float a;
+  //   a = 3.0;
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "blockname"
+               OpMemberName %10 0 "f"
+               OpName %12 ""
+               OpMemberDecorate %10 0 Offset 0
+               OpDecorate %10 Block
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 3
+         %10 = OpTypeStruct %6
+         %11 = OpTypePointer Uniform %10
+         %12 = OpVariable %11 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  protobufs::UniformBufferElementDescriptor blockname_3 =
+      MakeUniformBufferElementDescriptor(0, 0, {0});
+
+  uint32_t float_data[1];
+  float temp = 3.0;
+  memcpy(&float_data[0], &temp, sizeof(float));
+  ASSERT_TRUE(
+      AddFactHelper(&fact_manager, context.get(), float_data[0], blockname_3));
+
+  // The constant id is 9 for 3.0.
+  protobufs::IdUseDescriptor use_of_9_in_store =
+      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+
+  // This transformation is not available because no integer type is present to
+  // allow a constant index to be expressed:
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_9_in_store, blockname_3, 100, 101),
+      context.get(), fact_manager));
+}
+
+TEST(TransformationReplaceConstantWithUniformTest,
+     UniformFactsDoNotMatchConstants) {
+  // This test came from the following GLSL:
+  //
+  // #version 450
+  //
+  // uniform blockname {
+  //   int x; // == 9
+  //   int y; // == 10
+  // };
+  //
+  // void main()
+  // {
+  //   int a;
+  //   int b;
+  //   a = 9;
+  //   b = 10;
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %8 "a"
+               OpName %10 "b"
+               OpName %12 "blockname"
+               OpMemberName %12 0 "x"
+               OpMemberName %12 1 "y"
+               OpName %14 ""
+               OpMemberDecorate %12 0 Offset 0
+               OpMemberDecorate %12 1 Offset 4
+               OpDecorate %12 Block
+               OpDecorate %14 DescriptorSet 0
+               OpDecorate %14 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 9
+         %11 = OpConstant %6 10
+         %50 = OpConstant %6 0
+         %51 = OpConstant %6 1
+         %12 = OpTypeStruct %6 %6
+         %13 = OpTypePointer Uniform %12
+         %52 = OpTypePointer Uniform %6
+         %14 = OpVariable %13 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  protobufs::UniformBufferElementDescriptor blockname_9 =
+      MakeUniformBufferElementDescriptor(0, 0, {0});
+  protobufs::UniformBufferElementDescriptor blockname_10 =
+      MakeUniformBufferElementDescriptor(0, 0, {1});
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 9, blockname_9));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 10, blockname_10));
+
+  // The constant ids for 9 and 10 are 9 and 11 respectively
+  protobufs::IdUseDescriptor use_of_9_in_store =
+      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 10, 0);
+  protobufs::IdUseDescriptor use_of_11_in_store =
+      transformation::MakeIdUseDescriptor(11, SpvOpStore, 1, 10, 1);
+
+  // These are right:
+  ASSERT_TRUE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_9_in_store, blockname_9, 100, 101),
+      context.get(), fact_manager));
+  ASSERT_TRUE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_11_in_store, blockname_10, 102, 103),
+      context.get(), fact_manager));
+
+  // These are wrong because the constants do not match the facts about
+  // uniforms.
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_11_in_store, blockname_9, 100, 101),
+      context.get(), fact_manager));
+  ASSERT_FALSE(transformation::IsApplicable(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          use_of_9_in_store, blockname_10, 102, 103),
+      context.get(), fact_manager));
+}
+
+TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) {
+  // The following GLSL was the basis for this test:
+
+  // #version 450
+  //
+  // struct T {
+  //   float a[5]; // [1.0, 1.5, 1.75, 1.875, 1.9375]
+  //   ivec4 b; // (1, 2, 3, 4)
+  //   vec3 c; // (2.0, 2.5, 2.75)
+  //   uint d; // 42u
+  //   bool e; // Not used in test
+  // };
+  //
+  // uniform block {
+  //   T f;
+  //   int g; // 22
+  //   uvec2 h; // (100u, 200u)
+  // };
+  //
+  // void main()
+  // {
+  //   T myT;
+  //
+  //   myT.a[0] = 1.9375;
+  //   myT.a[1] = 1.875;
+  //   myT.a[2] = 1.75;
+  //   myT.a[3] = 1.5;
+  //   myT.a[4] = 1.0;
+  //
+  //   myT.b.x = 4;
+  //   myT.b.y = 3;
+  //   myT.b.z = 2;
+  //   myT.b.w = 1;
+  //
+  //   myT.b.r = 22;
+  //
+  //   myT.c[0] = 2.75;
+  //   myT.c[0] = 2.5;
+  //   myT.c[0] = 2.0;
+  //
+  //   myT.d = 42u;
+  //   myT.d = 100u;
+  //   myT.d = 200u;
+  //
+  //   myT.e = true; // No attempt to replace 'true' by a uniform value
+  //
+  // }
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %14 "T"
+               OpMemberName %14 0 "a"
+               OpMemberName %14 1 "b"
+               OpMemberName %14 2 "c"
+               OpMemberName %14 3 "d"
+               OpMemberName %14 4 "e"
+               OpName %16 "myT"
+               OpName %61 "T"
+               OpMemberName %61 0 "a"
+               OpMemberName %61 1 "b"
+               OpMemberName %61 2 "c"
+               OpMemberName %61 3 "d"
+               OpMemberName %61 4 "e"
+               OpName %63 "block"
+               OpMemberName %63 0 "f"
+               OpMemberName %63 1 "g"
+               OpMemberName %63 2 "h"
+               OpName %65 ""
+               OpDecorate %60 ArrayStride 16
+               OpMemberDecorate %61 0 Offset 0
+               OpMemberDecorate %61 1 Offset 80
+               OpMemberDecorate %61 2 Offset 96
+               OpMemberDecorate %61 3 Offset 108
+               OpMemberDecorate %61 4 Offset 112
+               OpMemberDecorate %63 0 Offset 0
+               OpMemberDecorate %63 1 Offset 128
+               OpMemberDecorate %63 2 Offset 136
+               OpDecorate %63 Block
+               OpDecorate %65 DescriptorSet 0
+               OpDecorate %65 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 5
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypeInt 32 1
+         %11 = OpTypeVector %10 4
+         %12 = OpTypeVector %6 3
+         %13 = OpTypeBool
+         %14 = OpTypeStruct %9 %11 %12 %7 %13
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %10 0
+         %18 = OpConstant %6 1.9375
+         %19 = OpTypePointer Function %6
+         %21 = OpConstant %10 1
+         %22 = OpConstant %6 1.875
+         %24 = OpConstant %10 2
+         %25 = OpConstant %6 1.75
+         %27 = OpConstant %10 3
+         %28 = OpConstant %6 1.5
+         %30 = OpConstant %10 4
+         %31 = OpConstant %6 1
+         %33 = OpConstant %7 0
+         %34 = OpTypePointer Function %10
+         %36 = OpConstant %7 1
+         %38 = OpConstant %7 2
+         %40 = OpConstant %7 3
+         %42 = OpConstant %10 22
+         %44 = OpConstant %6 2.75
+         %46 = OpConstant %6 2.5
+         %48 = OpConstant %6 2
+         %50 = OpConstant %7 42
+         %51 = OpTypePointer Function %7
+         %53 = OpConstant %7 100
+         %55 = OpConstant %7 200
+         %57 = OpConstantTrue %13
+         %58 = OpTypePointer Function %13
+         %60 = OpTypeArray %6 %8
+         %61 = OpTypeStruct %60 %11 %12 %7 %7
+         %62 = OpTypeVector %7 2
+         %63 = OpTypeStruct %61 %10 %62
+         %64 = OpTypePointer Uniform %63
+        %100 = OpTypePointer Uniform %10
+        %101 = OpTypePointer Uniform %7
+        %102 = OpTypePointer Uniform %6
+        %103 = OpTypePointer Uniform %13
+         %65 = OpVariable %64 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %16 = OpVariable %15 Function
+         %20 = OpAccessChain %19 %16 %17 %17
+               OpStore %20 %18
+         %23 = OpAccessChain %19 %16 %17 %21
+               OpStore %23 %22
+         %26 = OpAccessChain %19 %16 %17 %24
+               OpStore %26 %25
+         %29 = OpAccessChain %19 %16 %17 %27
+               OpStore %29 %28
+         %32 = OpAccessChain %19 %16 %17 %30
+               OpStore %32 %31
+         %35 = OpAccessChain %34 %16 %21 %33
+               OpStore %35 %30
+         %37 = OpAccessChain %34 %16 %21 %36
+               OpStore %37 %27
+         %39 = OpAccessChain %34 %16 %21 %38
+               OpStore %39 %24
+         %41 = OpAccessChain %34 %16 %21 %40
+               OpStore %41 %21
+         %43 = OpAccessChain %34 %16 %21 %33
+               OpStore %43 %42
+         %45 = OpAccessChain %19 %16 %24 %33
+               OpStore %45 %44
+         %47 = OpAccessChain %19 %16 %24 %33
+               OpStore %47 %46
+         %49 = OpAccessChain %19 %16 %24 %33
+               OpStore %49 %48
+         %52 = OpAccessChain %51 %16 %27
+               OpStore %52 %50
+         %54 = OpAccessChain %51 %16 %27
+               OpStore %54 %53
+         %56 = OpAccessChain %51 %16 %27
+               OpStore %56 %55
+         %59 = OpAccessChain %58 %16 %30
+               OpStore %59 %57
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  const float float_array_values[5] = {1.0, 1.5, 1.75, 1.875, 1.9375};
+  uint32_t float_array_data[5];
+  memcpy(&float_array_data, &float_array_values, sizeof(float_array_values));
+
+  const float float_vector_values[3] = {2.0, 2.5, 2.75};
+  uint32_t float_vector_data[3];
+  memcpy(&float_vector_data, &float_vector_values, sizeof(float_vector_values));
+
+  protobufs::UniformBufferElementDescriptor uniform_f_a_0 =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 0, 0});
+  protobufs::UniformBufferElementDescriptor uniform_f_a_1 =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 0, 1});
+  protobufs::UniformBufferElementDescriptor uniform_f_a_2 =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 0, 2});
+  protobufs::UniformBufferElementDescriptor uniform_f_a_3 =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 0, 3});
+  protobufs::UniformBufferElementDescriptor uniform_f_a_4 =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 0, 4});
+
+  protobufs::UniformBufferElementDescriptor uniform_f_b_x =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 1, 0});
+  protobufs::UniformBufferElementDescriptor uniform_f_b_y =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 1, 1});
+  protobufs::UniformBufferElementDescriptor uniform_f_b_z =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 1, 2});
+  protobufs::UniformBufferElementDescriptor uniform_f_b_w =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 1, 3});
+
+  protobufs::UniformBufferElementDescriptor uniform_f_c_x =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 2, 0});
+  protobufs::UniformBufferElementDescriptor uniform_f_c_y =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 2, 1});
+  protobufs::UniformBufferElementDescriptor uniform_f_c_z =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 2, 2});
+
+  protobufs::UniformBufferElementDescriptor uniform_f_d =
+      MakeUniformBufferElementDescriptor(0, 0, {0, 3});
+
+  protobufs::UniformBufferElementDescriptor uniform_g =
+      MakeUniformBufferElementDescriptor(0, 0, {1});
+
+  protobufs::UniformBufferElementDescriptor uniform_h_x =
+      MakeUniformBufferElementDescriptor(0, 0, {2, 0});
+  protobufs::UniformBufferElementDescriptor uniform_h_y =
+      MakeUniformBufferElementDescriptor(0, 0, {2, 1});
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[0],
+                            uniform_f_a_0));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[1],
+                            uniform_f_a_1));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[2],
+                            uniform_f_a_2));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[3],
+                            uniform_f_a_3));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[4],
+                            uniform_f_a_4));
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, uniform_f_b_x));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, uniform_f_b_y));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, uniform_f_b_z));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 4, uniform_f_b_w));
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[0],
+                            uniform_f_c_x));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[1],
+                            uniform_f_c_y));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[2],
+                            uniform_f_c_z));
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 42, uniform_f_d));
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22, uniform_g));
+
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 100, uniform_h_x));
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200, uniform_h_y));
+
+  std::vector<protobufs::TransformationReplaceConstantWithUniform>
+      transformations;
+
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(18, SpvOpStore, 1, 20, 0),
+          uniform_f_a_4, 200, 201));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0),
+          uniform_f_a_3, 202, 203));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(25, SpvOpStore, 1, 26, 0),
+          uniform_f_a_2, 204, 205));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 29, 0),
+          uniform_f_a_1, 206, 207));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(31, SpvOpStore, 1, 32, 0),
+          uniform_f_a_0, 208, 209));
+
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(30, SpvOpStore, 1, 35, 0),
+          uniform_f_b_w, 210, 211));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(27, SpvOpStore, 1, 37, 0),
+          uniform_f_b_z, 212, 213));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(24, SpvOpStore, 1, 39, 0),
+          uniform_f_b_y, 214, 215));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(21, SpvOpStore, 1, 41, 0),
+          uniform_f_b_x, 216, 217));
+
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(44, SpvOpStore, 1, 45, 0),
+          uniform_f_c_z, 220, 221));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(46, SpvOpStore, 1, 47, 0),
+          uniform_f_c_y, 222, 223));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(48, SpvOpStore, 1, 49, 0),
+          uniform_f_c_x, 224, 225));
+
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(50, SpvOpStore, 1, 52, 0),
+          uniform_f_d, 226, 227));
+
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(53, SpvOpStore, 1, 54, 0),
+          uniform_h_x, 228, 229));
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(55, SpvOpStore, 1, 56, 0),
+          uniform_h_y, 230, 231));
+
+  transformations.emplace_back(
+      transformation::MakeTransformationReplaceConstantWithUniform(
+          transformation::MakeIdUseDescriptor(42, SpvOpStore, 1, 43, 0),
+          uniform_g, 218, 219));
+
+  for (auto& transformation : transformations) {
+    ASSERT_TRUE(transformation::IsApplicable(transformation, context.get(),
+                                             fact_manager));
+    transformation::Apply(transformation, context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+               OpName %14 "T"
+               OpMemberName %14 0 "a"
+               OpMemberName %14 1 "b"
+               OpMemberName %14 2 "c"
+               OpMemberName %14 3 "d"
+               OpMemberName %14 4 "e"
+               OpName %16 "myT"
+               OpName %61 "T"
+               OpMemberName %61 0 "a"
+               OpMemberName %61 1 "b"
+               OpMemberName %61 2 "c"
+               OpMemberName %61 3 "d"
+               OpMemberName %61 4 "e"
+               OpName %63 "block"
+               OpMemberName %63 0 "f"
+               OpMemberName %63 1 "g"
+               OpMemberName %63 2 "h"
+               OpName %65 ""
+               OpDecorate %60 ArrayStride 16
+               OpMemberDecorate %61 0 Offset 0
+               OpMemberDecorate %61 1 Offset 80
+               OpMemberDecorate %61 2 Offset 96
+               OpMemberDecorate %61 3 Offset 108
+               OpMemberDecorate %61 4 Offset 112
+               OpMemberDecorate %63 0 Offset 0
+               OpMemberDecorate %63 1 Offset 128
+               OpMemberDecorate %63 2 Offset 136
+               OpDecorate %63 Block
+               OpDecorate %65 DescriptorSet 0
+               OpDecorate %65 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 5
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypeInt 32 1
+         %11 = OpTypeVector %10 4
+         %12 = OpTypeVector %6 3
+         %13 = OpTypeBool
+         %14 = OpTypeStruct %9 %11 %12 %7 %13
+         %15 = OpTypePointer Function %14
+         %17 = OpConstant %10 0
+         %18 = OpConstant %6 1.9375
+         %19 = OpTypePointer Function %6
+         %21 = OpConstant %10 1
+         %22 = OpConstant %6 1.875
+         %24 = OpConstant %10 2
+         %25 = OpConstant %6 1.75
+         %27 = OpConstant %10 3
+         %28 = OpConstant %6 1.5
+         %30 = OpConstant %10 4
+         %31 = OpConstant %6 1
+         %33 = OpConstant %7 0
+         %34 = OpTypePointer Function %10
+         %36 = OpConstant %7 1
+         %38 = OpConstant %7 2
+         %40 = OpConstant %7 3
+         %42 = OpConstant %10 22
+         %44 = OpConstant %6 2.75
+         %46 = OpConstant %6 2.5
+         %48 = OpConstant %6 2
+         %50 = OpConstant %7 42
+         %51 = OpTypePointer Function %7
+         %53 = OpConstant %7 100
+         %55 = OpConstant %7 200
+         %57 = OpConstantTrue %13
+         %58 = OpTypePointer Function %13
+         %60 = OpTypeArray %6 %8
+         %61 = OpTypeStruct %60 %11 %12 %7 %7
+         %62 = OpTypeVector %7 2
+         %63 = OpTypeStruct %61 %10 %62
+         %64 = OpTypePointer Uniform %63
+        %100 = OpTypePointer Uniform %10
+        %101 = OpTypePointer Uniform %7
+        %102 = OpTypePointer Uniform %6
+        %103 = OpTypePointer Uniform %13
+         %65 = OpVariable %64 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %16 = OpVariable %15 Function
+         %20 = OpAccessChain %19 %16 %17 %17
+        %200 = OpAccessChain %102 %65 %17 %17 %30
+        %201 = OpLoad %6 %200
+               OpStore %20 %201
+         %23 = OpAccessChain %19 %16 %17 %21
+        %202 = OpAccessChain %102 %65 %17 %17 %27
+        %203 = OpLoad %6 %202
+               OpStore %23 %203
+         %26 = OpAccessChain %19 %16 %17 %24
+        %204 = OpAccessChain %102 %65 %17 %17 %24
+        %205 = OpLoad %6 %204
+               OpStore %26 %205
+         %29 = OpAccessChain %19 %16 %17 %27
+        %206 = OpAccessChain %102 %65 %17 %17 %21
+        %207 = OpLoad %6 %206
+               OpStore %29 %207
+         %32 = OpAccessChain %19 %16 %17 %30
+        %208 = OpAccessChain %102 %65 %17 %17 %17
+        %209 = OpLoad %6 %208
+               OpStore %32 %209
+         %35 = OpAccessChain %34 %16 %21 %33
+        %210 = OpAccessChain %100 %65 %17 %21 %27
+        %211 = OpLoad %10 %210
+               OpStore %35 %211
+         %37 = OpAccessChain %34 %16 %21 %36
+        %212 = OpAccessChain %100 %65 %17 %21 %24
+        %213 = OpLoad %10 %212
+               OpStore %37 %213
+         %39 = OpAccessChain %34 %16 %21 %38
+        %214 = OpAccessChain %100 %65 %17 %21 %21
+        %215 = OpLoad %10 %214
+               OpStore %39 %215
+         %41 = OpAccessChain %34 %16 %21 %40
+        %216 = OpAccessChain %100 %65 %17 %21 %17
+        %217 = OpLoad %10 %216
+               OpStore %41 %217
+         %43 = OpAccessChain %34 %16 %21 %33
+        %218 = OpAccessChain %100 %65 %21
+        %219 = OpLoad %10 %218
+               OpStore %43 %219
+         %45 = OpAccessChain %19 %16 %24 %33
+        %220 = OpAccessChain %102 %65 %17 %24 %24
+        %221 = OpLoad %6 %220
+               OpStore %45 %221
+         %47 = OpAccessChain %19 %16 %24 %33
+        %222 = OpAccessChain %102 %65 %17 %24 %21
+        %223 = OpLoad %6 %222
+               OpStore %47 %223
+         %49 = OpAccessChain %19 %16 %24 %33
+        %224 = OpAccessChain %102 %65 %17 %24 %17
+        %225 = OpLoad %6 %224
+               OpStore %49 %225
+         %52 = OpAccessChain %51 %16 %27
+        %226 = OpAccessChain %101 %65 %17 %27
+        %227 = OpLoad %7 %226
+               OpStore %52 %227
+         %54 = OpAccessChain %51 %16 %27
+        %228 = OpAccessChain %101 %65 %24 %17
+        %229 = OpLoad %7 %228
+               OpStore %54 %229
+         %56 = OpAccessChain %51 %16 %27
+        %230 = OpAccessChain %101 %65 %24 %21
+        %231 = OpLoad %7 %230
+               OpStore %56 %231
+         %59 = OpAccessChain %58 %16 %30
+               OpStore %59 %57
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools

+ 84 - 0
3rdparty/spirv-tools/test/fuzz/uniform_buffer_element_descriptor_test.cpp

@@ -0,0 +1,84 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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/fuzz/uniform_buffer_element_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(UniformBufferElementDescriptorTest, TestEquality) {
+  // Test that equality works as expected for various buffer element
+  // descriptors.
+
+  protobufs::UniformBufferElementDescriptor descriptor1 =
+      MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3});
+  protobufs::UniformBufferElementDescriptor descriptor2 =
+      MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3});
+  protobufs::UniformBufferElementDescriptor descriptor3 =
+      MakeUniformBufferElementDescriptor(0, 1, {1, 2, 3});
+  protobufs::UniformBufferElementDescriptor descriptor4 =
+      MakeUniformBufferElementDescriptor(1, 0, {1, 2, 3});
+  protobufs::UniformBufferElementDescriptor descriptor5 =
+      MakeUniformBufferElementDescriptor(1, 1, {1, 2, 3});
+  protobufs::UniformBufferElementDescriptor descriptor6 =
+      MakeUniformBufferElementDescriptor(0, 0, {1, 2, 4});
+  protobufs::UniformBufferElementDescriptor descriptor7 =
+      MakeUniformBufferElementDescriptor(0, 0, {1, 2});
+
+  ASSERT_TRUE(
+      UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor1));
+  ASSERT_TRUE(
+      UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor2));
+  ASSERT_TRUE(
+      UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor3));
+  ASSERT_TRUE(
+      UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor4));
+  ASSERT_TRUE(
+      UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor5));
+  ASSERT_TRUE(
+      UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor6));
+  ASSERT_TRUE(
+      UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor7));
+
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor3));
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor1));
+
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor4));
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor1));
+
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor5));
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor1));
+
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor6));
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor1));
+
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor7));
+  ASSERT_FALSE(
+      UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor1));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools

+ 199 - 0
3rdparty/spirv-tools/test/opt/upgrade_memory_model_test.cpp

@@ -2037,4 +2037,203 @@ OpFunctionEnd
   SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
   SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
 }
 }
 
 
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoad) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768
+; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%relaxed = OpConstant %int 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicLoad %int %ssbo_var %device %relaxed
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadPreviousFlags) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834
+; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%acquire_ssbo = OpConstant %int 66
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicLoad %int %ssbo_var %device %acquire_ssbo
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicStore) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32768
+; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%device = OpConstant %int 1
+%relaxed = OpConstant %int 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpAtomicStore %ssbo_var %device %relaxed %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicStorePreviousFlags) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32836
+; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%device = OpConstant %int 1
+%release_ssbo = OpConstant %int 68
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpAtomicStore %ssbo_var %device %release_ssbo %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchange) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768
+; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile]] [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%device = OpConstant %int 1
+%relaxed = OpConstant %int 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicCompareExchange %int %ssbo_var %device %relaxed %relaxed %int_0 %int_1
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchangePreviousFlags) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile_acq_rel:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32840
+; CHECK: [[volatile_acq:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834
+; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile_acq_rel]] [[volatile_acq]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%device = OpConstant %int 1
+%acq_ssbo = OpConstant %int 66
+%acq_rel_ssbo = OpConstant %int 72
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicCompareExchange %int %ssbo_var %device %acq_rel_ssbo %acq_ssbo %int_0 %int_1
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadMemberDecoration) {
+  const std::string text = R"(
+; CHECK-NOT: OpMemberDecorate {{.*}} {{.*}} Volatile
+; CHECK: [[relaxed:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 0
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768
+; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[relaxed]]
+; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpMemberDecorate %struct 1 Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%relaxed = OpConstant %int 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%struct = OpTypeStruct %int %int
+%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
+%ssbo_var = OpVariable %ptr_ssbo_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep0 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_0
+%ld0 = OpAtomicLoad %int %gep0 %device %relaxed
+%gep1 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_1
+%ld1 = OpAtomicLoad %int %gep1 %device %relaxed
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
 }  // namespace
 }  // namespace

+ 147 - 0
3rdparty/spirv-tools/test/val/val_atomics_test.cpp

@@ -1998,6 +1998,153 @@ TEST_F(ValidateAtomics, CompareExchangeWeakV14Bad) {
           "AtomicCompareExchangeWeak requires SPIR-V version 1.3 or earlier"));
           "AtomicCompareExchangeWeak requires SPIR-V version 1.3 or earlier"));
 }
 }
 
 
+TEST_F(ValidateAtomics, CompareExchangeVolatileMatch) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VulkanMemoryModelKHR
+OpCapability Linkage
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical VulkanKHR
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%workgroup = OpConstant %int 2
+%volatile = OpConstant %int 32768
+%ptr_wg_int = OpTypePointer Workgroup %int
+%wg_var = OpVariable %ptr_wg_int Workgroup
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %volatile %int_0 %int_1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateAtomics, CompareExchangeVolatileMismatch) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VulkanMemoryModelKHR
+OpCapability Linkage
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical VulkanKHR
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%workgroup = OpConstant %int 2
+%volatile = OpConstant %int 32768
+%non_volatile = OpConstant %int 0
+%ptr_wg_int = OpTypePointer Workgroup %int
+%wg_var = OpVariable %ptr_wg_int Workgroup
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %non_volatile %volatile %int_0 %int_1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Volatile mask setting must match for Equal and "
+                        "Unequal memory semantics"));
+}
+
+TEST_F(ValidateAtomics, CompareExchangeVolatileMismatchCooperativeMatrix) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VulkanMemoryModelKHR
+OpCapability Linkage
+OpCapability CooperativeMatrixNV
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_NV_cooperative_matrix"
+OpMemoryModel Logical VulkanKHR
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%workgroup = OpConstant %int 2
+%volatile = OpSpecConstant %int 32768
+%non_volatile = OpSpecConstant %int 32768
+%ptr_wg_int = OpTypePointer Workgroup %int
+%wg_var = OpVariable %ptr_wg_int Workgroup
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %non_volatile %int_0 %int_1
+OpReturn
+OpFunctionEnd
+)";
+
+  // This is ok because we cannot evaluate the spec constant defaults.
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateAtomics, VolatileRequiresVulkanMemoryModel) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%workgroup = OpConstant %int 2
+%volatile = OpConstant %int 32768
+%ptr_wg_int = OpTypePointer Workgroup %int
+%wg_var = OpVariable %ptr_wg_int Workgroup
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicLoad %int %wg_var %workgroup %volatile
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory Semantics Volatile requires capability "
+                        "VulkanMemoryModelKHR"));
+}
+
+TEST_F(ValidateAtomics, CooperativeMatrixSemanticsMustBeConstant) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability CooperativeMatrixNV
+OpExtension "SPV_NV_cooperative_matrix"
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%workgroup = OpConstant %int 2
+%undef = OpUndef %int
+%ptr_wg_int = OpTypePointer Workgroup %int
+%wg_var = OpVariable %ptr_wg_int Workgroup
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicLoad %int %wg_var %workgroup %undef
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory Semantics must be a constant instruction when "
+                        "CooperativeMatrixNV capability is present"));
+}
+
 }  // namespace
 }  // namespace
 }  // namespace val
 }  // namespace val
 }  // namespace spvtools
 }  // namespace spvtools

+ 109 - 0
3rdparty/spirv-tools/test/val/val_barriers_test.cpp

@@ -1361,6 +1361,115 @@ OpFunctionEnd
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 }
 
 
+TEST_F(ValidateBarriers, VolatileMemoryBarrier) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability VulkanMemoryModelKHR
+OpCapability VulkanMemoryModelDeviceScopeKHR
+OpCapability Linkage
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical VulkanKHR
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%semantics = OpConstant %int 32768
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%1 = OpLabel
+OpMemoryBarrier %device %semantics
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory Semantics Volatile can only be used with "
+                        "atomic instructions"));
+}
+
+TEST_F(ValidateBarriers, VolatileControlBarrier) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability VulkanMemoryModelKHR
+OpCapability VulkanMemoryModelDeviceScopeKHR
+OpCapability Linkage
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical VulkanKHR
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%semantics = OpConstant %int 32768
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%1 = OpLabel
+OpControlBarrier %device %device %semantics
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory Semantics Volatile can only be used with "
+                        "atomic instructions"));
+}
+
+TEST_F(ValidateBarriers, CooperativeMatrixSpecConstantVolatile) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability VulkanMemoryModelKHR
+OpCapability VulkanMemoryModelDeviceScopeKHR
+OpCapability CooperativeMatrixNV
+OpCapability Linkage
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_NV_cooperative_matrix"
+OpMemoryModel Logical VulkanKHR
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%semantics = OpSpecConstant %int 32768
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%1 = OpLabel
+OpControlBarrier %device %device %semantics
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateBarriers, CooperativeMatrixNonConstantSemantics) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability VulkanMemoryModelKHR
+OpCapability VulkanMemoryModelDeviceScopeKHR
+OpCapability CooperativeMatrixNV
+OpCapability Linkage
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_NV_cooperative_matrix"
+OpMemoryModel Logical VulkanKHR
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%semantics = OpUndef %int
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%1 = OpLabel
+OpControlBarrier %device %device %semantics
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory Semantics must be a constant instruction when "
+                        "CooperativeMatrixNV capability is present"));
+}
+
 }  // namespace
 }  // namespace
 }  // namespace val
 }  // namespace val
 }  // namespace spvtools
 }  // namespace spvtools

+ 4 - 2
3rdparty/spirv-tools/test/val/val_capability_test.cpp

@@ -1168,8 +1168,10 @@ std::make_pair(std::string(kOpenCLMemoryModel) +
           ShaderDependencies()),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n"
           "OpEntryPoint Kernel %func \"compute\" \n"
-          "OpDecorate %intt Component 0\n"
-          "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+          "OpDecorate %var Component 0\n"
+          "%intt = OpTypeInt 32 0\n"
+          "%ptr = OpTypePointer Input %intt\n"
+          "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
           ShaderDependencies()),
           ShaderDependencies()),
 std::make_pair(std::string(kOpenCLMemoryModel) +
 std::make_pair(std::string(kOpenCLMemoryModel) +
           "OpEntryPoint Kernel %func \"compute\" \n"
           "OpEntryPoint Kernel %func \"compute\" \n"

+ 308 - 0
3rdparty/spirv-tools/test/val/val_decoration_test.cpp

@@ -6400,6 +6400,314 @@ OpDecorate %1 BufferBlock
                         "requires SPIR-V version 1.3 or earlier"));
                         "requires SPIR-V version 1.3 or earlier"));
 }
 }
 
 
+// Component
+
+TEST_F(ValidateDecorations, ComponentDecorationBadTarget) {
+  std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpDecorate %t Component 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%t = OpTypeVector %float 2
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Target of Component decoration must be "
+                        "a memory object declaration"));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) {
+  std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpDecorate %v Component 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%t = OpTypeVector %float 2
+%ptr_private = OpTypePointer Private %t
+%v = OpVariable %ptr_private Private
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Target of Component decoration is invalid: must "
+                        "point to a Storage Class of Input(1) or Output(3)"));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationBadTypeVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability Matrix
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpDecorate %v Component 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%vtype = OpTypeVector %float 4
+%t = OpTypeMatrix %vtype 4
+%ptr_input = OpTypePointer Input %t
+%v = OpVariable %ptr_input Input
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Component decoration specified for type"));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a scalar or vector"));
+}
+
+std::string ShaderWithComponentDecoration(const std::string& type,
+                                          const std::string& decoration) {
+  return R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %entryPointOutput
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %entryPointOutput Location 0
+OpDecorate %entryPointOutput )" +
+         decoration + R"(
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%vtype = )" + type + R"(
+%float_0 = OpConstant %float 0
+%_ptr_Output_vtype = OpTypePointer Output %vtype
+%entryPointOutput = OpVariable %_ptr_Output_vtype Output
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationIntGood0Vulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv =
+      ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 0");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationIntGood1Vulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv =
+      ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 1");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationIntGood2Vulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv =
+      ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 2");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationIntGood3Vulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv =
+      ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 3");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationIntBad4Vulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv =
+      ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 4");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Sequence of components starting with 4 "
+                        "and ending with 4 gets larger than 3"));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationVector3GoodVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv =
+      ShaderWithComponentDecoration("OpTypeVector %float 3", "Component 1");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationVector4GoodVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv =
+      ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 0");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationVector4Bad1Vulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv =
+      ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 1");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Sequence of components starting with 1 "
+                        "and ending with 4 gets larger than 3"));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationVector4Bad3Vulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv =
+      ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 3");
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Sequence of components starting with 3 "
+                        "and ending with 6 gets larger than 3"));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationBlockGood) {
+  std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %9 %12
+OpExecutionMode %4 OriginUpperLeft
+OpDecorate %9 Location 0
+OpMemberDecorate %block 0 Location 2
+OpMemberDecorate %block 0 Component 1
+OpDecorate %block Block
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%float = OpTypeFloat 32
+%vec3 = OpTypeVector %float 3
+%8 = OpTypePointer Output %vec3
+%9 = OpVariable %8 Output
+%block = OpTypeStruct %vec3
+%11 = OpTypePointer Input %block
+%12 = OpVariable %11 Input
+%int = OpTypeInt 32 1
+%14 = OpConstant %int 0
+%15 = OpTypePointer Input %vec3
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%16 = OpAccessChain %15 %12 %14
+%17 = OpLoad %vec3 %16
+OpStore %9 %17
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationBlockBadVulkan) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %9 %12
+OpExecutionMode %4 OriginUpperLeft
+OpDecorate %9 Location 0
+OpMemberDecorate %block 0 Location 2
+OpMemberDecorate %block 0 Component 2
+OpDecorate %block Block
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%float = OpTypeFloat 32
+%vec3 = OpTypeVector %float 3
+%8 = OpTypePointer Output %vec3
+%9 = OpVariable %8 Output
+%block = OpTypeStruct %vec3
+%11 = OpTypePointer Input %block
+%12 = OpVariable %11 Input
+%int = OpTypeInt 32 1
+%14 = OpConstant %int 0
+%15 = OpTypePointer Input %vec3
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%16 = OpAccessChain %15 %12 %14
+%17 = OpLoad %vec3 %16
+OpStore %9 %17
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Sequence of components starting with 2 "
+                        "and ending with 4 gets larger than 3"));
+}
+
+TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) {
+  std::string spirv = R"(
+              OpCapability Shader
+              OpMemoryModel Logical GLSL450
+              OpEntryPoint Vertex %main "main"
+
+              OpDecorate %param_f Component 0
+
+      %void = OpTypeVoid
+   %void_fn = OpTypeFunction %void
+     %float = OpTypeFloat 32
+   %float_0 = OpConstant %float 0
+   %int     = OpTypeInt 32 0
+   %int_2   = OpConstant %int 2
+  %struct_b = OpTypeStruct %float
+
+%extra_fn = OpTypeFunction %void %float
+
+  %helper = OpFunction %void None %extra_fn
+ %param_f = OpFunctionParameter %float
+%helper_label = OpLabel
+            OpReturn
+            OpFunctionEnd
+
+    %main = OpFunction %void None %void_fn
+   %label = OpLabel
+            OpReturn
+            OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
 }  // namespace
 }  // namespace
 }  // namespace val
 }  // namespace val
 }  // namespace spvtools
 }  // namespace spvtools

+ 266 - 0
3rdparty/spirv-tools/test/val/val_memory_test.cpp

@@ -3566,6 +3566,272 @@ OpMemoryModel Logical GLSL450
                         "the Result Type"));
                         "the Result Type"));
 }
 }
 
 
+TEST_F(ValidateMemory, StoreToUniformBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int4 = OpTypeVector %int 4
+%struct = OpTypeStruct %int4
+%ptr_uniform_struct = OpTypePointer Uniform %struct
+%ptr_uniform_int4 = OpTypePointer Uniform %int4
+%ptr_uniform_int = OpTypePointer Uniform %int
+%var = OpVariable %ptr_uniform_struct Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0
+%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0
+OpStore %gep2 %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMemory, StoreToUniformBlockVulkan) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int4 = OpTypeVector %int 4
+%struct = OpTypeStruct %int4
+%ptr_uniform_struct = OpTypePointer Uniform %struct
+%ptr_uniform_int4 = OpTypePointer Uniform %int4
+%ptr_uniform_int = OpTypePointer Uniform %int
+%var = OpVariable %ptr_uniform_struct Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0
+%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0
+OpStore %gep2 %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
+}
+
+// This test requires that the struct is not id 2.
+TEST_F(ValidateMemory, StoreToUniformBlockVulkan2) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %gid_var
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %3 Block
+OpMemberDecorate %3 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %gid_var BuiltIn GlobalInvocationId
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int3 = OpTypeVector %int 3
+%int4 = OpTypeVector %int 4
+%3 = OpTypeStruct %int4
+%ptr_uniform_struct = OpTypePointer Uniform %3
+%ptr_uniform_int4 = OpTypePointer Uniform %int4
+%ptr_uniform_int = OpTypePointer Uniform %int
+%var = OpVariable %ptr_uniform_struct Uniform
+%ptr_input_int3 = OpTypePointer Input %int3
+%gid_var = OpVariable %ptr_input_int3 Input
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0
+%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0
+OpStore %gep2 %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
+}
+
+TEST_F(ValidateMemory, StoreToUniformBufferBlockVulkan) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct BufferBlock
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int4 = OpTypeVector %int 4
+%struct = OpTypeStruct %int4
+%ptr_uniform_struct = OpTypePointer Uniform %struct
+%ptr_uniform_int4 = OpTypePointer Uniform %int4
+%ptr_uniform_int = OpTypePointer Uniform %int
+%var = OpVariable %ptr_uniform_struct Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0
+%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0
+OpStore %gep2 %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
+TEST_F(ValidateMemory, StoreToUniformBlockVulkanArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int4 = OpTypeVector %int 4
+%struct = OpTypeStruct %int4
+%array_struct = OpTypeArray %struct %int_1
+%ptr_uniform_array = OpTypePointer Uniform %array_struct
+%ptr_uniform_struct = OpTypePointer Uniform %struct
+%ptr_uniform_int4 = OpTypePointer Uniform %int4
+%ptr_uniform_int = OpTypePointer Uniform %int
+%var = OpVariable %ptr_uniform_array Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep1 = OpAccessChain %ptr_uniform_int %var %int_0 %int_0 %int_0
+%gep2 = OpCopyObject %ptr_uniform_int %gep1
+OpStore %gep2 %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
+}
+
+// This test requires that the struct is not id 2.
+TEST_F(ValidateMemory, StoreToUniformBlockVulkanArray2) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %gid_var
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %gid_var BuiltIn GlobalInvocationId
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int3 = OpTypeVector %int 3
+%int4 = OpTypeVector %int 4
+%struct = OpTypeStruct %int4
+%array_struct = OpTypeArray %struct %int_1
+%ptr_uniform_array = OpTypePointer Uniform %array_struct
+%ptr_uniform_struct = OpTypePointer Uniform %struct
+%ptr_uniform_int4 = OpTypePointer Uniform %int4
+%ptr_uniform_int = OpTypePointer Uniform %int
+%var = OpVariable %ptr_uniform_array Uniform
+%ptr_input_int3 = OpTypePointer Input %int3
+%gid_var = OpVariable %ptr_input_int3 Input
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep1 = OpAccessChain %ptr_uniform_int %var %int_0 %int_0 %int_0
+%gep2 = OpCopyObject %ptr_uniform_int %gep1
+OpStore %gep2 %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
+}
+
+TEST_F(ValidateMemory, StoreToUniformBlockVulkanRuntimeArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability RuntimeDescriptorArrayEXT
+OpExtension "SPV_EXT_descriptor_indexing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int4 = OpTypeVector %int 4
+%struct = OpTypeStruct %int4
+%array_struct = OpTypeRuntimeArray %struct
+%ptr_uniform_array = OpTypePointer Uniform %array_struct
+%ptr_uniform_struct = OpTypePointer Uniform %struct
+%ptr_uniform_int4 = OpTypePointer Uniform %int4
+%ptr_uniform_int = OpTypePointer Uniform %int
+%var = OpVariable %ptr_uniform_array Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 %int_0
+%gep2 = OpInBoundsAccessChain %ptr_uniform_int %gep1 %int_0
+OpStore %gep2 %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
+}
+
 }  // namespace
 }  // namespace
 }  // namespace val
 }  // namespace val
 }  // namespace spvtools
 }  // namespace spvtools

+ 4 - 4
3rdparty/spirv-tools/tools/as/as.cpp

@@ -21,6 +21,7 @@
 #include "tools/io.h"
 #include "tools/io.h"
 
 
 void print_usage(char* argv0) {
 void print_usage(char* argv0) {
+  std::string target_env_list = spvTargetEnvList(19, 80);
   printf(
   printf(
       R"(%s - Create a SPIR-V binary module from SPIR-V assembly text
       R"(%s - Create a SPIR-V binary module from SPIR-V assembly text
 
 
@@ -41,11 +42,10 @@ Options:
                   Numeric IDs in the binary will have the same values as in the
                   Numeric IDs in the binary will have the same values as in the
                   source. Non-numeric IDs are allocated by filling in the gaps,
                   source. Non-numeric IDs are allocated by filling in the gaps,
                   starting with 1 and going up.
                   starting with 1 and going up.
-  --target-env {vulkan1.0|vulkan1.1|spv1.0|spv1.1|spv1.2|spv1.3|spv1.4}
-                  Use Vulkan 1.0, Vulkan 1.1, SPIR-V 1.0, SPIR-V 1.1,
-                  SPIR-V 1.2, SPIR-V 1.3, or SPIR-V 1.4
+  --target-env    {%s}
+                  Use specified environment.
 )",
 )",
-      argv0, argv0);
+      argv0, argv0, target_env_list.c_str());
 }
 }
 
 
 static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
 static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;

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

@@ -213,7 +213,7 @@ bool Replay(const spv_target_env& target_env,
 
 
 bool Fuzz(const spv_target_env& target_env,
 bool Fuzz(const spv_target_env& target_env,
           const spvtools::FuzzerOptions& fuzzer_options,
           const spvtools::FuzzerOptions& fuzzer_options,
-          const std::vector<uint>& binary_in,
+          const std::vector<uint32_t>& binary_in,
           const spvtools::fuzz::protobufs::FactSequence& initial_facts,
           const spvtools::fuzz::protobufs::FactSequence& initial_facts,
           std::vector<uint32_t>* binary_out,
           std::vector<uint32_t>* binary_out,
           spvtools::fuzz::protobufs::TransformationSequence*
           spvtools::fuzz::protobufs::TransformationSequence*

+ 4 - 4
3rdparty/spirv-tools/tools/link/linker.cpp

@@ -23,6 +23,7 @@
 #include "tools/io.h"
 #include "tools/io.h"
 
 
 void print_usage(char* argv0) {
 void print_usage(char* argv0) {
+  std::string target_env_list = spvTargetEnvList(27, 95);
   printf(
   printf(
       R"(%s - Link SPIR-V binary files together.
       R"(%s - Link SPIR-V binary files together.
 
 
@@ -39,11 +40,10 @@ Options:
   --allow-partial-linkage Allow partial linkage by accepting imported symbols to be unresolved.
   --allow-partial-linkage Allow partial linkage by accepting imported symbols to be unresolved.
   --verify-ids            Verify that IDs in the resulting modules are truly unique.
   --verify-ids            Verify that IDs in the resulting modules are truly unique.
   --version               Display linker version information
   --version               Display linker version information
-  --target-env            {vulkan1.0|vulkan1.1|spv1.0|spv1.1|spv1.2|spv1.3|spv1.4|opencl2.1|opencl2.2}
-                          Use Vulkan 1.0, Vulkan 1.1, SPIR-V 1.0, SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3,
-                          SPIR-V1.4, OpenCL 2.1, OpenCL 2.2 validation rules.
+  --target-env            {%s}
+                          Use validation rules from the specified environment.
 )",
 )",
-      argv0, argv0);
+      argv0, argv0, target_env_list.c_str());
 }
 }
 
 
 int main(int argc, char** argv) {
 int main(int argc, char** argv) {

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

@@ -92,6 +92,7 @@ std::string GetWebGPUToVulkanPasses() {
 }
 }
 
 
 void PrintUsage(const char* program) {
 void PrintUsage(const char* program) {
+  std::string target_env_list = spvTargetEnvList(16, 80);
   // NOTE: Please maintain flags in lexicographical order.
   // NOTE: Please maintain flags in lexicographical order.
   printf(
   printf(
       R"(%s - Optimize a SPIR-V binary file.
       R"(%s - Optimize a SPIR-V binary file.
@@ -436,9 +437,9 @@ Options (in lexicographical order):)",
   printf(R"(
   printf(R"(
   --target-env=<env>
   --target-env=<env>
                Set the target environment. Without this flag the target
                Set the target environment. Without this flag the target
-               enviroment defaults to spv1.3.
-               <env> must be one of vulkan1.0, vulkan1.1, opencl2.2, spv1.0,
-               spv1.1, spv1.2, spv1.3, or webgpu0.)");
+               enviroment defaults to spv1.3. <env> must be one of
+               {%s})",
+         target_env_list.c_str());
   printf(R"(
   printf(R"(
   --time-report
   --time-report
                Print the resource utilization of each pass (e.g., CPU time,
                Print the resource utilization of each pass (e.g., CPU time,

+ 4 - 6
3rdparty/spirv-tools/tools/val/val.cpp

@@ -25,6 +25,7 @@
 #include "tools/util/cli_consumer.h"
 #include "tools/util/cli_consumer.h"
 
 
 void print_usage(char* argv0) {
 void print_usage(char* argv0) {
+  std::string target_env_list = spvTargetEnvList(36, 105);
   printf(
   printf(
       R"(%s - Validate a SPIR-V binary file.
       R"(%s - Validate a SPIR-V binary file.
 
 
@@ -65,13 +66,10 @@ Options:
   --before-hlsl-legalization       Allows code patterns that are intended to be
   --before-hlsl-legalization       Allows code patterns that are intended to be
                                    fixed by spirv-opt's legalization passes.
                                    fixed by spirv-opt's legalization passes.
   --version                        Display validator version information.
   --version                        Display validator version information.
-  --target-env                     {vulkan1.0|vulkan1.1|vulkan1.1spv1.4|opencl2.2|spv1.0|spv1.1|
-                                    spv1.2|spv1.3|spv1.4|webgpu0}
-                                   Use Vulkan 1.0, Vulkan 1.1, Vulkan 1.1 with SPIR-V 1.4,
-                                   OpenCL 2.2, SPIR-V 1.0, SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3,
-                                   SPIR-V 1.4, or WIP WebGPU validation rules.
+  --target-env                     {%s}
+                                   Use validation rules from the specified environment.
 )",
 )",
-      argv0, argv0);
+      argv0, argv0, target_env_list.c_str());
 }
 }
 
 
 int main(int argc, char** argv) {
 int main(int argc, char** argv) {

+ 282 - 0
3rdparty/spirv-tools/utils/git-sync-deps

@@ -0,0 +1,282 @@
+#!/usr/bin/env python
+# Copyright 2014 Google Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#    * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#    * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#    * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Parse a DEPS file and git checkout all of the dependencies.
+
+Args:
+  An optional list of deps_os values.
+
+Environment Variables:
+  GIT_EXECUTABLE: path to "git" binary; if unset, will look for one of
+  ['git', 'git.exe', 'git.bat'] in your default path.
+
+  GIT_SYNC_DEPS_PATH: file to get the dependency list from; if unset,
+  will use the file ../DEPS relative to this script's directory.
+
+  GIT_SYNC_DEPS_QUIET: if set to non-empty string, suppress messages.
+
+Git Config:
+  To disable syncing of a single repository:
+      cd path/to/repository
+      git config sync-deps.disable true
+
+  To re-enable sync:
+      cd path/to/repository
+      git config --unset sync-deps.disable
+"""
+
+
+import os
+import re
+import subprocess
+import sys
+import threading
+from builtins import bytes
+
+
+def git_executable():
+  """Find the git executable.
+
+  Returns:
+      A string suitable for passing to subprocess functions, or None.
+  """
+  envgit = os.environ.get('GIT_EXECUTABLE')
+  searchlist = ['git', 'git.exe', 'git.bat']
+  if envgit:
+    searchlist.insert(0, envgit)
+  with open(os.devnull, 'w') as devnull:
+    for git in searchlist:
+      try:
+        subprocess.call([git, '--version'], stdout=devnull)
+      except (OSError,):
+        continue
+      return git
+  return None
+
+
+DEFAULT_DEPS_PATH = os.path.normpath(
+  os.path.join(os.path.dirname(__file__), os.pardir, 'DEPS'))
+
+
+def usage(deps_file_path = None):
+  sys.stderr.write(
+    'Usage: run to grab dependencies, with optional platform support:\n')
+  sys.stderr.write('  %s %s' % (sys.executable, __file__))
+  if deps_file_path:
+    parsed_deps = parse_file_to_dict(deps_file_path)
+    if 'deps_os' in parsed_deps:
+      for deps_os in parsed_deps['deps_os']:
+        sys.stderr.write(' [%s]' % deps_os)
+  sys.stderr.write('\n\n')
+  sys.stderr.write(__doc__)
+
+
+def git_repository_sync_is_disabled(git, directory):
+  try:
+    disable = subprocess.check_output(
+      [git, 'config', 'sync-deps.disable'], cwd=directory)
+    return disable.lower().strip() in ['true', '1', 'yes', 'on']
+  except subprocess.CalledProcessError:
+    return False
+
+
+def is_git_toplevel(git, directory):
+  """Return true iff the directory is the top level of a Git repository.
+
+  Args:
+    git (string) the git executable
+
+    directory (string) the path into which the repository
+              is expected to be checked out.
+  """
+  try:
+    toplevel = subprocess.check_output(
+      [git, 'rev-parse', '--show-toplevel'], cwd=directory).strip()
+    return os.path.realpath(bytes(directory, 'utf8')) == os.path.realpath(toplevel)
+  except subprocess.CalledProcessError:
+    return False
+
+
+def status(directory, checkoutable):
+  def truncate(s, length):
+    return s if len(s) <= length else s[:(length - 3)] + '...'
+  dlen = 36
+  directory = truncate(directory, dlen)
+  checkoutable = truncate(checkoutable, 40)
+  sys.stdout.write('%-*s @ %s\n' % (dlen, directory, checkoutable))
+
+
+def git_checkout_to_directory(git, repo, checkoutable, directory, verbose):
+  """Checkout (and clone if needed) a Git repository.
+
+  Args:
+    git (string) the git executable
+
+    repo (string) the location of the repository, suitable
+         for passing to `git clone`.
+
+    checkoutable (string) a tag, branch, or commit, suitable for
+                 passing to `git checkout`
+
+    directory (string) the path into which the repository
+              should be checked out.
+
+    verbose (boolean)
+
+  Raises an exception if any calls to git fail.
+  """
+  if not os.path.isdir(directory):
+    subprocess.check_call(
+      [git, 'clone', '--quiet', repo, directory])
+
+  if not is_git_toplevel(git, directory):
+    # if the directory exists, but isn't a git repo, you will modify
+    # the parent repostory, which isn't what you want.
+    sys.stdout.write('%s\n  IS NOT TOP-LEVEL GIT DIRECTORY.\n' % directory)
+    return
+
+  # Check to see if this repo is disabled.  Quick return.
+  if git_repository_sync_is_disabled(git, directory):
+    sys.stdout.write('%s\n  SYNC IS DISABLED.\n' % directory)
+    return
+
+  with open(os.devnull, 'w') as devnull:
+    # If this fails, we will fetch before trying again.  Don't spam user
+    # with error infomation.
+    if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable],
+                            cwd=directory, stderr=devnull):
+      # if this succeeds, skip slow `git fetch`.
+      if verbose:
+        status(directory, checkoutable)  # Success.
+      return
+
+  # If the repo has changed, always force use of the correct repo.
+  # If origin already points to repo, this is a quick no-op.
+  subprocess.check_call(
+      [git, 'remote', 'set-url', 'origin', repo], cwd=directory)
+
+  subprocess.check_call([git, 'fetch', '--quiet'], cwd=directory)
+
+  subprocess.check_call([git, 'checkout', '--quiet', checkoutable], cwd=directory)
+
+  if verbose:
+    status(directory, checkoutable)  # Success.
+
+
+def parse_file_to_dict(path):
+  dictionary = {}
+  contents = open(path).read()
+  # Need to convert Var() to vars[], so that the DEPS is actually Python. Var()
+  # comes from Autoroller using gclient which has a slightly different DEPS
+  # format.
+  contents = re.sub(r"Var\((.*?)\)", r"vars[\1]", contents)
+  exec(contents, dictionary)
+  return dictionary
+
+
+def git_sync_deps(deps_file_path, command_line_os_requests, verbose):
+  """Grab dependencies, with optional platform support.
+
+  Args:
+    deps_file_path (string) Path to the DEPS file.
+
+    command_line_os_requests (list of strings) Can be empty list.
+        List of strings that should each be a key in the deps_os
+        dictionary in the DEPS file.
+
+  Raises git Exceptions.
+  """
+  git = git_executable()
+  assert git
+
+  deps_file_directory = os.path.dirname(deps_file_path)
+  deps_file = parse_file_to_dict(deps_file_path)
+  dependencies = deps_file['deps'].copy()
+  os_specific_dependencies = deps_file.get('deps_os', dict())
+  if 'all' in command_line_os_requests:
+    for value in list(os_specific_dependencies.values()):
+      dependencies.update(value)
+  else:
+    for os_name in command_line_os_requests:
+      # Add OS-specific dependencies
+      if os_name in os_specific_dependencies:
+        dependencies.update(os_specific_dependencies[os_name])
+  for directory in dependencies:
+    for other_dir in dependencies:
+      if directory.startswith(other_dir + '/'):
+        raise Exception('%r is parent of %r' % (other_dir, directory))
+  list_of_arg_lists = []
+  for directory in sorted(dependencies):
+    if '@' in dependencies[directory]:
+      repo, checkoutable = dependencies[directory].split('@', 1)
+    else:
+      raise Exception("please specify commit or tag")
+
+    relative_directory = os.path.join(deps_file_directory, directory)
+
+    list_of_arg_lists.append(
+      (git, repo, checkoutable, relative_directory, verbose))
+
+  multithread(git_checkout_to_directory, list_of_arg_lists)
+
+  for directory in deps_file.get('recursedeps', []):
+    recursive_path = os.path.join(deps_file_directory, directory, 'DEPS')
+    git_sync_deps(recursive_path, command_line_os_requests, verbose)
+
+
+def multithread(function, list_of_arg_lists):
+  # for args in list_of_arg_lists:
+  #   function(*args)
+  # return
+  threads = []
+  for args in list_of_arg_lists:
+    thread = threading.Thread(None, function, None, args)
+    thread.start()
+    threads.append(thread)
+  for thread in threads:
+    thread.join()
+
+
+def main(argv):
+  deps_file_path = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH)
+  verbose = not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False))
+
+  if '--help' in argv or '-h' in argv:
+    usage(deps_file_path)
+    return 1
+
+  git_sync_deps(deps_file_path, argv, verbose)
+  # subprocess.check_call(
+  #     [sys.executable,
+  #      os.path.join(os.path.dirname(deps_file_path), 'bin', 'fetch-gn')])
+  return 0
+
+
+if __name__ == '__main__':
+  exit(main(sys.argv[1:]))

+ 31 - 0
3rdparty/spirv-tools/utils/roll_deps.sh

@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# 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.
+
+# Attempts to roll all entries in DEPS to origin/master and creates a
+# commit.
+#
+# Depends on roll-dep from depot_path being in PATH.
+
+# This script assumes it's parent directory is the repo root.
+repo_path=$(dirname "$0")/..
+
+effcee_dir="external/effcee/"
+googletest_dir="external/googletest/"
+re2_dir="external/re2/"
+spirv_headers_dir="external/spirv-headers/"
+
+cd "$repo_path"
+
+roll-dep "$@" "${effcee_dir}" "${googletest_dir}" "${re2_dir}" "${spirv_headers_dir}"