Browse Source

Updated spirv-tools.

Бранимир Караџић 6 years ago
parent
commit
5907520a19
50 changed files with 2777 additions and 264 deletions
  1. 3 1
      3rdparty/spirv-tools/Android.mk
  2. 4 0
      3rdparty/spirv-tools/BUILD.gn
  3. 1 1
      3rdparty/spirv-tools/CHANGES
  4. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  5. 40 5
      3rdparty/spirv-tools/include/spirv-tools/instrument.hpp
  6. 28 0
      3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp
  7. 4 0
      3rdparty/spirv-tools/source/opt/CMakeLists.txt
  8. 6 0
      3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp
  9. 19 1
      3rdparty/spirv-tools/source/opt/desc_sroa.cpp
  10. 0 4
      3rdparty/spirv-tools/source/opt/graphics_robust_access_pass.h
  11. 427 0
      3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp
  12. 133 0
      3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.h
  13. 70 27
      3rdparty/spirv-tools/source/opt/instrument_pass.cpp
  14. 28 6
      3rdparty/spirv-tools/source/opt/instrument_pass.h
  15. 21 0
      3rdparty/spirv-tools/source/opt/ir_builder.h
  16. 30 4
      3rdparty/spirv-tools/source/opt/optimizer.cpp
  17. 2 1
      3rdparty/spirv-tools/source/opt/pass.cpp
  18. 2 0
      3rdparty/spirv-tools/source/opt/passes.h
  19. 29 11
      3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp
  20. 5 5
      3rdparty/spirv-tools/source/opt/private_to_local_pass.h
  21. 125 65
      3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp
  22. 15 8
      3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h
  23. 43 4
      3rdparty/spirv-tools/source/opt/type_manager.cpp
  24. 5 2
      3rdparty/spirv-tools/source/opt/type_manager.h
  25. 2 2
      3rdparty/spirv-tools/source/opt/types.cpp
  26. 2 2
      3rdparty/spirv-tools/source/opt/types.h
  27. 146 0
      3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
  28. 72 0
      3rdparty/spirv-tools/source/opt/wrap_opkill.h
  29. 2 0
      3rdparty/spirv-tools/source/reduce/CMakeLists.txt
  30. 3 0
      3rdparty/spirv-tools/source/reduce/reducer.cpp
  31. 49 0
      3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp
  32. 36 0
      3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h
  33. 1 3
      3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
  34. 2 4
      3rdparty/spirv-tools/source/val/validate_constants.cpp
  35. 17 17
      3rdparty/spirv-tools/source/val/validate_extensions.cpp
  36. 40 36
      3rdparty/spirv-tools/test/link/type_match_test.cpp
  37. 2 0
      3rdparty/spirv-tools/test/opt/CMakeLists.txt
  38. 61 0
      3rdparty/spirv-tools/test/opt/desc_sroa_test.cpp
  39. 620 0
      3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp
  40. 13 8
      3rdparty/spirv-tools/test/opt/pass_fixture.h
  41. 33 0
      3rdparty/spirv-tools/test/opt/private_to_local_test.cpp
  42. 98 3
      3rdparty/spirv-tools/test/opt/scalar_replacement_test.cpp
  43. 28 26
      3rdparty/spirv-tools/test/opt/types_test.cpp
  44. 267 0
      3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp
  45. 1 0
      3rdparty/spirv-tools/test/reduce/CMakeLists.txt
  46. 177 0
      3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp
  47. 5 1
      3rdparty/spirv-tools/test/tools/opt/flags.py
  48. 16 0
      3rdparty/spirv-tools/test/val/val_constants_test.cpp
  49. 42 15
      3rdparty/spirv-tools/test/val/val_ext_inst_test.cpp
  50. 1 1
      3rdparty/spirv-tools/test/val/val_id_test.cpp

+ 3 - 1
3rdparty/spirv-tools/Android.mk

@@ -117,6 +117,7 @@ SPVTOOLS_OPT_SRC_FILES := \
 		source/opt/inline_exhaustive_pass.cpp \
 		source/opt/inline_opaque_pass.cpp \
 		source/opt/inst_bindless_check_pass.cpp \
+		source/opt/inst_buff_addr_check_pass.cpp \
 		source/opt/instruction.cpp \
 		source/opt/instruction_list.cpp \
 		source/opt/instrument_pass.cpp \
@@ -171,7 +172,8 @@ SPVTOOLS_OPT_SRC_FILES := \
 		source/opt/upgrade_memory_model.cpp \
 		source/opt/value_number_table.cpp \
 		source/opt/vector_dce.cpp \
-		source/opt/workaround1209.cpp
+		source/opt/workaround1209.cpp \
+		source/opt/wrap_opkill.cpp
 
 # Locations of grammar files.
 #

+ 4 - 0
3rdparty/spirv-tools/BUILD.gn

@@ -535,6 +535,8 @@ static_library("spvtools_opt") {
     "source/opt/inline_pass.h",
     "source/opt/inst_bindless_check_pass.cpp",
     "source/opt/inst_bindless_check_pass.h",
+    "source/opt/inst_buff_addr_check_pass.cpp",
+    "source/opt/inst_buff_addr_check_pass.h",
     "source/opt/instruction.cpp",
     "source/opt/instruction.h",
     "source/opt/instruction_list.cpp",
@@ -650,6 +652,8 @@ static_library("spvtools_opt") {
     "source/opt/vector_dce.h",
     "source/opt/workaround1209.cpp",
     "source/opt/workaround1209.h",
+    "source/opt/wrap_opkill.cpp",
+    "source/opt/wrap_opkill.h",
   ]
 
   deps = [

+ 1 - 1
3rdparty/spirv-tools/CHANGES

@@ -18,7 +18,7 @@ v2019.4 2019-08-08
    - Handle nested breaks from switches. (#2624)
    - Optimizer: Handle array type with OpSpecConstantOp length (#2652)
    - Perform merge return with single return in loop. (#2714)
-   - Add —preserve-bindings and —preserve-spec-constants (#2693)
+   - Add --preserve-bindings and --preserve-spec-constants (#2693)
    - Remove Common Uniform Elimination Pass (#2731)
    - Allow ray tracing shaders in inst bindle check pass. (#2733)
    - Add pass to inject code for robust-buffer-access semantics (#2771)

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

@@ -1 +1 @@
-"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-2-g95386f9e"
+"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-16-g8336d192"

+ 40 - 5
3rdparty/spirv-tools/include/spirv-tools/instrument.hpp

@@ -23,8 +23,9 @@
 // communicate with shaders instrumented by passes created by:
 //
 //   CreateInstBindlessCheckPass
+//   CreateInstBuffAddrCheckPass
 //
-// More detailed documentation of this routine can be found in optimizer.hpp
+// More detailed documentation of these routines can be found in optimizer.hpp
 
 namespace spvtools {
 
@@ -157,6 +158,12 @@ static const int kInst2BindlessUninitOutDescIndex = kInst2StageOutCnt + 1;
 static const int kInst2BindlessUninitOutUnused = kInst2StageOutCnt + 2;
 static const int kInst2BindlessUninitOutCnt = kInst2StageOutCnt + 3;
 
+// A buffer address unalloc error will output the 64-bit pointer in
+// two 32-bit pieces, lower bits first.
+static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1;
+static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2;
+static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3;
+
 // DEPRECATED
 static const int kInstBindlessOutDescIndex = kInstStageOutCnt + 1;
 static const int kInstBindlessOutDescBound = kInstStageOutCnt + 2;
@@ -171,6 +178,7 @@ static const int kInst2MaxOutCnt = kInst2StageOutCnt + 3;
 // These are the possible validation error codes.
 static const int kInstErrorBindlessBounds = 0;
 static const int kInstErrorBindlessUninit = 1;
+static const int kInstErrorBuffAddrUnallocRef = 2;
 
 // Direct Input Buffer Offsets
 //
@@ -187,14 +195,16 @@ static const int kDebugInputDataOffset = 0;
 // These are the bindings for the different buffers which are
 // read or written by the instrumentation passes.
 //
-// This is the output buffer written by InstBindlessCheckPass
-// and possibly other future validations.
+// This is the output buffer written by InstBindlessCheckPass,
+// InstBuffAddrCheckPass, and possibly other future validations.
 static const int kDebugOutputBindingStream = 0;
 
-// The binding for the input buffer read by InstBindlessCheckPass and
-// possibly other future validations.
+// The binding for the input buffer read by InstBindlessCheckPass.
 static const int kDebugInputBindingBindless = 1;
 
+// The binding for the input buffer read by InstBuffAddrCheckPass.
+static const int kDebugInputBindingBuffAddr = 2;
+
 // Bindless Validation Input Buffer Format
 //
 // An input buffer for bindless validation consists of a single array of
@@ -216,6 +226,31 @@ static const int kDebugInputBindlessOffsetReserved = 0;
 // Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ]
 static const int kDebugInputBindlessOffsetLengths = 1;
 
+// Buffer Device Address Input Buffer Format
+//
+// An input buffer for buffer device address validation consists of a single
+// array of unsigned 64-bit integers we will call Data[]. This array is
+// formatted as follows:
+//
+// At offset kDebugInputBuffAddrPtrOffset is a list of sorted valid buffer
+// addresses. The list is terminated with the address 0xffffffffffffffff.
+// If 0x0 is not a valid buffer address, this address is inserted at the
+// start of the list.
+//
+static const int kDebugInputBuffAddrPtrOffset = 1;
+//
+// At offset kDebugInputBuffAddrLengthOffset in Data[] is a single uint64 which
+// gives an offset to the start of the buffer length data. More
+// specifically, for a buffer whose pointer is located at input buffer offset
+// i, the length is located at:
+//
+// Data[ i - kDebugInputBuffAddrPtrOffset
+//         + Data[ kDebugInputBuffAddrLengthOffset ] ]
+//
+// The length associated with the 0xffffffffffffffff address is zero. If
+// not a valid buffer, the length associated with the 0x0 address is zero.
+static const int kDebugInputBuffAddrLengthOffset = 0;
+
 }  // namespace spvtools
 
 #endif  // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_

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

@@ -729,6 +729,30 @@ Optimizer::PassToken CreateInstBindlessCheckPass(
     uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false,
     bool input_init_enable = false, uint32_t version = 1);
 
+// Create a pass to instrument physical buffer address checking
+// This pass instruments all physical buffer address references to check that
+// all referenced bytes fall in a valid buffer. If the reference is
+// invalid, a record is written to the debug output buffer (if space allows)
+// and a null value is returned. This pass is designed to support buffer
+// address validation in the Vulkan validation layers.
+//
+// Dead code elimination should be run after this pass as the original,
+// potentially invalid code is not removed and could cause undefined behavior,
+// including crashes. Instruction simplification would likely also be
+// beneficial. It is also generally recommended that this pass (and all
+// instrumentation passes) be run after any legalization and optimization
+// passes. This will give better analysis for the instrumentation and avoid
+// potentially de-optimizing the instrument code, for example, inlining
+// the debug record output function throughout the module.
+//
+// The instrumentation will read and write buffers in debug
+// descriptor set |desc_set|. It will write |shader_id| in each output record
+// to identify the shader module which generated the record.
+// |version| specifies the output buffer record format.
+Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
+                                                 uint32_t shader_id,
+                                                 uint32_t version = 2);
+
 // Create a pass to upgrade to the VulkanKHR memory model.
 // This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
 // Additionally, it modifies memory, image, atomic and barrier operations to
@@ -795,6 +819,10 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass();
 // for the first index.
 Optimizer::PassToken CreateDescriptorScalarReplacementPass();
 
+// Create a pass to replace all OpKill instruction with a function call to a
+// function that has a single OpKill.  This allows more code to be inlined.
+Optimizer::PassToken CreateWrapOpKillPass();
+
 }  // namespace spvtools
 
 #endif  // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_

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

@@ -55,6 +55,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
   inline_opaque_pass.h
   inline_pass.h
   inst_bindless_check_pass.h
+  inst_buff_addr_check_pass.h
   instruction.h
   instruction_list.h
   instrument_pass.h
@@ -113,6 +114,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
   value_number_table.h
   vector_dce.h
   workaround1209.h
+  wrap_opkill.h
 
   aggressive_dead_code_elim_pass.cpp
   basic_block.cpp
@@ -157,6 +159,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
   inline_opaque_pass.cpp
   inline_pass.cpp
   inst_bindless_check_pass.cpp
+  inst_buff_addr_check_pass.cpp
   instruction.cpp
   instruction_list.cpp
   instrument_pass.cpp
@@ -212,6 +215,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
   value_number_table.cpp
   vector_dce.cpp
   workaround1209.cpp
+  wrap_opkill.cpp
 )
 
 if(MSVC)

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

@@ -527,6 +527,9 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
                                     pointer_type->storage_class());
         uint32_t new_pointer_type_id =
             context()->get_type_mgr()->GetTypeInstruction(&pointerTy);
+        if (new_pointer_type_id == 0) {
+          return false;
+        }
 
         if (new_pointer_type_id != use->type_id()) {
           return CanUpdateUses(use, new_pointer_type_id);
@@ -542,6 +545,9 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
         const analysis::Type* new_type =
             type_mgr->GetMemberType(type, access_chain);
         uint32_t new_type_id = type_mgr->GetTypeInstruction(new_type);
+        if (new_type_id == 0) {
+          return false;
+        }
 
         if (new_type_id != use->type_id()) {
           return CanUpdateUses(use, new_type_id);

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

@@ -14,7 +14,7 @@
 
 #include "source/opt/desc_sroa.h"
 
-#include <source/util/string_utils.h>
+#include "source/util/string_utils.h"
 
 namespace spvtools {
 namespace opt {
@@ -248,6 +248,24 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
     context()->AddAnnotationInst(std::move(new_decoration));
   }
 
+  // Create a new OpName for the replacement variable.
+  for (auto p : context()->GetNames(var->result_id())) {
+    Instruction* name_inst = p.second;
+    std::string name_str = utils::MakeString(name_inst->GetOperand(1).words);
+    name_str += "[";
+    name_str += utils::ToString(idx);
+    name_str += "]";
+
+    std::unique_ptr<Instruction> new_name(new Instruction(
+        context(), SpvOpName, 0, 0,
+        std::initializer_list<Operand>{
+            {SPV_OPERAND_TYPE_ID, {id}},
+            {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
+    Instruction* new_name_inst = new_name.get();
+    context()->AddDebug2Inst(std::move(new_name));
+    get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst);
+  }
+
   return id;
 }
 

+ 0 - 4
3rdparty/spirv-tools/source/opt/graphics_robust_access_pass.h

@@ -77,10 +77,6 @@ class GraphicsRobustAccessPass : public Pass {
   // type. Ignores any value bits beyond the width of the type.
   Instruction* GetValueForType(uint64_t value, const analysis::Integer* type);
 
-  // Returns the unsigned value for the given constant.  Assumes it's at most
-  // 64 bits wide.
-  uint64_t GetUnsignedValueForConstant(const analysis::Constant* c);
-
   // Converts an integer value to an unsigned wider integer type, using either
   // sign extension or zero extension.  The new instruction is inserted
   // immediately before |before_inst|, and is analyzed for definitions and uses.

+ 427 - 0
3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp

@@ -0,0 +1,427 @@
+// Copyright (c) 2019 The Khronos Group Inc.
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "inst_buff_addr_check_pass.h"
+
+namespace spvtools {
+namespace opt {
+
+uint32_t InstBuffAddrCheckPass::CloneOriginalReference(
+    Instruction* ref_inst, InstructionBuilder* builder) {
+  // Clone original ref with new result id (if load)
+  assert(
+      (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) &&
+      "unexpected ref");
+  std::unique_ptr<Instruction> new_ref_inst(ref_inst->Clone(context()));
+  uint32_t ref_result_id = ref_inst->result_id();
+  uint32_t new_ref_id = 0;
+  if (ref_result_id != 0) {
+    new_ref_id = TakeNextId();
+    new_ref_inst->SetResultId(new_ref_id);
+  }
+  // Register new reference and add to new block
+  Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst));
+  uid2offset_[added_inst->unique_id()] = uid2offset_[ref_inst->unique_id()];
+  if (new_ref_id != 0)
+    get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id);
+  return new_ref_id;
+}
+
+bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) {
+  if (ref_inst->opcode() != SpvOpLoad && ref_inst->opcode() != SpvOpStore)
+    return false;
+  uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0);
+  analysis::DefUseManager* du_mgr = get_def_use_mgr();
+  Instruction* ptr_inst = du_mgr->GetDef(ptr_id);
+  if (ptr_inst->opcode() != SpvOpAccessChain) return false;
+  uint32_t ptr_ty_id = ptr_inst->type_id();
+  Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id);
+  if (ptr_ty_inst->GetSingleWordInOperand(0) !=
+      SpvStorageClassPhysicalStorageBufferEXT)
+    return false;
+  return true;
+}
+
+// TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ??
+void InstBuffAddrCheckPass::GenCheckCode(
+    uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id,
+    uint32_t stage_idx, Instruction* ref_inst,
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  BasicBlock* back_blk_ptr = &*new_blocks->back();
+  InstructionBuilder builder(
+      context(), back_blk_ptr,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  // Gen conditional branch on check_id. Valid branch generates original
+  // reference. Invalid generates debug output and zero result (if needed).
+  uint32_t merge_blk_id = TakeNextId();
+  uint32_t valid_blk_id = TakeNextId();
+  uint32_t invalid_blk_id = TakeNextId();
+  std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
+  std::unique_ptr<Instruction> valid_label(NewLabel(valid_blk_id));
+  std::unique_ptr<Instruction> invalid_label(NewLabel(invalid_blk_id));
+  (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id,
+                                     merge_blk_id, SpvSelectionControlMaskNone);
+  // Gen valid branch
+  std::unique_ptr<BasicBlock> new_blk_ptr(
+      new BasicBlock(std::move(valid_label)));
+  builder.SetInsertPoint(&*new_blk_ptr);
+  uint32_t new_ref_id = CloneOriginalReference(ref_inst, &builder);
+  (void)builder.AddBranch(merge_blk_id);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  // Gen invalid block
+  new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
+  builder.SetInsertPoint(&*new_blk_ptr);
+  // Convert uptr from uint64 to 2 uint32
+  Instruction* lo_uptr_inst =
+      builder.AddUnaryOp(GetUintId(), SpvOpUConvert, ref_uptr_id);
+  Instruction* rshift_uptr_inst =
+      builder.AddBinaryOp(GetUint64Id(), SpvOpShiftRightLogical, ref_uptr_id,
+                          builder.GetUintConstantId(32));
+  Instruction* hi_uptr_inst = builder.AddUnaryOp(GetUintId(), SpvOpUConvert,
+                                                 rshift_uptr_inst->result_id());
+  GenDebugStreamWrite(
+      uid2offset_[ref_inst->unique_id()], stage_idx,
+      {error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
+      &builder);
+  // Gen zero for invalid  reference
+  uint32_t ref_type_id = ref_inst->type_id();
+  (void)builder.AddBranch(merge_blk_id);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  // Gen merge block
+  new_blk_ptr.reset(new BasicBlock(std::move(merge_label)));
+  builder.SetInsertPoint(&*new_blk_ptr);
+  // Gen phi of new reference and zero, if necessary, and replace the
+  // result id of the original reference with that of the Phi. Kill original
+  // reference.
+  if (new_ref_id != 0) {
+    Instruction* phi_inst = builder.AddPhi(
+        ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id),
+                      invalid_blk_id});
+    context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id());
+  }
+  new_blocks->push_back(std::move(new_blk_ptr));
+  context()->KillInst(ref_inst);
+}
+
+uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) {
+  Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+  switch (type_inst->opcode()) {
+    case SpvOpTypeFloat:
+    case SpvOpTypeInt:
+      return type_inst->GetSingleWordInOperand(0) / 8u;
+    case SpvOpTypeVector:
+    case SpvOpTypeMatrix:
+      return type_inst->GetSingleWordInOperand(1) *
+             GetTypeLength(type_inst->GetSingleWordInOperand(0));
+    case SpvOpTypePointer:
+      assert(type_inst->GetSingleWordInOperand(0) ==
+                 SpvStorageClassPhysicalStorageBufferEXT &&
+             "unexpected pointer type");
+      return 8u;
+    default:
+      assert(false && "unexpected buffer reference type");
+      return 0;
+  }
+}
+
+void InstBuffAddrCheckPass::AddParam(uint32_t type_id,
+                                     std::vector<uint32_t>* param_vec,
+                                     std::unique_ptr<Function>* input_func) {
+  uint32_t pid = TakeNextId();
+  param_vec->push_back(pid);
+  std::unique_ptr<Instruction> param_inst(new Instruction(
+      get_module()->context(), SpvOpFunctionParameter, type_id, pid, {}));
+  get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
+  (*input_func)->AddParameter(std::move(param_inst));
+}
+
+uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() {
+  if (search_test_func_id_ == 0) {
+    // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)"
+    // which searches input buffer for buffer which most likely contains the
+    // pointer value |ref_ptr| and verifies that the entire reference of
+    // length |len| bytes is contained in the buffer.
+    search_test_func_id_ = TakeNextId();
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    std::vector<const analysis::Type*> param_types = {
+        type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())};
+    analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types);
+    analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
+    std::unique_ptr<Instruction> func_inst(
+        new Instruction(get_module()->context(), SpvOpFunction, GetBoolId(),
+                        search_test_func_id_,
+                        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+                          {SpvFunctionControlMaskNone}},
+                         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+                          {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
+    get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
+    std::unique_ptr<Function> input_func =
+        MakeUnique<Function>(std::move(func_inst));
+    std::vector<uint32_t> param_vec;
+    // Add ref_ptr and length parameters
+    AddParam(GetUint64Id(), &param_vec, &input_func);
+    AddParam(GetUintId(), &param_vec, &input_func);
+    // Empty first block.
+    uint32_t first_blk_id = TakeNextId();
+    std::unique_ptr<Instruction> first_blk_label(NewLabel(first_blk_id));
+    std::unique_ptr<BasicBlock> first_blk_ptr =
+        MakeUnique<BasicBlock>(std::move(first_blk_label));
+    InstructionBuilder builder(
+        context(), &*first_blk_ptr,
+        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+    uint32_t hdr_blk_id = TakeNextId();
+    // Branch to search loop header
+    std::unique_ptr<Instruction> hdr_blk_label(NewLabel(hdr_blk_id));
+    (void)builder.AddInstruction(MakeUnique<Instruction>(
+        context(), SpvOpBranch, 0, 0,
+        std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {hdr_blk_id}}}));
+    first_blk_ptr->SetParent(&*input_func);
+    input_func->AddBasicBlock(std::move(first_blk_ptr));
+    // Linear search loop header block
+    // TODO(greg-lunarg): Implement binary search
+    std::unique_ptr<BasicBlock> hdr_blk_ptr =
+        MakeUnique<BasicBlock>(std::move(hdr_blk_label));
+    builder.SetInsertPoint(&*hdr_blk_ptr);
+    // Phi for search index. Starts with 1.
+    uint32_t cont_blk_id = TakeNextId();
+    std::unique_ptr<Instruction> cont_blk_label(NewLabel(cont_blk_id));
+    // Deal with def-use cycle caused by search loop index computation.
+    // Create Add and Phi instructions first, then do Def analysis on Add.
+    // Add Phi and Add instructions and do Use analysis later.
+    uint32_t idx_phi_id = TakeNextId();
+    uint32_t idx_inc_id = TakeNextId();
+    std::unique_ptr<Instruction> idx_inc_inst(new Instruction(
+        context(), SpvOpIAdd, GetUintId(), idx_inc_id,
+        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+          {builder.GetUintConstantId(1u)}}}));
+    std::unique_ptr<Instruction> idx_phi_inst(new Instruction(
+        context(), SpvOpPhi, GetUintId(), idx_phi_id,
+        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+          {builder.GetUintConstantId(1u)}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
+    get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst);
+    // Add (previously created) search index phi
+    (void)builder.AddInstruction(std::move(idx_phi_inst));
+    // LoopMerge
+    uint32_t bound_test_blk_id = TakeNextId();
+    std::unique_ptr<Instruction> bound_test_blk_label(
+        NewLabel(bound_test_blk_id));
+    (void)builder.AddInstruction(MakeUnique<Instruction>(
+        context(), SpvOpLoopMerge, 0, 0,
+        std::initializer_list<Operand>{
+            {SPV_OPERAND_TYPE_ID, {bound_test_blk_id}},
+            {SPV_OPERAND_TYPE_ID, {cont_blk_id}},
+            {SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvLoopControlMaskNone}}}));
+    // Branch to continue/work block
+    (void)builder.AddInstruction(MakeUnique<Instruction>(
+        context(), SpvOpBranch, 0, 0,
+        std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
+    hdr_blk_ptr->SetParent(&*input_func);
+    input_func->AddBasicBlock(std::move(hdr_blk_ptr));
+    // Continue/Work Block. Read next buffer pointer and break if greater
+    // than ref_ptr arg.
+    std::unique_ptr<BasicBlock> cont_blk_ptr =
+        MakeUnique<BasicBlock>(std::move(cont_blk_label));
+    builder.SetInsertPoint(&*cont_blk_ptr);
+    // Add (previously created) search index increment now.
+    (void)builder.AddInstruction(std::move(idx_inc_inst));
+    // Load next buffer address from debug input buffer
+    uint32_t ibuf_id = GetInputBufferId();
+    uint32_t ibuf_ptr_id = GetInputBufferPtrId();
+    Instruction* uptr_ac_inst = builder.AddTernaryOp(
+        ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
+        builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id);
+    uint32_t ibuf_type_id = GetInputBufferTypeId();
+    Instruction* uptr_load_inst =
+        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, uptr_ac_inst->result_id());
+    // If loaded address greater than ref_ptr arg, break, else branch back to
+    // loop header
+    Instruction* uptr_test_inst =
+        builder.AddBinaryOp(GetBoolId(), SpvOpUGreaterThan,
+                            uptr_load_inst->result_id(), param_vec[0]);
+    (void)builder.AddConditionalBranch(uptr_test_inst->result_id(),
+                                       bound_test_blk_id, hdr_blk_id,
+                                       kInvalidId, SpvSelectionControlMaskNone);
+    cont_blk_ptr->SetParent(&*input_func);
+    input_func->AddBasicBlock(std::move(cont_blk_ptr));
+    // Bounds test block. Read length of selected buffer and test that
+    // all len arg bytes are in buffer.
+    std::unique_ptr<BasicBlock> bound_test_blk_ptr =
+        MakeUnique<BasicBlock>(std::move(bound_test_blk_label));
+    builder.SetInsertPoint(&*bound_test_blk_ptr);
+    // Decrement index to point to previous/candidate buffer address
+    Instruction* cand_idx_inst = builder.AddBinaryOp(
+        GetUintId(), SpvOpISub, idx_inc_id, builder.GetUintConstantId(1u));
+    // Load candidate buffer address
+    Instruction* cand_ac_inst =
+        builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
+                             builder.GetUintConstantId(kDebugInputDataOffset),
+                             cand_idx_inst->result_id());
+    Instruction* cand_load_inst =
+        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, cand_ac_inst->result_id());
+    // Compute offset of ref_ptr from candidate buffer address
+    Instruction* offset_inst = builder.AddBinaryOp(
+        ibuf_type_id, SpvOpISub, param_vec[0], cand_load_inst->result_id());
+    // Convert ref length to uint64
+    Instruction* ref_len_64_inst =
+        builder.AddUnaryOp(ibuf_type_id, SpvOpUConvert, param_vec[1]);
+    // Add ref length to ref offset to compute end of reference
+    Instruction* ref_end_inst =
+        builder.AddBinaryOp(ibuf_type_id, SpvOpIAdd, offset_inst->result_id(),
+                            ref_len_64_inst->result_id());
+    // Load starting index of lengths in input buffer and convert to uint32
+    Instruction* len_start_ac_inst =
+        builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
+                             builder.GetUintConstantId(kDebugInputDataOffset),
+                             builder.GetUintConstantId(0u));
+    Instruction* len_start_load_inst = builder.AddUnaryOp(
+        ibuf_type_id, SpvOpLoad, len_start_ac_inst->result_id());
+    Instruction* len_start_32_inst = builder.AddUnaryOp(
+        GetUintId(), SpvOpUConvert, len_start_load_inst->result_id());
+    // Decrement search index to get candidate buffer length index
+    Instruction* cand_len_idx_inst =
+        builder.AddBinaryOp(GetUintId(), SpvOpISub, cand_idx_inst->result_id(),
+                            builder.GetUintConstantId(1u));
+    // Add candidate length index to start index
+    Instruction* len_idx_inst = builder.AddBinaryOp(
+        GetUintId(), SpvOpIAdd, cand_len_idx_inst->result_id(),
+        len_start_32_inst->result_id());
+    // Load candidate buffer length
+    Instruction* len_ac_inst =
+        builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
+                             builder.GetUintConstantId(kDebugInputDataOffset),
+                             len_idx_inst->result_id());
+    Instruction* len_load_inst =
+        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, len_ac_inst->result_id());
+    // Test if reference end within candidate buffer length
+    Instruction* len_test_inst = builder.AddBinaryOp(
+        GetBoolId(), SpvOpULessThanEqual, ref_end_inst->result_id(),
+        len_load_inst->result_id());
+    // Return test result
+    (void)builder.AddInstruction(MakeUnique<Instruction>(
+        context(), SpvOpReturnValue, 0, 0,
+        std::initializer_list<Operand>{
+            {SPV_OPERAND_TYPE_ID, {len_test_inst->result_id()}}}));
+    // Close block
+    bound_test_blk_ptr->SetParent(&*input_func);
+    input_func->AddBasicBlock(std::move(bound_test_blk_ptr));
+    // Close function and add function to module
+    std::unique_ptr<Instruction> func_end_inst(
+        new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {}));
+    get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
+    input_func->SetFunctionEnd(std::move(func_end_inst));
+    context()->AddFunction(std::move(input_func));
+  }
+  return search_test_func_id_;
+}
+
+uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst,
+                                                 InstructionBuilder* builder,
+                                                 uint32_t* ref_uptr_id) {
+  // Enable Int64 if necessary
+  if (!get_feature_mgr()->HasCapability(SpvCapabilityInt64)) {
+    std::unique_ptr<Instruction> cap_int64_inst(new Instruction(
+        context(), SpvOpCapability, 0, 0,
+        std::initializer_list<Operand>{
+            {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityInt64}}}));
+    get_def_use_mgr()->AnalyzeInstDefUse(&*cap_int64_inst);
+    get_module()->AddCapability(std::move(cap_int64_inst));
+  }
+  // Convert reference pointer to uint64
+  uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
+  Instruction* ref_uptr_inst =
+      builder->AddUnaryOp(GetUint64Id(), SpvOpConvertPtrToU, ref_ptr_id);
+  *ref_uptr_id = ref_uptr_inst->result_id();
+  // Compute reference length in bytes
+  analysis::DefUseManager* du_mgr = get_def_use_mgr();
+  Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id);
+  uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
+  Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id);
+  uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
+  uint32_t ref_len_id = builder->GetUintConstantId(ref_len);
+  // Gen call to search and test function
+  const std::vector<uint32_t> args = {GetSearchAndTestFuncId(), *ref_uptr_id,
+                                      ref_len_id};
+  Instruction* call_inst =
+      builder->AddNaryOp(GetBoolId(), SpvOpFunctionCall, args);
+  uint32_t retval = call_inst->result_id();
+  return retval;
+}
+
+void InstBuffAddrCheckPass::GenBuffAddrCheckCode(
+    BasicBlock::iterator ref_inst_itr,
+    UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  // Look for reference through indexed descriptor. If found, analyze and
+  // save components. If not, return.
+  Instruction* ref_inst = &*ref_inst_itr;
+  if (!IsPhysicalBuffAddrReference(ref_inst)) return;
+  // Move original block's preceding instructions into first new block
+  std::unique_ptr<BasicBlock> new_blk_ptr;
+  MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
+  InstructionBuilder builder(
+      context(), &*new_blk_ptr,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef);
+  // Generate code to do search and test if all bytes of reference
+  // are within a listed buffer. Return reference pointer converted to uint64.
+  uint32_t ref_uptr_id;
+  uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id);
+  // Generate test of search results with true branch
+  // being full reference and false branch being debug output and zero
+  // for the referenced value.
+  GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst,
+               new_blocks);
+  // Move original block's remaining code into remainder/merge block and add
+  // to new blocks
+  BasicBlock* back_blk_ptr = &*new_blocks->back();
+  MovePostludeCode(ref_block_itr, back_blk_ptr);
+}
+
+void InstBuffAddrCheckPass::InitInstBuffAddrCheck() {
+  // Initialize base class
+  InitializeInstrument();
+  // Initialize class
+  search_test_func_id_ = 0;
+}
+
+Pass::Status InstBuffAddrCheckPass::ProcessImpl() {
+  // Perform bindless bounds check on each entry point function in module
+  InstProcessFunction pfn =
+      [this](BasicBlock::iterator ref_inst_itr,
+             UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+             std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+        return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
+                                    new_blocks);
+      };
+  bool modified = InstProcessEntryPointCallTree(pfn);
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+Pass::Status InstBuffAddrCheckPass::Process() {
+  if (!get_feature_mgr()->HasCapability(
+          SpvCapabilityPhysicalStorageBufferAddressesEXT))
+    return Status::SuccessWithoutChange;
+  InitInstBuffAddrCheck();
+  return ProcessImpl();
+}
+
+}  // namespace opt
+}  // namespace spvtools

+ 133 - 0
3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.h

@@ -0,0 +1,133 @@
+// Copyright (c) 2019 The Khronos Group Inc.
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 LunarG 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.
+
+#ifndef LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
+#define LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
+
+#include "instrument_pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// This class/pass is designed to support the GPU-assisted validation layer of
+// the Buffer Device Address (BDA) extension in
+// https://github.com/KhronosGroup/Vulkan-ValidationLayers. The internal and
+// external design of this class may change as the layer evolves.
+class InstBuffAddrCheckPass : public InstrumentPass {
+ public:
+  // For test harness only
+  InstBuffAddrCheckPass()
+      : InstrumentPass(7, 23, kInstValidationIdBuffAddr, 1) {}
+  // For all other interfaces
+  InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version)
+      : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr,
+                       version) {}
+
+  ~InstBuffAddrCheckPass() override = default;
+
+  // See optimizer.hpp for pass user documentation.
+  Status Process() override;
+
+  const char* name() const override { return "inst-bindless-check-pass"; }
+
+ private:
+  // Return byte length of type |type_id|. Must be int, float, vector, matrix
+  // or physical pointer.
+  uint32_t GetTypeLength(uint32_t type_id);
+
+  // Add |type_id| param to |input_func| and add id to |param_vec|.
+  void AddParam(uint32_t type_id, std::vector<uint32_t>* param_vec,
+                std::unique_ptr<Function>* input_func);
+
+  // Return id for search and test function. Generate it if not already gen'd.
+  uint32_t GetSearchAndTestFuncId();
+
+  // Generate code into |builder| to do search of the BDA debug input buffer
+  // for the buffer used by |ref_inst| and test that all bytes of reference
+  // are within the buffer. Returns id of boolean value which is true if
+  // search and test is successful, false otherwise.
+  uint32_t GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder,
+                            uint32_t* ref_uptr_id);
+
+  // This function does checking instrumentation on a single
+  // instruction which references through a physical storage buffer address.
+  // GenBuffAddrCheckCode generates code that checks that all bytes that
+  // are referenced fall within a buffer that was queried via
+  // the Vulkan API call vkGetBufferDeviceAddressEXT().
+  //
+  // The function is designed to be passed to
+  // InstrumentPass::InstProcessEntryPointCallTree(), which applies the
+  // function to each instruction in a module and replaces the instruction
+  // with instrumented code if warranted.
+  //
+  // If |ref_inst_itr| is a physical storage buffer reference, return in
+  // |new_blocks| the result of instrumenting it with validation code within
+  // its block at |ref_block_itr|.  The validation code first executes a check
+  // for the specific condition called for. If the check passes, it executes
+  // the remainder of the reference, otherwise writes a record to the debug
+  // output buffer stream including |function_idx, instruction_idx, stage_idx|
+  // and replaces the reference with the null value of the original type. The
+  // block at |ref_block_itr| can just be replaced with the blocks in
+  // |new_blocks|, which will contain at least two blocks. The last block will
+  // comprise all instructions following |ref_inst_itr|,
+  // preceded by a phi instruction if needed.
+  //
+  // This instrumentation function utilizes GenDebugStreamWrite() to write its
+  // error records. The validation-specific part of the error record will
+  // have the format:
+  //
+  //    Validation Error Code (=kInstErrorBuffAddr)
+  //    Buffer Address (lowest 32 bits)
+  //    Buffer Address (highest 32 bits)
+  //
+  void GenBuffAddrCheckCode(
+      BasicBlock::iterator ref_inst_itr,
+      UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+      std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Return true if |ref_inst| is a physical buffer address reference, false
+  // otherwise.
+  bool IsPhysicalBuffAddrReference(Instruction* ref_inst);
+
+  // Clone original reference |ref_inst| into |builder| and return id of result
+  uint32_t CloneOriginalReference(Instruction* ref_inst,
+                                  InstructionBuilder* builder);
+
+  // Generate instrumentation code for boolean test result |check_id|,
+  // adding new blocks to |new_blocks|. Generate conditional branch to valid
+  // or invalid reference blocks. Generate valid reference block which does
+  // original reference |ref_inst|. Then generate invalid reference block which
+  // writes debug error output utilizing |ref_inst|, |error_id| and
+  // |stage_idx|. Generate merge block for valid and invalid reference blocks.
+  // Kill original reference.
+  void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t length_id,
+                    uint32_t stage_idx, Instruction* ref_inst,
+                    std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Initialize state for instrumenting physical buffer address checking
+  void InitInstBuffAddrCheck();
+
+  // Apply GenBuffAddrCheckCode to every instruction in module.
+  Pass::Status ProcessImpl();
+
+  // Id of search and test function, if already gen'd, else zero.
+  uint32_t search_test_func_id_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_

+ 70 - 27
3rdparty/spirv-tools/source/opt/instrument_pass.cpp

@@ -107,7 +107,7 @@ void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
       builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id,
                            builder->GetUintConstantId(field_offset));
   uint32_t buf_id = GetOutputBufferId();
-  uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
+  uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
   Instruction* achain_inst =
       builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
                             builder->GetUintConstantId(kDebugOutputDataOffset),
@@ -373,19 +373,33 @@ void InstrumentPass::UpdateSucceedingPhis(
       });
 }
 
-// Return id for output buffer uint ptr type
-uint32_t InstrumentPass::GetBufferUintPtrId() {
-  if (buffer_uint_ptr_id_ == 0) {
-    buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
+uint32_t InstrumentPass::GetOutputBufferPtrId() {
+  if (output_buffer_ptr_id_ == 0) {
+    output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
         GetUintId(), SpvStorageClassStorageBuffer);
   }
-  return buffer_uint_ptr_id_;
+  return output_buffer_ptr_id_;
+}
+
+uint32_t InstrumentPass::GetInputBufferTypeId() {
+  return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id()
+                                                       : GetUintId();
+}
+
+uint32_t InstrumentPass::GetInputBufferPtrId() {
+  if (input_buffer_ptr_id_ == 0) {
+    input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
+        GetInputBufferTypeId(), SpvStorageClassStorageBuffer);
+  }
+  return input_buffer_ptr_id_;
 }
 
 uint32_t InstrumentPass::GetOutputBufferBinding() {
   switch (validation_id_) {
     case kInstValidationIdBindless:
       return kDebugOutputBindingStream;
+    case kInstValidationIdBuffAddr:
+      return kDebugOutputBindingStream;
     default:
       assert(false && "unexpected validation id");
   }
@@ -396,20 +410,24 @@ uint32_t InstrumentPass::GetInputBufferBinding() {
   switch (validation_id_) {
     case kInstValidationIdBindless:
       return kDebugInputBindingBindless;
+    case kInstValidationIdBuffAddr:
+      return kDebugInputBindingBuffAddr;
     default:
       assert(false && "unexpected validation id");
   }
   return 0;
 }
 
-analysis::Type* InstrumentPass::GetUintRuntimeArrayType(
-    analysis::DecorationManager* deco_mgr, analysis::TypeManager* type_mgr) {
-  if (uint_rarr_ty_ == nullptr) {
-    analysis::Integer uint_ty(32, false);
+analysis::Type* InstrumentPass::GetUintXRuntimeArrayType(
+    uint32_t width, analysis::Type** rarr_ty) {
+  if (*rarr_ty == nullptr) {
+    analysis::DecorationManager* deco_mgr = get_decoration_mgr();
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::Integer uint_ty(width, false);
     analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
     analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty);
-    uint_rarr_ty_ = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
-    uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(uint_rarr_ty_);
+    *rarr_ty = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
+    uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(*rarr_ty);
     // By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of
     // a block, and will therefore be decorated with an ArrayStride. Therefore
     // the undecorated type returned here will not be pre-existing and can
@@ -418,9 +436,16 @@ analysis::Type* InstrumentPass::GetUintRuntimeArrayType(
     // invalidated after this pass.
     assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 &&
            "used RuntimeArray type returned");
-    deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u);
+    deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride,
+                               width / 8u);
   }
-  return uint_rarr_ty_;
+  return *rarr_ty;
+}
+
+analysis::Type* InstrumentPass::GetUintRuntimeArrayType(uint32_t width) {
+  analysis::Type** rarr_ty =
+      (width == 64) ? &uint64_rarr_ty_ : &uint32_rarr_ty_;
+  return GetUintXRuntimeArrayType(width, rarr_ty);
 }
 
 void InstrumentPass::AddStorageBufferExt() {
@@ -445,8 +470,7 @@ uint32_t InstrumentPass::GetOutputBufferId() {
     // If not created yet, create one
     analysis::DecorationManager* deco_mgr = get_decoration_mgr();
     analysis::TypeManager* type_mgr = context()->get_type_mgr();
-    analysis::Type* reg_uint_rarr_ty =
-        GetUintRuntimeArrayType(deco_mgr, type_mgr);
+    analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(32);
     analysis::Integer uint_ty(32, false);
     analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
     analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty});
@@ -494,8 +518,8 @@ uint32_t InstrumentPass::GetInputBufferId() {
     // If not created yet, create one
     analysis::DecorationManager* deco_mgr = get_decoration_mgr();
     analysis::TypeManager* type_mgr = context()->get_type_mgr();
-    analysis::Type* reg_uint_rarr_ty =
-        GetUintRuntimeArrayType(deco_mgr, type_mgr);
+    uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u;
+    analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width);
     analysis::Struct buf_ty({reg_uint_rarr_ty});
     analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty);
     uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
@@ -555,6 +579,16 @@ uint32_t InstrumentPass::GetUintId() {
   return uint_id_;
 }
 
+uint32_t InstrumentPass::GetUint64Id() {
+  if (uint64_id_ == 0) {
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::Integer uint64_ty(64, false);
+    analysis::Type* reg_uint64_ty = type_mgr->GetRegisteredType(&uint64_ty);
+    uint64_id_ = type_mgr->GetTypeInstruction(reg_uint64_ty);
+  }
+  return uint64_id_;
+}
+
 uint32_t InstrumentPass::GetVecUintId(uint32_t len) {
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::Integer uint_ty(32, false);
@@ -642,7 +676,7 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
         (version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt;
     uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt;
     uint32_t buf_id = GetOutputBufferId();
-    uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
+    uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
     Instruction* obuf_curr_sz_ac_inst =
         builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
                             builder.GetUintConstantId(kDebugOutputSizeOffset));
@@ -713,16 +747,17 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
 uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
   uint32_t func_id = param2input_func_id_[param_cnt];
   if (func_id != 0) return func_id;
-  // Create input function for param_cnt
+  // Create input function for param_cnt.
   func_id = TakeNextId();
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   std::vector<const analysis::Type*> param_types;
   for (uint32_t c = 0; c < param_cnt; ++c)
     param_types.push_back(type_mgr->GetType(GetUintId()));
-  analysis::Function func_ty(type_mgr->GetType(GetUintId()), param_types);
+  uint32_t ibuf_type_id = GetInputBufferTypeId();
+  analysis::Function func_ty(type_mgr->GetType(ibuf_type_id), param_types);
   analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
   std::unique_ptr<Instruction> func_inst(new Instruction(
-      get_module()->context(), SpvOpFunction, GetUintId(), func_id,
+      get_module()->context(), SpvOpFunction, ibuf_type_id, func_id,
       {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
         {SpvFunctionControlMaskNone}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
@@ -752,22 +787,27 @@ uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
   // loaded value if it exists, and load value from input buffer at new offset.
   // Return last loaded value.
   uint32_t buf_id = GetInputBufferId();
-  uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
+  uint32_t buf_ptr_id = GetInputBufferPtrId();
   uint32_t last_value_id = 0;
   for (uint32_t p = 0; p < param_cnt; ++p) {
     uint32_t offset_id;
     if (p == 0) {
       offset_id = param_vec[0];
     } else {
+      if (ibuf_type_id != GetUintId()) {
+        Instruction* ucvt_inst =
+            builder.AddUnaryOp(GetUintId(), SpvOpUConvert, last_value_id);
+        last_value_id = ucvt_inst->result_id();
+      }
       Instruction* offset_inst = builder.AddBinaryOp(
           GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]);
       offset_id = offset_inst->result_id();
     }
     Instruction* ac_inst = builder.AddTernaryOp(
-        buf_uint_ptr_id, SpvOpAccessChain, buf_id,
+        buf_ptr_id, SpvOpAccessChain, buf_id,
         builder.GetUintConstantId(kDebugInputDataOffset), offset_id);
     Instruction* load_inst =
-        builder.AddUnaryOp(GetUintId(), SpvOpLoad, ac_inst->result_id());
+        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, ac_inst->result_id());
     last_value_id = load_inst->result_id();
   }
   (void)builder.AddInstruction(MakeUnique<Instruction>(
@@ -894,18 +934,21 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
 
 void InstrumentPass::InitializeInstrument() {
   output_buffer_id_ = 0;
-  buffer_uint_ptr_id_ = 0;
+  output_buffer_ptr_id_ = 0;
+  input_buffer_ptr_id_ = 0;
   output_func_id_ = 0;
   output_func_param_cnt_ = 0;
   input_buffer_id_ = 0;
   v4float_id_ = 0;
   uint_id_ = 0;
+  uint64_id_ = 0;
   v4uint_id_ = 0;
   v3uint_id_ = 0;
   bool_id_ = 0;
   void_id_ = 0;
   storage_buffer_ext_defined_ = false;
-  uint_rarr_ty_ = nullptr;
+  uint32_rarr_ty_ = nullptr;
+  uint64_rarr_ty_ = nullptr;
 
   // clear collections
   id2function_.clear();

+ 28 - 6
3rdparty/spirv-tools/source/opt/instrument_pass.h

@@ -60,6 +60,7 @@ namespace opt {
 // These are used to identify the general validation being done and map to
 // its output buffers.
 static const uint32_t kInstValidationIdBindless = 0;
+static const uint32_t kInstValidationIdBuffAddr = 1;
 
 class InstrumentPass : public Pass {
   using cbb_ptr = const BasicBlock*;
@@ -217,6 +218,9 @@ class InstrumentPass : public Pass {
   // Return id for 32-bit unsigned type
   uint32_t GetUintId();
 
+  // Return id for 32-bit unsigned type
+  uint32_t GetUint64Id();
+
   // Return id for 32-bit unsigned type
   uint32_t GetBoolId();
 
@@ -224,11 +228,20 @@ class InstrumentPass : public Pass {
   uint32_t GetVoidId();
 
   // Return pointer to type for runtime array of uint
-  analysis::Type* GetUintRuntimeArrayType(analysis::DecorationManager* deco_mgr,
-                                          analysis::TypeManager* type_mgr);
+  analysis::Type* GetUintXRuntimeArrayType(uint32_t width,
+                                           analysis::Type** rarr_ty);
+
+  // Return pointer to type for runtime array of uint
+  analysis::Type* GetUintRuntimeArrayType(uint32_t width);
 
   // Return id for buffer uint type
-  uint32_t GetBufferUintPtrId();
+  uint32_t GetOutputBufferPtrId();
+
+  // Return id for buffer uint type
+  uint32_t GetInputBufferTypeId();
+
+  // Return id for buffer uint type
+  uint32_t GetInputBufferPtrId();
 
   // Return binding for output buffer for current validation.
   uint32_t GetOutputBufferBinding();
@@ -354,8 +367,11 @@ class InstrumentPass : public Pass {
   // id for output buffer variable
   uint32_t output_buffer_id_;
 
-  // type id for output buffer element
-  uint32_t buffer_uint_ptr_id_;
+  // ptr type id for output buffer element
+  uint32_t output_buffer_ptr_id_;
+
+  // ptr type id for input buffer element
+  uint32_t input_buffer_ptr_id_;
 
   // id for debug output function
   uint32_t output_func_id_;
@@ -381,6 +397,9 @@ class InstrumentPass : public Pass {
   // id for 32-bit unsigned type
   uint32_t uint_id_;
 
+  // id for 32-bit unsigned type
+  uint32_t uint64_id_;
+
   // id for bool type
   uint32_t bool_id_;
 
@@ -394,7 +413,10 @@ class InstrumentPass : public Pass {
   bool storage_buffer_ext_defined_;
 
   // runtime array of uint type
-  analysis::Type* uint_rarr_ty_;
+  analysis::Type* uint64_rarr_ty_;
+
+  // runtime array of uint type
+  analysis::Type* uint32_rarr_ty_;
 
   // Pre-instrumentation same-block insts
   std::unordered_map<uint32_t, Instruction*> same_block_pre_;

+ 21 - 0
3rdparty/spirv-tools/source/opt/ir_builder.h

@@ -465,6 +465,23 @@ class InstructionBuilder {
     return AddInstruction(std::move(new_inst));
   }
 
+  Instruction* AddFunctionCall(uint32_t result_type, uint32_t function,
+                               const std::vector<uint32_t>& parameters) {
+    std::vector<Operand> operands;
+    operands.push_back({SPV_OPERAND_TYPE_ID, {function}});
+    for (uint32_t id : parameters) {
+      operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+    }
+
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), SpvOpFunctionCall, result_type, result_id, operands));
+    return AddInstruction(std::move(new_inst));
+  }
+
   // Inserts the new instruction before the insertion point.
   Instruction* AddInstruction(std::unique_ptr<Instruction>&& insn) {
     Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
@@ -512,6 +529,10 @@ class InstructionBuilder {
 
   // Returns true if the users requested to update |analysis|.
   inline bool IsAnalysisUpdateRequested(IRContext::Analysis analysis) const {
+    if (!GetContext()->AreAnalysesValid(analysis)) {
+      // Do not try to update something that is not built.
+      return false;
+    }
     return preserved_analyses_ & analysis;
   }
 

+ 30 - 4
3rdparty/spirv-tools/source/opt/optimizer.cpp

@@ -107,8 +107,10 @@ Optimizer& Optimizer::RegisterPass(PassToken&& p) {
 // or enable more copy propagation.
 Optimizer& Optimizer::RegisterLegalizationPasses() {
   return
-      // Remove unreachable block so that merge return works.
-      RegisterPass(CreateDeadBranchElimPass())
+      // Wrap OpKill instructions so all other code can be inlined.
+      RegisterPass(CreateWrapOpKillPass())
+          // Remove unreachable block so that merge return works.
+          .RegisterPass(CreateDeadBranchElimPass())
           // Merge the returns so we can inline.
           .RegisterPass(CreateMergeReturnPass())
           // Make sure uses and definitions are in the same function.
@@ -154,7 +156,8 @@ Optimizer& Optimizer::RegisterLegalizationPasses() {
 }
 
 Optimizer& Optimizer::RegisterPerformancePasses() {
-  return RegisterPass(CreateDeadBranchElimPass())
+  return RegisterPass(CreateWrapOpKillPass())
+      .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateMergeReturnPass())
       .RegisterPass(CreateInlineExhaustivePass())
       .RegisterPass(CreateAggressiveDCEPass())
@@ -190,7 +193,8 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
 }
 
 Optimizer& Optimizer::RegisterSizePasses() {
-  return RegisterPass(CreateDeadBranchElimPass())
+  return RegisterPass(CreateWrapOpKillPass())
+      .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateMergeReturnPass())
       .RegisterPass(CreateInlineExhaustivePass())
       .RegisterPass(CreateAggressiveDCEPass())
@@ -396,11 +400,20 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
   } else if (pass_name == "replace-invalid-opcode") {
     RegisterPass(CreateReplaceInvalidOpcodePass());
   } else if (pass_name == "inst-bindless-check") {
+    RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2));
+    RegisterPass(CreateSimplificationPass());
+    RegisterPass(CreateDeadBranchElimPass());
+    RegisterPass(CreateBlockMergePass());
+    RegisterPass(CreateAggressiveDCEPass());
+  } else if (pass_name == "inst-desc-idx-check") {
     RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2));
     RegisterPass(CreateSimplificationPass());
     RegisterPass(CreateDeadBranchElimPass());
     RegisterPass(CreateBlockMergePass());
     RegisterPass(CreateAggressiveDCEPass());
+  } else if (pass_name == "inst-buff-addr-check") {
+    RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2));
+    RegisterPass(CreateAggressiveDCEPass());
   } else if (pass_name == "simplify-instructions") {
     RegisterPass(CreateSimplificationPass());
   } else if (pass_name == "ssa-rewrite") {
@@ -477,6 +490,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
     RegisterPass(CreateDecomposeInitializedVariablesPass());
   } else if (pass_name == "graphics-robust-access") {
     RegisterPass(CreateGraphicsRobustAccessPass());
+  } else if (pass_name == "wrap-opkill") {
+    RegisterPass(CreateWrapOpKillPass());
   } else {
     Errorf(consumer(), nullptr, {},
            "Unknown flag '--%s'. Use --help for a list of valid flags",
@@ -853,6 +868,13 @@ Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
                                              input_init_enable, version));
 }
 
+Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
+                                                 uint32_t shader_id,
+                                                 uint32_t version) {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
+}
+
 Optimizer::PassToken CreateCodeSinkingPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
       MakeUnique<opt::CodeSinkingPass>());
@@ -893,4 +915,8 @@ Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
       MakeUnique<opt::DescriptorScalarReplacement>());
 }
 
+Optimizer::PassToken CreateWrapOpKillPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
+}
+
 }  // namespace spvtools

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

@@ -43,7 +43,8 @@ Pass::Status Pass::Run(IRContext* ctx) {
   if (status == Status::SuccessWithChange) {
     ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses());
   }
-  assert(ctx->IsConsistent());
+  assert((status == Status::Failure || ctx->IsConsistent()) &&
+         "An analysis in the context is out of date.");
   return status;
 }
 

+ 2 - 0
3rdparty/spirv-tools/source/opt/passes.h

@@ -43,6 +43,7 @@
 #include "source/opt/inline_exhaustive_pass.h"
 #include "source/opt/inline_opaque_pass.h"
 #include "source/opt/inst_bindless_check_pass.h"
+#include "source/opt/inst_buff_addr_check_pass.h"
 #include "source/opt/legalize_vector_shuffle_pass.h"
 #include "source/opt/licm_pass.h"
 #include "source/opt/local_access_chain_convert_pass.h"
@@ -76,5 +77,6 @@
 #include "source/opt/upgrade_memory_model.h"
 #include "source/opt/vector_dce.h"
 #include "source/opt/workaround1209.h"
+#include "source/opt/wrap_opkill.h"
 
 #endif  // SOURCE_OPT_PASSES_H_

+ 29 - 11
3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp

@@ -58,7 +58,9 @@ Pass::Status PrivateToLocalPass::Process() {
 
   modified = !variables_to_move.empty();
   for (auto p : variables_to_move) {
-    MoveVariable(p.first, p.second);
+    if (!MoveVariable(p.first, p.second)) {
+      return Status::Failure;
+    }
     localized_variables.insert(p.first->result_id());
   }
 
@@ -112,7 +114,7 @@ Function* PrivateToLocalPass::FindLocalFunction(const Instruction& inst) const {
   return target_function;
 }  // namespace opt
 
-void PrivateToLocalPass::MoveVariable(Instruction* variable,
+bool PrivateToLocalPass::MoveVariable(Instruction* variable,
                                       Function* function) {
   // The variable needs to be removed from the global section, and placed in the
   // header of the function.  First step remove from the global list.
@@ -125,6 +127,9 @@ void PrivateToLocalPass::MoveVariable(Instruction* variable,
 
   // Update the type as well.
   uint32_t new_type_id = GetNewType(variable->type_id());
+  if (new_type_id == 0) {
+    return false;
+  }
   variable->SetResultType(new_type_id);
 
   // Place the variable at the start of the first basic block.
@@ -133,7 +138,7 @@ void PrivateToLocalPass::MoveVariable(Instruction* variable,
   function->begin()->begin()->InsertBefore(move(var));
 
   // Update uses where the type may have changed.
-  UpdateUses(variable->result_id());
+  return UpdateUses(variable->result_id());
 }
 
 uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
@@ -143,7 +148,9 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
       old_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx);
   uint32_t new_type_id =
       type_mgr->FindPointerToType(pointee_type_id, SpvStorageClassFunction);
-  context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id));
+  if (new_type_id != 0) {
+    context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id));
+  }
   return new_type_id;
 }
 
@@ -168,7 +175,7 @@ bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
   }
 }
 
-void PrivateToLocalPass::UpdateUse(Instruction* inst) {
+bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
   // The cases in this switch have to match the cases in |IsValidUse|.  If we
   // don't think it is valid, the optimization will not view the variable as a
   // candidate, and therefore the use will not be updated.
@@ -179,14 +186,20 @@ void PrivateToLocalPass::UpdateUse(Instruction* inst) {
       // The type is fine because it is the type pointed to, and that does not
       // change.
       break;
-    case SpvOpAccessChain:
+    case SpvOpAccessChain: {
       context()->ForgetUses(inst);
-      inst->SetResultType(GetNewType(inst->type_id()));
+      uint32_t new_type_id = GetNewType(inst->type_id());
+      if (new_type_id == 0) {
+        return false;
+      }
+      inst->SetResultType(new_type_id);
       context()->AnalyzeUses(inst);
 
       // Update uses where the type may have changed.
-      UpdateUses(inst->result_id());
-      break;
+      if (!UpdateUses(inst->result_id())) {
+        return false;
+      }
+    } break;
     case SpvOpName:
     case SpvOpEntryPoint:  // entry points will be updated separately.
       break;
@@ -195,15 +208,20 @@ void PrivateToLocalPass::UpdateUse(Instruction* inst) {
              "Do not know how to update the type for this instruction.");
       break;
   }
+  return true;
 }
-void PrivateToLocalPass::UpdateUses(uint32_t id) {
+
+bool PrivateToLocalPass::UpdateUses(uint32_t id) {
   std::vector<Instruction*> uses;
   context()->get_def_use_mgr()->ForEachUser(
       id, [&uses](Instruction* use) { uses.push_back(use); });
 
   for (Instruction* use : uses) {
-    UpdateUse(use);
+    if (!UpdateUse(use)) {
+      return false;
+    }
   }
+  return true;
 }
 
 }  // namespace opt

+ 5 - 5
3rdparty/spirv-tools/source/opt/private_to_local_pass.h

@@ -41,8 +41,8 @@ class PrivateToLocalPass : public Pass {
 
  private:
   // Moves |variable| from the private storage class to the function storage
-  // class of |function|.
-  void MoveVariable(Instruction* variable, Function* function);
+  // class of |function|.  Returns false if the variable could not be moved.
+  bool MoveVariable(Instruction* variable, Function* function);
 
   // |inst| is an instruction declaring a varible.  If that variable is
   // referenced in a single function and all of uses are valid as defined by
@@ -58,13 +58,13 @@ class PrivateToLocalPass : public Pass {
   // Given the result id of a pointer type, |old_type_id|, this function
   // returns the id of a the same pointer type except the storage class has
   // been changed to function.  If the type does not already exist, it will be
-  // created.
+  // created.  Returns 0 if the new type could not be found or generated.
   uint32_t GetNewType(uint32_t old_type_id);
 
   // Updates |inst|, and any instruction dependent on |inst|, to reflect the
   // change of the base pointer now pointing to the function storage class.
-  void UpdateUse(Instruction* inst);
-  void UpdateUses(uint32_t id);
+  bool UpdateUse(Instruction* inst);
+  bool UpdateUses(uint32_t id);
 };
 
 }  // namespace opt

+ 125 - 65
3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp

@@ -78,36 +78,47 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
   }
 
   std::vector<Instruction*> dead;
-  if (get_def_use_mgr()->WhileEachUser(
-          inst, [this, &replacements, &dead](Instruction* user) {
-            if (!IsAnnotationInst(user->opcode())) {
-              switch (user->opcode()) {
-                case SpvOpLoad:
-                  ReplaceWholeLoad(user, replacements);
-                  dead.push_back(user);
-                  break;
-                case SpvOpStore:
-                  ReplaceWholeStore(user, replacements);
-                  dead.push_back(user);
-                  break;
-                case SpvOpAccessChain:
-                case SpvOpInBoundsAccessChain:
-                  if (ReplaceAccessChain(user, replacements))
-                    dead.push_back(user);
-                  else
-                    return false;
-                  break;
-                case SpvOpName:
-                case SpvOpMemberName:
-                  break;
-                default:
-                  assert(false && "Unexpected opcode");
-                  break;
+  bool replaced_all_uses = get_def_use_mgr()->WhileEachUser(
+      inst, [this, &replacements, &dead](Instruction* user) {
+        if (!IsAnnotationInst(user->opcode())) {
+          switch (user->opcode()) {
+            case SpvOpLoad:
+              if (ReplaceWholeLoad(user, replacements)) {
+                dead.push_back(user);
+              } else {
+                return false;
               }
-            }
-            return true;
-          }))
+              break;
+            case SpvOpStore:
+              if (ReplaceWholeStore(user, replacements)) {
+                dead.push_back(user);
+              } else {
+                return false;
+              }
+              break;
+            case SpvOpAccessChain:
+            case SpvOpInBoundsAccessChain:
+              if (ReplaceAccessChain(user, replacements))
+                dead.push_back(user);
+              else
+                return false;
+              break;
+            case SpvOpName:
+            case SpvOpMemberName:
+              break;
+            default:
+              assert(false && "Unexpected opcode");
+              break;
+          }
+        }
+        return true;
+      });
+
+  if (replaced_all_uses) {
     dead.push_back(inst);
+  } else {
+    return Status::Failure;
+  }
 
   // If there are no dead instructions to clean up, return with no changes.
   if (dead.empty()) return Status::SuccessWithoutChange;
@@ -133,7 +144,7 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
   return Status::SuccessWithChange;
 }
 
-void ScalarReplacementPass::ReplaceWholeLoad(
+bool ScalarReplacementPass::ReplaceWholeLoad(
     Instruction* load, const std::vector<Instruction*>& replacements) {
   // Replaces the load of the entire composite with a load from each replacement
   // variable followed by a composite construction.
@@ -150,6 +161,9 @@ void ScalarReplacementPass::ReplaceWholeLoad(
 
     Instruction* type = GetStorageType(var);
     uint32_t loadId = TakeNextId();
+    if (loadId == 0) {
+      return false;
+    }
     std::unique_ptr<Instruction> newLoad(
         new Instruction(context(), SpvOpLoad, type->result_id(), loadId,
                         std::initializer_list<Operand>{
@@ -168,6 +182,9 @@ void ScalarReplacementPass::ReplaceWholeLoad(
 
   // Construct a new composite.
   uint32_t compositeId = TakeNextId();
+  if (compositeId == 0) {
+    return false;
+  }
   where = load;
   std::unique_ptr<Instruction> compositeConstruct(new Instruction(
       context(), SpvOpCompositeConstruct, load->type_id(), compositeId, {}));
@@ -180,9 +197,10 @@ void ScalarReplacementPass::ReplaceWholeLoad(
   get_def_use_mgr()->AnalyzeInstDefUse(&*where);
   context()->set_instr_block(&*where, block);
   context()->ReplaceAllUsesWith(load->result_id(), compositeId);
+  return true;
 }
 
-void ScalarReplacementPass::ReplaceWholeStore(
+bool ScalarReplacementPass::ReplaceWholeStore(
     Instruction* store, const std::vector<Instruction*>& replacements) {
   // Replaces a store to the whole composite with a series of extract and stores
   // to each element.
@@ -199,6 +217,9 @@ void ScalarReplacementPass::ReplaceWholeStore(
 
     Instruction* type = GetStorageType(var);
     uint32_t extractId = TakeNextId();
+    if (extractId == 0) {
+      return false;
+    }
     std::unique_ptr<Instruction> extract(new Instruction(
         context(), SpvOpCompositeExtract, type->result_id(), extractId,
         std::initializer_list<Operand>{
@@ -224,6 +245,7 @@ void ScalarReplacementPass::ReplaceWholeStore(
     get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
     context()->set_instr_block(&*iter, block);
   }
+  return true;
 }
 
 bool ScalarReplacementPass::ReplaceAccessChain(
@@ -247,6 +269,9 @@ bool ScalarReplacementPass::ReplaceAccessChain(
       // Replace input access chain with another access chain.
       BasicBlock::iterator chainIter(chain);
       uint32_t replacementId = TakeNextId();
+      if (replacementId == 0) {
+        return false;
+      }
       std::unique_ptr<Instruction> replacementChain(new Instruction(
           context(), chain->opcode(), chain->type_id(), replacementId,
           std::initializer_list<Operand>{
@@ -329,6 +354,10 @@ void ScalarReplacementPass::TransferAnnotations(
     if (decoration == SpvDecorationInvariant ||
         decoration == SpvDecorationRestrict) {
       for (auto var : *replacements) {
+        if (var == nullptr) {
+          continue;
+        }
+
         std::unique_ptr<Instruction> annotation(
             new Instruction(context(), SpvOpDecorate, 0, 0,
                             std::initializer_list<Operand>{
@@ -350,6 +379,11 @@ void ScalarReplacementPass::CreateVariable(
     std::vector<Instruction*>* replacements) {
   uint32_t ptrId = GetOrCreatePointerType(typeId);
   uint32_t id = TakeNextId();
+
+  if (id == 0) {
+    replacements->push_back(nullptr);
+  }
+
   std::unique_ptr<Instruction> variable(new Instruction(
       context(), SpvOpVariable, ptrId, id,
       std::initializer_list<Operand>{
@@ -671,44 +705,51 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst) const {
 
 bool ScalarReplacementPass::CheckUses(const Instruction* inst,
                                       VariableStats* stats) const {
+  uint64_t max_legal_index = GetMaxLegalIndex(inst);
+
   bool ok = true;
-  get_def_use_mgr()->ForEachUse(
-      inst, [this, stats, &ok](const Instruction* user, uint32_t index) {
-        // Annotations are check as a group separately.
-        if (!IsAnnotationInst(user->opcode())) {
-          switch (user->opcode()) {
-            case SpvOpAccessChain:
-            case SpvOpInBoundsAccessChain:
-              if (index == 2u && user->NumInOperands() > 1) {
-                uint32_t id = user->GetSingleWordInOperand(1u);
-                const Instruction* opInst = get_def_use_mgr()->GetDef(id);
-                if (!IsCompileTimeConstantInst(opInst->opcode())) {
-                  ok = false;
-                } else {
-                  if (!CheckUsesRelaxed(user)) ok = false;
-                }
-                stats->num_partial_accesses++;
-              } else {
-                ok = false;
-              }
-              break;
-            case SpvOpLoad:
-              if (!CheckLoad(user, index)) ok = false;
-              stats->num_full_accesses++;
-              break;
-            case SpvOpStore:
-              if (!CheckStore(user, index)) ok = false;
-              stats->num_full_accesses++;
-              break;
-            case SpvOpName:
-            case SpvOpMemberName:
-              break;
-            default:
+  get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok](
+                                          const Instruction* user,
+                                          uint32_t index) {
+    // Annotations are check as a group separately.
+    if (!IsAnnotationInst(user->opcode())) {
+      switch (user->opcode()) {
+        case SpvOpAccessChain:
+        case SpvOpInBoundsAccessChain:
+          if (index == 2u && user->NumInOperands() > 1) {
+            uint32_t id = user->GetSingleWordInOperand(1u);
+            const Instruction* opInst = get_def_use_mgr()->GetDef(id);
+            const auto* constant =
+                context()->get_constant_mgr()->GetConstantFromInst(opInst);
+            if (!constant) {
               ok = false;
-              break;
+            } else if (constant->GetZeroExtendedValue() >= max_legal_index) {
+              ok = false;
+            } else {
+              if (!CheckUsesRelaxed(user)) ok = false;
+            }
+            stats->num_partial_accesses++;
+          } else {
+            ok = false;
           }
-        }
-      });
+          break;
+        case SpvOpLoad:
+          if (!CheckLoad(user, index)) ok = false;
+          stats->num_full_accesses++;
+          break;
+        case SpvOpStore:
+          if (!CheckStore(user, index)) ok = false;
+          stats->num_full_accesses++;
+          break;
+        case SpvOpName:
+        case SpvOpMemberName:
+          break;
+        default:
+          ok = false;
+          break;
+      }
+    }
+  });
 
   return ok;
 }
@@ -838,5 +879,24 @@ Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) {
   return null_inst;
 }
 
+uint64_t ScalarReplacementPass::GetMaxLegalIndex(
+    const Instruction* var_inst) const {
+  assert(var_inst->opcode() == SpvOpVariable &&
+         "|var_inst| must be a variable instruction.");
+  Instruction* type = GetStorageType(var_inst);
+  switch (type->opcode()) {
+    case SpvOpTypeStruct:
+      return type->NumInOperands();
+    case SpvOpTypeArray:
+      return GetArrayLength(type);
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector:
+      return GetNumElements(type);
+    default:
+      return 0;
+  }
+  return 0;
+}
+
 }  // namespace opt
 }  // namespace spvtools

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

@@ -143,7 +143,8 @@ class ScalarReplacementPass : public Pass {
   bool CheckStore(const Instruction* inst, uint32_t index) const;
 
   // Creates a variable of type |typeId| from the |index|'th element of
-  // |varInst|. The new variable is added to |replacements|.
+  // |varInst|. The new variable is added to |replacements|.  If the variable
+  // could not be created, then |nullptr| is appended to |replacements|.
   void CreateVariable(uint32_t typeId, Instruction* varInst, uint32_t index,
                       std::vector<Instruction*>* replacements);
 
@@ -187,21 +188,21 @@ class ScalarReplacementPass : public Pass {
   // Generates a load for each replacement variable and then creates a new
   // composite by combining all of the loads.
   //
-  // |load| must be a load.
-  void ReplaceWholeLoad(Instruction* load,
+  // |load| must be a load.  Returns true if successful.
+  bool ReplaceWholeLoad(Instruction* load,
                         const std::vector<Instruction*>& replacements);
 
   // Replaces the store to the entire composite.
   //
   // Generates a composite extract and store for each element in the scalarized
-  // variable from the original store data input.
-  void ReplaceWholeStore(Instruction* store,
+  // variable from the original store data input.  Returns true if successful.
+  bool ReplaceWholeStore(Instruction* store,
                          const std::vector<Instruction*>& replacements);
 
   // Replaces an access chain to the composite variable with either a direct use
   // of the appropriate replacement variable or another access chain with the
-  // replacement variable as the base and one fewer indexes. Returns false if
-  // the chain has an out of bounds access.
+  // replacement variable as the base and one fewer indexes. Returns true if
+  // successful.
   bool ReplaceAccessChain(Instruction* chain,
                           const std::vector<Instruction*>& replacements);
 
@@ -222,10 +223,16 @@ class ScalarReplacementPass : public Pass {
   // Maps type id to OpConstantNull for that type.
   std::unordered_map<uint32_t, uint32_t> type_to_null_;
 
+  // Returns the number of elements in the variable |var_inst|.
+  uint64_t GetMaxLegalIndex(const Instruction* var_inst) const;
+
+  // Returns true if |length| is larger than limit on the size of the variable
+  // that we will be willing to split.
+  bool IsLargerThanSizeLimit(uint64_t length) const;
+
   // Limit on the number of members in an object that will be replaced.
   // 0 means there is no limit.
   uint32_t max_num_elements_;
-  bool IsLargerThanSizeLimit(uint64_t length) const;
   char name_[55];
 };
 

+ 43 - 4
3rdparty/spirv-tools/source/opt/type_manager.cpp

@@ -213,6 +213,10 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
   std::unique_ptr<Instruction> typeInst;
   // TODO(1841): Handle id overflow.
   id = context()->TakeNextId();
+  if (id == 0) {
+    return 0;
+  }
+
   RegisterType(id, *type);
   switch (type->kind()) {
 #define DefineParameterlessCase(kind)                                     \
@@ -247,6 +251,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
       break;
     case Type::kVector: {
       uint32_t subtype = GetTypeInstruction(type->AsVector()->element_type());
+      if (subtype == 0) {
+        return 0;
+      }
       typeInst =
           MakeUnique<Instruction>(context(), SpvOpTypeVector, 0, id,
                                   std::initializer_list<Operand>{
@@ -257,6 +264,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
     }
     case Type::kMatrix: {
       uint32_t subtype = GetTypeInstruction(type->AsMatrix()->element_type());
+      if (subtype == 0) {
+        return 0;
+      }
       typeInst =
           MakeUnique<Instruction>(context(), SpvOpTypeMatrix, 0, id,
                                   std::initializer_list<Operand>{
@@ -268,6 +278,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
     case Type::kImage: {
       const Image* image = type->AsImage();
       uint32_t subtype = GetTypeInstruction(image->sampled_type());
+      if (subtype == 0) {
+        return 0;
+      }
       typeInst = MakeUnique<Instruction>(
           context(), SpvOpTypeImage, 0, id,
           std::initializer_list<Operand>{
@@ -289,6 +302,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
     case Type::kSampledImage: {
       uint32_t subtype =
           GetTypeInstruction(type->AsSampledImage()->image_type());
+      if (subtype == 0) {
+        return 0;
+      }
       typeInst = MakeUnique<Instruction>(
           context(), SpvOpTypeSampledImage, 0, id,
           std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
@@ -296,6 +312,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
     }
     case Type::kArray: {
       uint32_t subtype = GetTypeInstruction(type->AsArray()->element_type());
+      if (subtype == 0) {
+        return 0;
+      }
       typeInst = MakeUnique<Instruction>(
           context(), SpvOpTypeArray, 0, id,
           std::initializer_list<Operand>{
@@ -306,6 +325,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
     case Type::kRuntimeArray: {
       uint32_t subtype =
           GetTypeInstruction(type->AsRuntimeArray()->element_type());
+      if (subtype == 0) {
+        return 0;
+      }
       typeInst = MakeUnique<Instruction>(
           context(), SpvOpTypeRuntimeArray, 0, id,
           std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
@@ -315,7 +337,11 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
       std::vector<Operand> ops;
       const Struct* structTy = type->AsStruct();
       for (auto ty : structTy->element_types()) {
-        ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {GetTypeInstruction(ty)}));
+        uint32_t member_type_id = GetTypeInstruction(ty);
+        if (member_type_id == 0) {
+          return 0;
+        }
+        ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {member_type_id}));
       }
       typeInst =
           MakeUnique<Instruction>(context(), SpvOpTypeStruct, 0, id, ops);
@@ -337,6 +363,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
     case Type::kPointer: {
       const Pointer* pointer = type->AsPointer();
       uint32_t subtype = GetTypeInstruction(pointer->pointee_type());
+      if (subtype == 0) {
+        return 0;
+      }
       typeInst = MakeUnique<Instruction>(
           context(), SpvOpTypePointer, 0, id,
           std::initializer_list<Operand>{
@@ -348,10 +377,17 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
     case Type::kFunction: {
       std::vector<Operand> ops;
       const Function* function = type->AsFunction();
-      ops.push_back(Operand(SPV_OPERAND_TYPE_ID,
-                            {GetTypeInstruction(function->return_type())}));
+      uint32_t return_type_id = GetTypeInstruction(function->return_type());
+      if (return_type_id == 0) {
+        return 0;
+      }
+      ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {return_type_id}));
       for (auto ty : function->param_types()) {
-        ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {GetTypeInstruction(ty)}));
+        uint32_t paramater_type_id = GetTypeInstruction(ty);
+        if (paramater_type_id == 0) {
+          return 0;
+        }
+        ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {paramater_type_id}));
       }
       typeInst =
           MakeUnique<Instruction>(context(), SpvOpTypeFunction, 0, id, ops);
@@ -594,6 +630,9 @@ void TypeManager::RegisterType(uint32_t id, const Type& type) {
 
 Type* TypeManager::GetRegisteredType(const Type* type) {
   uint32_t id = GetTypeInstruction(type);
+  if (id == 0) {
+    return nullptr;
+  }
   return GetType(id);
 }
 

+ 5 - 2
3rdparty/spirv-tools/source/opt/type_manager.h

@@ -101,7 +101,8 @@ class TypeManager {
   std::pair<Type*, std::unique_ptr<Pointer>> GetTypeAndPointerType(
       uint32_t id, SpvStorageClass sc) const;
 
-  // Returns an id for a declaration representing |type|.
+  // Returns an id for a declaration representing |type|.  Returns 0 if the type
+  // does not exists, and could not be generated.
   //
   // If |type| is registered, then the registered id is returned. Otherwise,
   // this function recursively adds type and annotation instructions as
@@ -109,7 +110,8 @@ class TypeManager {
   uint32_t GetTypeInstruction(const Type* type);
 
   // Find pointer to type and storage in module, return its resultId.  If it is
-  // not found, a new type is created, and its id is returned.
+  // not found, a new type is created, and its id is returned.  Returns 0 if the
+  // type could not be created.
   uint32_t FindPointerToType(uint32_t type_id, SpvStorageClass storage_class);
 
   // Registers |id| to |type|.
@@ -118,6 +120,7 @@ class TypeManager {
   // unchanged.
   void RegisterType(uint32_t id, const Type& type);
 
+  // Return the registered type object that is the same as |type|.
   Type* GetRegisteredType(const Type* type);
 
   // Removes knowledge of |id| from the manager.

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

@@ -571,10 +571,10 @@ void Pointer::GetExtraHashWords(std::vector<uint32_t>* words,
 
 void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; }
 
-Function::Function(Type* ret_type, const std::vector<const Type*>& params)
+Function::Function(const Type* ret_type, const std::vector<const Type*>& params)
     : Type(kFunction), return_type_(ret_type), param_types_(params) {}
 
-Function::Function(Type* ret_type, std::vector<const Type*>& params)
+Function::Function(const Type* ret_type, std::vector<const Type*>& params)
     : Type(kFunction), return_type_(ret_type), param_types_(params) {}
 
 bool Function::IsSameImpl(const Type* that, IsSameCache* seen) const {

+ 2 - 2
3rdparty/spirv-tools/source/opt/types.h

@@ -520,8 +520,8 @@ class Pointer : public Type {
 
 class Function : public Type {
  public:
-  Function(Type* ret_type, const std::vector<const Type*>& params);
-  Function(Type* ret_type, std::vector<const Type*>& params);
+  Function(const Type* ret_type, const std::vector<const Type*>& params);
+  Function(const Type* ret_type, std::vector<const Type*>& params);
   Function(const Function&) = default;
 
   std::string str() const override;

+ 146 - 0
3rdparty/spirv-tools/source/opt/wrap_opkill.cpp

@@ -0,0 +1,146 @@
+// 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/opt/wrap_opkill.h"
+
+#include "ir_builder.h"
+
+namespace spvtools {
+namespace opt {
+
+Pass::Status WrapOpKill::Process() {
+  bool modified = false;
+
+  for (auto& func : *get_module()) {
+    bool successful = func.WhileEachInst([this, &modified](Instruction* inst) {
+      if (inst->opcode() == SpvOpKill) {
+        modified = true;
+        if (!ReplaceWithFunctionCall(inst)) {
+          return false;
+        }
+      }
+      return true;
+    });
+
+    if (!successful) {
+      return Status::Failure;
+    }
+  }
+
+  if (opkill_function_ != nullptr) {
+    assert(modified &&
+           "The function should only be generated if something was modified.");
+    context()->AddFunction(std::move(opkill_function_));
+  }
+  return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
+}
+
+bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
+  assert(inst->opcode() == SpvOpKill &&
+         "|inst| must be an OpKill instruction.");
+  InstructionBuilder ir_builder(
+      context(), inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  uint32_t func_id = GetOpKillFuncId();
+  if (func_id == 0) {
+    return false;
+  }
+  if (ir_builder.AddFunctionCall(GetVoidTypeId(), func_id, {}) == nullptr) {
+    return false;
+  }
+  ir_builder.AddUnreachable();
+  context()->KillInst(inst);
+  return true;
+}
+
+uint32_t WrapOpKill::GetVoidTypeId() {
+  if (void_type_id_ != 0) {
+    return void_type_id_;
+  }
+
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::Void void_type;
+  void_type_id_ = type_mgr->GetTypeInstruction(&void_type);
+  return void_type_id_;
+}
+
+uint32_t WrapOpKill::GetVoidFunctionTypeId() {
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::Void void_type;
+  const analysis::Type* registered_void_type =
+      type_mgr->GetRegisteredType(&void_type);
+
+  analysis::Function func_type(registered_void_type, {});
+  return type_mgr->GetTypeInstruction(&func_type);
+}
+
+uint32_t WrapOpKill::GetOpKillFuncId() {
+  if (opkill_function_ != nullptr) {
+    return opkill_function_->result_id();
+  }
+
+  uint32_t opkill_func_id = TakeNextId();
+  if (opkill_func_id == 0) {
+    return 0;
+  }
+
+  // Generate the function start instruction
+  std::unique_ptr<Instruction> func_start(new Instruction(
+      context(), SpvOpFunction, GetVoidTypeId(), opkill_func_id, {}));
+  func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}});
+  func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}});
+  opkill_function_.reset(new Function(std::move(func_start)));
+
+  // Generate the function end instruction
+  std::unique_ptr<Instruction> func_end(
+      new Instruction(context(), SpvOpFunctionEnd, 0, 0, {}));
+  opkill_function_->SetFunctionEnd(std::move(func_end));
+
+  // Create the one basic block for the function.
+  uint32_t lab_id = TakeNextId();
+  if (lab_id == 0) {
+    return 0;
+  }
+  std::unique_ptr<Instruction> label_inst(
+      new Instruction(context(), SpvOpLabel, 0, lab_id, {}));
+  std::unique_ptr<BasicBlock> bb(new BasicBlock(std::move(label_inst)));
+
+  // Add the OpKill to the basic block
+  std::unique_ptr<Instruction> kill_inst(
+      new Instruction(context(), SpvOpKill, 0, 0, {}));
+  bb->AddInstruction(std::move(kill_inst));
+
+  // Add the bb to the function
+  opkill_function_->AddBasicBlock(std::move(bb));
+
+  // Add the function to the module.
+  if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
+    opkill_function_->ForEachInst(
+        [this](Instruction* inst) { context()->AnalyzeDefUse(inst); });
+  }
+
+  if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
+    for (BasicBlock& basic_block : *opkill_function_) {
+      context()->set_instr_block(basic_block.GetLabelInst(), &basic_block);
+      for (Instruction& inst : basic_block) {
+        context()->set_instr_block(&inst, &basic_block);
+      }
+    }
+  }
+
+  return opkill_function_->result_id();
+}
+
+}  // namespace opt
+}  // namespace spvtools

+ 72 - 0
3rdparty/spirv-tools/source/opt/wrap_opkill.h

@@ -0,0 +1,72 @@
+// 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_OPT_WRAP_OPKILL_H_
+#define SOURCE_OPT_WRAP_OPKILL_H_
+
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// Documented in optimizer.hpp
+class WrapOpKill : public Pass {
+ public:
+  WrapOpKill() : void_type_id_(0) {}
+
+  const char* name() const override { return "wrap-opkill"; }
+
+  Status Process() override;
+
+  IRContext::Analysis GetPreservedAnalyses() override {
+    return IRContext::kAnalysisDefUse |
+           IRContext::kAnalysisInstrToBlockMapping |
+           IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
+           IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId |
+           IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants |
+           IRContext::kAnalysisTypes;
+  }
+
+ private:
+  // Replaces the OpKill instruction |inst| with a function call to a function
+  // that contains a single instruction, which is OpKill.  An OpUnreachable
+  // instruction will be placed after the function call.  Return true if
+  // successful.
+  bool ReplaceWithFunctionCall(Instruction* inst);
+
+  // Returns the id of the void type.
+  uint32_t GetVoidTypeId();
+
+  // Returns the id of the function type for a void function with no parameters.
+  uint32_t GetVoidFunctionTypeId();
+
+  // Return the id of a function that has return type void, has no parameters,
+  // and contains a single instruction, which is an OpKill.  Returns 0 if the
+  // function could not be generated.
+  uint32_t GetOpKillFuncId();
+
+  // The id of the void type.  If its value is 0, then the void type has not
+  // been found or created yet.
+  uint32_t void_type_id_;
+
+  // The function that is a single instruction, which is an OpKill.  The
+  // function has a void return type and takes no parameters. If the function is
+  // |nullptr|, then the function has not been generated.
+  std::unique_ptr<Function> opkill_function_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_WRAP_OPKILL_H_

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

@@ -30,6 +30,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
         remove_function_reduction_opportunity.h
         remove_function_reduction_opportunity_finder.h
         remove_opname_instruction_reduction_opportunity_finder.h
+        remove_relaxed_precision_decoration_opportunity_finder.h
         remove_selection_reduction_opportunity.h
         remove_selection_reduction_opportunity_finder.h
         remove_unreferenced_instruction_reduction_opportunity_finder.h
@@ -56,6 +57,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
         remove_function_reduction_opportunity.cpp
         remove_function_reduction_opportunity_finder.cpp
         remove_instruction_reduction_opportunity.cpp
+        remove_relaxed_precision_decoration_opportunity_finder.cpp
         remove_selection_reduction_opportunity.cpp
         remove_selection_reduction_opportunity_finder.cpp
         remove_unreferenced_instruction_reduction_opportunity_finder.cpp

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

@@ -25,6 +25,7 @@
 #include "source/reduce/remove_block_reduction_opportunity_finder.h"
 #include "source/reduce/remove_function_reduction_opportunity_finder.h"
 #include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
+#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
 #include "source/reduce/remove_selection_reduction_opportunity_finder.h"
 #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
 #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
@@ -175,6 +176,8 @@ Reducer::ReductionResultStatus Reducer::Run(
 void Reducer::AddDefaultReductionPasses() {
   AddReductionPass(spvtools::MakeUnique<
                    RemoveOpNameInstructionReductionOpportunityFinder>());
+  AddReductionPass(spvtools::MakeUnique<
+                   RemoveRelaxedPrecisionDecorationOpportunityFinder>());
   AddReductionPass(
       spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
   AddReductionPass(

+ 49 - 0
3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp

@@ -0,0 +1,49 @@
+// Copyright (c) 2018 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
+
+#include "source/reduce/remove_instruction_reduction_opportunity.h"
+
+namespace spvtools {
+namespace reduce {
+
+std::vector<std::unique_ptr<ReductionOpportunity>>
+RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities(
+    opt::IRContext* context) const {
+  std::vector<std::unique_ptr<ReductionOpportunity>> result;
+
+  // Consider all annotation instructions
+  for (auto& inst : context->module()->annotations()) {
+    // We are interested in removing instructions of the form:
+    //   SpvOpDecorate %id RelaxedPrecision
+    // and
+    //   SpvOpMemberDecorate %id member RelaxedPrecision
+    if ((inst.opcode() == SpvOpDecorate &&
+         inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) ||
+        (inst.opcode() == SpvOpMemberDecorate &&
+         inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) {
+      result.push_back(
+          MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+    }
+  }
+  return result;
+}
+
+std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const {
+  return "RemoveRelaxedPrecisionDecorationOpportunityFinder";
+}
+
+}  // namespace reduce
+}  // namespace spvtools

+ 36 - 0
3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h

@@ -0,0 +1,36 @@
+// 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_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
+#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
+
+#include "source/reduce/reduction_opportunity_finder.h"
+
+namespace spvtools {
+namespace reduce {
+
+// A finder for opportunities to remove relaxed precision decorations.
+class RemoveRelaxedPrecisionDecorationOpportunityFinder
+    : public ReductionOpportunityFinder {
+ public:
+  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
+      opt::IRContext* context) const override;
+
+  std::string GetName() const override;
+};
+
+}  // namespace reduce
+}  // namespace spvtools
+
+#endif  // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_

+ 1 - 3
3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp

@@ -21,11 +21,9 @@
 namespace spvtools {
 namespace reduce {
 
-using opt::IRContext;
-
 std::vector<std::unique_ptr<ReductionOpportunity>>
 RemoveUnreferencedInstructionReductionOpportunityFinder::
-    GetAvailableOpportunities(IRContext* context) const {
+    GetAvailableOpportunities(opt::IRContext* context) const {
   std::vector<std::unique_ptr<ReductionOpportunity>> result;
 
   for (auto& function : *context->module()) {

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

@@ -116,15 +116,13 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _,
             inst->GetOperandAs<uint32_t>(constituent_index);
         const auto constituent = _.FindDef(constituent_id);
         if (!constituent ||
-            !(SpvOpConstantComposite == constituent->opcode() ||
-              SpvOpSpecConstantComposite == constituent->opcode() ||
-              SpvOpUndef == constituent->opcode())) {
+            !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
           // The message says "... or undef" because the spec does not say
           // undef is a constant.
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << opcode_name << " Constituent <id> '"
                  << _.getIdName(constituent_id)
-                 << "' is not a constant composite or undef.";
+                 << "' is not a constant or undef.";
         }
         const auto vector = _.FindDef(constituent->type_id());
         if (!vector) {

+ 17 - 17
3rdparty/spirv-tools/source/val/validate_extensions.cpp

@@ -923,8 +923,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
 
       case OpenCLLIB::Fract:
       case OpenCLLIB::Modf:
-      case OpenCLLIB::Sincos:
-      case OpenCLLIB::Remquo: {
+      case OpenCLLIB::Sincos: {
         if (!_.IsFloatScalarOrVectorType(result_type)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << ext_inst_name() << ": "
@@ -939,24 +938,14 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
                     "3, 4, 8 or 16 components";
         }
 
-        uint32_t operand_index = 4;
-        const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
         if (result_type != x_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << ext_inst_name() << ": "
                  << "expected type of operand X to be equal to Result Type";
         }
 
-        if (ext_inst_key == OpenCLLIB::Remquo) {
-          const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
-          if (result_type != y_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected type of operand Y to be equal to Result Type";
-          }
-        }
-
-        const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++);
+        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
         uint32_t p_storage_class = 0;
         uint32_t p_data_type = 0;
         if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
@@ -985,7 +974,8 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
       }
 
       case OpenCLLIB::Frexp:
-      case OpenCLLIB::Lgamma_r: {
+      case OpenCLLIB::Lgamma_r:
+      case OpenCLLIB::Remquo: {
         if (!_.IsFloatScalarOrVectorType(result_type)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << ext_inst_name() << ": "
@@ -1000,14 +990,24 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
                     "3, 4, 8 or 16 components";
         }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        uint32_t operand_index = 4;
+        const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
         if (result_type != x_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << ext_inst_name() << ": "
                  << "expected type of operand X to be equal to Result Type";
         }
 
-        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+        if (ext_inst_key == OpenCLLIB::Remquo) {
+          const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
+          if (result_type != y_type) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << ext_inst_name() << ": "
+                   << "expected type of operand Y to be equal to Result Type";
+          }
+        }
+
+        const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++);
         uint32_t p_storage_class = 0;
         uint32_t p_data_type = 0;
         if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {

+ 40 - 36
3rdparty/spirv-tools/test/link/type_match_test.cpp

@@ -84,44 +84,46 @@ using TypeMatch = spvtest::LinkerTest;
   MatchF(T##Of##A##Of##B, \
          MatchPart1(B, b) MatchPart2(A, a, b) MatchPart2(T, type, a))
 
+// clang-format off
 // Basic types
-Match1(Int);
-Match1(Float);
-Match1(Opaque);
-Match1(Sampler);
-Match1(Event);
-Match1(DeviceEvent);
-Match1(ReserveId);
-Match1(Queue);
-Match1(Pipe);
-Match1(PipeStorage);
-Match1(NamedBarrier);
+Match1(Int)
+Match1(Float)
+Match1(Opaque)
+Match1(Sampler)
+Match1(Event)
+Match1(DeviceEvent)
+Match1(ReserveId)
+Match1(Queue)
+Match1(Pipe)
+Match1(PipeStorage)
+Match1(NamedBarrier)
 
 // Simpler (restricted) compound types
-Match2(Vector, Float);
-Match3(Matrix, Vector, Float);
-Match2(Image, Float);
+Match2(Vector, Float)
+Match3(Matrix, Vector, Float)
+Match2(Image, Float)
 
 // Unrestricted compound types
 #define MatchCompounds1(A) \
-  Match2(RuntimeArray, A); \
-  Match2(Struct, A);       \
-  Match2(Pointer, A);      \
-  Match2(Function, A);     \
-  Match2(Array, A);
+  Match2(RuntimeArray, A)  \
+  Match2(Struct, A)        \
+  Match2(Pointer, A)       \
+  Match2(Function, A)      \
+  Match2(Array, A)
 #define MatchCompounds2(A, B) \
-  Match3(RuntimeArray, A, B); \
-  Match3(Struct, A, B);       \
-  Match3(Pointer, A, B);      \
-  Match3(Function, A, B);     \
-  Match3(Array, A, B);
-
-MatchCompounds1(Float);
-MatchCompounds2(Array, Float);
-MatchCompounds2(RuntimeArray, Float);
-MatchCompounds2(Struct, Float);
-MatchCompounds2(Pointer, Float);
-MatchCompounds2(Function, Float);
+  Match3(RuntimeArray, A, B)  \
+  Match3(Struct, A, B)        \
+  Match3(Pointer, A, B)       \
+  Match3(Function, A, B)      \
+  Match3(Array, A, B)
+
+MatchCompounds1(Float)
+MatchCompounds2(Array, Float)
+MatchCompounds2(RuntimeArray, Float)
+MatchCompounds2(Struct, Float)
+MatchCompounds2(Pointer, Float)
+MatchCompounds2(Function, Float)
+// clang-format on
 
 // ForwardPointer tests, which don't fit into the previous mold
 #define MatchFpF(N, CODE)                                             \
@@ -134,11 +136,13 @@ MatchCompounds2(Function, Float);
 #define MatchFp2(T, A) \
   MatchFpF(ForwardPointerOf##T, MatchPart1(A, a) MatchPart2(T, realtype, a))
 
-MatchFp1(Float);
-MatchFp2(Array, Float);
-MatchFp2(RuntimeArray, Float);
-MatchFp2(Struct, Float);
-MatchFp2(Function, Float);
+    // clang-format off
+MatchFp1(Float)
+MatchFp2(Array, Float)
+MatchFp2(RuntimeArray, Float)
+MatchFp2(Struct, Float)
+MatchFp2(Function, Float)
+// clang-format on
 
 }  // namespace
 }  // namespace spvtools

+ 2 - 0
3rdparty/spirv-tools/test/opt/CMakeLists.txt

@@ -52,6 +52,7 @@ add_spvtools_unittest(TARGET opt
        inline_test.cpp
        insert_extract_elim_test.cpp
        inst_bindless_check_test.cpp
+       inst_buff_addr_check_test.cpp
        instruction_list_test.cpp
        instruction_test.cpp
        ir_builder.cpp
@@ -97,6 +98,7 @@ add_spvtools_unittest(TARGET opt
        value_table_test.cpp
        vector_dce_test.cpp
        workaround1209_test.cpp
+       wrap_opkill_test.cpp
   LIBS SPIRV-Tools-opt
   PCH_FILE pch_test_opt
 )

+ 61 - 0
3rdparty/spirv-tools/test/opt/desc_sroa_test.cpp

@@ -204,6 +204,67 @@ TEST_F(DescriptorScalarReplacementTest, ExpandSSBO) {
   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
 }
 
+TEST_F(DescriptorScalarReplacementTest, NameNewVariables) {
+  // Checks that if the original variable has a name, then the new variables
+  // will have a name derived from that name.
+  const std::string text = R"(
+; CHECK: OpName [[var1:%\w+]] "SSBO[0]"
+; CHECK: OpName [[var2:%\w+]] "SSBO[1]"
+; CHECK: OpDecorate [[var1]] DescriptorSet 0
+; CHECK: OpDecorate [[var1]] Binding 0
+; CHECK: OpDecorate [[var2]] DescriptorSet 0
+; CHECK: OpDecorate [[var2]] Binding 1
+; CHECK: OpTypeStruct
+; CHECK: [[struct_type:%\w+]] = OpTypeStruct
+; CHECK: [[ptr_type:%\w+]] = OpTypePointer Uniform [[struct_type]]
+; CHECK: [[var1]] = OpVariable [[ptr_type]] Uniform
+; CHECK: [[var2]] = OpVariable [[ptr_type]] Uniform
+; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var1]] %uint_0 %uint_0 %uint_0
+; CHECK: OpLoad %v4float [[ac1]]
+; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var2]] %uint_0 %uint_0 %uint_0
+; CHECK: OpLoad %v4float [[ac2]]
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource HLSL 600
+               OpName %buffers "SSBO"
+               OpDecorate %buffers DescriptorSet 0
+               OpDecorate %buffers Binding 0
+               OpMemberDecorate %S 0 Offset 0
+               OpDecorate %_runtimearr_S ArrayStride 16
+               OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0
+               OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable
+               OpDecorate %type_StructuredBuffer_S BufferBlock
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+      %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+          %S = OpTypeStruct %v4float
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_arr_type_StructuredBuffer_S_uint_2 = OpTypeArray %type_StructuredBuffer_S %uint_2
+%_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 = OpTypePointer Uniform %_arr_type_StructuredBuffer_S_uint_2
+%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S
+       %void = OpTypeVoid
+         %19 = OpTypeFunction %void
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+    %buffers = OpVariable %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 Uniform
+       %main = OpFunction %void None %19
+         %21 = OpLabel
+         %22 = OpAccessChain %_ptr_Uniform_v4float %buffers %uint_0 %uint_0 %uint_0 %uint_0
+         %23 = OpLoad %v4float %22
+         %24 = OpAccessChain %_ptr_Uniform_type_StructuredBuffer_S %buffers %uint_1
+         %25 = OpAccessChain %_ptr_Uniform_v4float %24 %uint_0 %uint_0 %uint_0
+         %26 = OpLoad %v4float %25
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools

+ 620 - 0
3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp

@@ -0,0 +1,620 @@
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 LunarG 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.
+
+// Bindless Check Instrumentation Tests.
+// Tests ending with V2 use version 2 record format.
+
+#include <string>
+#include <vector>
+
+#include "test/opt/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using InstBuffAddrTest = PassTest<::testing::Test>;
+
+TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) {
+  // #version 450
+  // #extension GL_EXT_buffer_reference : enable
+  //
+  // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
+  //
+  // layout(set = 0, binding = 0) uniform ufoo {
+  //     bufStruct data;
+  //     uint offset;
+  // } u_info;
+  //
+  // layout(buffer_reference, std140) buffer bufStruct {
+  //     layout(offset = 0) int a[2];
+  //     layout(offset = 32) int b;
+  // };
+  //
+  // void main() {
+  //     u_info.data.b = 0xca7;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability PhysicalStorageBufferAddressesEXT
+OpExtension "SPV_EXT_physical_storage_buffer"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %ufoo "ufoo"
+OpMemberName %ufoo 0 "data"
+OpMemberName %ufoo 1 "offset"
+OpName %bufStruct "bufStruct"
+OpMemberName %bufStruct 0 "a"
+OpMemberName %bufStruct 1 "b"
+OpName %u_info "u_info"
+OpMemberDecorate %ufoo 0 Offset 0
+OpMemberDecorate %ufoo 1 Offset 8
+OpDecorate %ufoo Block
+OpDecorate %_arr_int_uint_2 ArrayStride 16
+OpMemberDecorate %bufStruct 0 Offset 0
+OpMemberDecorate %bufStruct 1 Offset 32
+OpDecorate %bufStruct Block
+OpDecorate %u_info DescriptorSet 0
+OpDecorate %u_info Binding 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
+%uint = OpTypeInt 32 0
+%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
+%int = OpTypeInt 32 1
+%uint_2 = OpConstant %uint 2
+%_arr_int_uint_2 = OpTypeArray %int %uint_2
+%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
+%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
+%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
+%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
+%int_1 = OpConstant %int 1
+%int_3239 = OpConstant %int 3239
+%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability Int64
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %ufoo "ufoo"
+OpMemberName %ufoo 0 "data"
+OpMemberName %ufoo 1 "offset"
+OpName %bufStruct "bufStruct"
+OpMemberName %bufStruct 0 "a"
+OpMemberName %bufStruct 1 "b"
+OpName %u_info "u_info"
+OpMemberDecorate %ufoo 0 Offset 0
+OpMemberDecorate %ufoo 1 Offset 8
+OpDecorate %ufoo Block
+OpDecorate %_arr_int_uint_2 ArrayStride 16
+OpMemberDecorate %bufStruct 0 Offset 0
+OpMemberDecorate %bufStruct 1 Offset 32
+OpDecorate %bufStruct Block
+OpDecorate %u_info DescriptorSet 0
+OpDecorate %u_info Binding 0
+OpDecorate %_runtimearr_ulong ArrayStride 8
+OpDecorate %_struct_39 Block
+OpMemberDecorate %_struct_39 0 Offset 0
+OpDecorate %41 DescriptorSet 7
+OpDecorate %41 Binding 2
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_77 Block
+OpMemberDecorate %_struct_77 0 Offset 0
+OpMemberDecorate %_struct_77 1 Offset 4
+OpDecorate %79 DescriptorSet 7
+OpDecorate %79 Binding 0
+OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
+%uint = OpTypeInt 32 0
+%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
+%int = OpTypeInt 32 1
+%uint_2 = OpConstant %uint 2
+%_arr_int_uint_2 = OpTypeArray %int %uint_2
+%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
+%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
+%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
+%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
+%int_1 = OpConstant %int 1
+%int_3239 = OpConstant %int 3239
+%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%ulong = OpTypeInt 64 0
+%uint_4 = OpConstant %uint 4
+%bool = OpTypeBool
+%28 = OpTypeFunction %bool %ulong %uint
+%uint_1 = OpConstant %uint 1
+%_runtimearr_ulong = OpTypeRuntimeArray %ulong
+%_struct_39 = OpTypeStruct %_runtimearr_ulong
+%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
+%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
+%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
+%uint_0 = OpConstant %uint 0
+%uint_32 = OpConstant %uint 32
+%70 = OpTypeFunction %void %uint %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_77 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77
+%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%uint_10 = OpConstant %uint 10
+%uint_23 = OpConstant %uint 23
+%uint_5 = OpConstant %uint 5
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_8 = OpConstant %uint 8
+%uint_9 = OpConstant %uint 9
+%uint_48 = OpConstant %uint 48
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
+%18 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %17
+%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %18 %int_1
+OpStore %22 %int_3239 Aligned 16
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %8
+%19 = OpLabel
+%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
+%21 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %20
+%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %21 %int_1
+%24 = OpConvertPtrToU %ulong %22
+%61 = OpFunctionCall %bool %26 %24 %uint_4
+OpSelectionMerge %62 None
+OpBranchConditional %61 %63 %64
+%63 = OpLabel
+OpStore %22 %int_3239 Aligned 16
+OpBranch %62
+%64 = OpLabel
+%65 = OpUConvert %uint %24
+%67 = OpShiftRightLogical %ulong %24 %uint_32
+%68 = OpUConvert %uint %67
+%124 = OpFunctionCall %void %69 %uint_48 %uint_2 %65 %68
+OpBranch %62
+%62 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%26 = OpFunction %bool None %28
+%29 = OpFunctionParameter %ulong
+%30 = OpFunctionParameter %uint
+%31 = OpLabel
+OpBranch %32
+%32 = OpLabel
+%34 = OpPhi %uint %uint_1 %31 %35 %33
+OpLoopMerge %37 %33 None
+OpBranch %33
+%33 = OpLabel
+%35 = OpIAdd %uint %34 %uint_1
+%44 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %35
+%45 = OpLoad %ulong %44
+%46 = OpUGreaterThan %bool %45 %29
+OpBranchConditional %46 %37 %32
+%37 = OpLabel
+%47 = OpISub %uint %35 %uint_1
+%48 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %47
+%49 = OpLoad %ulong %48
+%50 = OpISub %ulong %29 %49
+%51 = OpUConvert %ulong %30
+%52 = OpIAdd %ulong %50 %51
+%53 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %uint_0
+%54 = OpLoad %ulong %53
+%55 = OpUConvert %uint %54
+%56 = OpISub %uint %47 %uint_1
+%57 = OpIAdd %uint %56 %55
+%58 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %57
+%59 = OpLoad %ulong %58
+%60 = OpULessThanEqual %bool %52 %59
+OpReturnValue %60
+OpFunctionEnd
+%69 = OpFunction %void None %70
+%71 = OpFunctionParameter %uint
+%72 = OpFunctionParameter %uint
+%73 = OpFunctionParameter %uint
+%74 = OpFunctionParameter %uint
+%75 = OpLabel
+%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0
+%83 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10
+%84 = OpIAdd %uint %83 %uint_10
+%85 = OpArrayLength %uint %79 1
+%86 = OpULessThanEqual %bool %84 %85
+OpSelectionMerge %87 None
+OpBranchConditional %86 %88 %87
+%88 = OpLabel
+%89 = OpIAdd %uint %83 %uint_0
+%90 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %89
+OpStore %90 %uint_10
+%92 = OpIAdd %uint %83 %uint_1
+%93 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %92
+OpStore %93 %uint_23
+%94 = OpIAdd %uint %83 %uint_2
+%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94
+OpStore %95 %71
+%98 = OpIAdd %uint %83 %uint_3
+%99 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %98
+OpStore %99 %uint_5
+%103 = OpLoad %v3uint %gl_GlobalInvocationID
+%104 = OpCompositeExtract %uint %103 0
+%105 = OpCompositeExtract %uint %103 1
+%106 = OpCompositeExtract %uint %103 2
+%107 = OpIAdd %uint %83 %uint_4
+%108 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %107
+OpStore %108 %104
+%109 = OpIAdd %uint %83 %uint_5
+%110 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %109
+OpStore %110 %105
+%112 = OpIAdd %uint %83 %uint_6
+%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112
+OpStore %113 %106
+%115 = OpIAdd %uint %83 %uint_7
+%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115
+OpStore %116 %72
+%118 = OpIAdd %uint %83 %uint_8
+%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118
+OpStore %119 %73
+%121 = OpIAdd %uint %83 %uint_9
+%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121
+OpStore %122 %74
+OpBranch %87
+%87 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBuffAddrCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, 2u);
+}
+
+TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
+  // #version 450
+  // #extension GL_EXT_buffer_reference : enable
+
+  // // forward reference
+  // layout(buffer_reference) buffer blockType;
+
+  // layout(buffer_reference, std430, buffer_reference_align = 16) buffer
+  // blockType {
+  //   int x;
+  //   blockType next;
+  // };
+
+  // layout(std430) buffer rootBlock {
+  //   blockType root;
+  // } r;
+
+  // void main()
+  // {
+  //   blockType b = r.root;
+  //   b = b.next;
+  //   b.x = 531;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability PhysicalStorageBufferAddressesEXT
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %blockType "blockType"
+OpMemberName %blockType 0 "x"
+OpMemberName %blockType 1 "next"
+OpName %rootBlock "rootBlock"
+OpMemberName %rootBlock 0 "root"
+OpName %r "r"
+OpMemberDecorate %blockType 0 Offset 0
+OpMemberDecorate %blockType 1 Offset 8
+OpDecorate %blockType Block
+OpMemberDecorate %rootBlock 0 Offset 0
+OpDecorate %rootBlock Block
+OpDecorate %r DescriptorSet 0
+OpDecorate %r Binding 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
+%int = OpTypeInt 32 1
+%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
+%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
+%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
+%int_0 = OpConstant %int 0
+%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
+%int_1 = OpConstant %int 1
+%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
+%int_531 = OpConstant %int 531
+%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability Int64
+OpCapability Int64
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %blockType "blockType"
+OpMemberName %blockType 0 "x"
+OpMemberName %blockType 1 "next"
+OpName %rootBlock "rootBlock"
+OpMemberName %rootBlock 0 "root"
+OpName %r "r"
+OpMemberDecorate %blockType 0 Offset 0
+OpMemberDecorate %blockType 1 Offset 8
+OpDecorate %blockType Block
+OpMemberDecorate %rootBlock 0 Offset 0
+OpDecorate %rootBlock Block
+OpDecorate %r DescriptorSet 0
+OpDecorate %r Binding 0
+OpDecorate %_runtimearr_ulong ArrayStride 8
+OpDecorate %_struct_45 Block
+OpMemberDecorate %_struct_45 0 Offset 0
+OpDecorate %47 DescriptorSet 7
+OpDecorate %47 Binding 2
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_84 Block
+OpMemberDecorate %_struct_84 0 Offset 0
+OpMemberDecorate %_struct_84 1 Offset 4
+OpDecorate %86 DescriptorSet 7
+OpDecorate %86 Binding 0
+OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
+%int = OpTypeInt 32 1
+%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
+%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
+%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
+%int_0 = OpConstant %int 0
+%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
+%int_1 = OpConstant %int 1
+%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
+%int_531 = OpConstant %int 531
+%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%uint = OpTypeInt 32 0
+%uint_2 = OpConstant %uint 2
+%ulong = OpTypeInt 64 0
+%uint_8 = OpConstant %uint 8
+%bool = OpTypeBool
+%34 = OpTypeFunction %bool %ulong %uint
+%uint_1 = OpConstant %uint 1
+%_runtimearr_ulong = OpTypeRuntimeArray %ulong
+%_struct_45 = OpTypeStruct %_runtimearr_ulong
+%_ptr_StorageBuffer__struct_45 = OpTypePointer StorageBuffer %_struct_45
+%47 = OpVariable %_ptr_StorageBuffer__struct_45 StorageBuffer
+%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
+%uint_0 = OpConstant %uint 0
+%uint_32 = OpConstant %uint 32
+%77 = OpTypeFunction %void %uint %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_84 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_84 = OpTypePointer StorageBuffer %_struct_84
+%86 = OpVariable %_ptr_StorageBuffer__struct_84 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%uint_10 = OpConstant %uint 10
+%uint_4 = OpConstant %uint 4
+%uint_23 = OpConstant %uint 23
+%uint_5 = OpConstant %uint 5
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_9 = OpConstant %uint 9
+%uint_44 = OpConstant %uint 44
+%132 = OpConstantNull %_ptr_PhysicalStorageBufferEXT_blockType
+%uint_46 = OpConstant %uint 46
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
+%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
+%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
+%22 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
+%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %22 %int_0
+OpStore %26 %int_531 Aligned 16
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
+%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
+%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
+%30 = OpConvertPtrToU %ulong %21
+%67 = OpFunctionCall %bool %32 %30 %uint_8
+OpSelectionMerge %68 None
+OpBranchConditional %67 %69 %70
+%69 = OpLabel
+%71 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
+OpBranch %68
+%70 = OpLabel
+%72 = OpUConvert %uint %30
+%74 = OpShiftRightLogical %ulong %30 %uint_32
+%75 = OpUConvert %uint %74
+%131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75
+OpBranch %68
+%68 = OpLabel
+%133 = OpPhi %_ptr_PhysicalStorageBufferEXT_blockType %71 %69 %132 %70
+%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %133 %int_0
+%134 = OpConvertPtrToU %ulong %26
+%135 = OpFunctionCall %bool %32 %134 %uint_4
+OpSelectionMerge %136 None
+OpBranchConditional %135 %137 %138
+%137 = OpLabel
+OpStore %26 %int_531 Aligned 16
+OpBranch %136
+%138 = OpLabel
+%139 = OpUConvert %uint %134
+%140 = OpShiftRightLogical %ulong %134 %uint_32
+%141 = OpUConvert %uint %140
+%143 = OpFunctionCall %void %76 %uint_46 %uint_2 %139 %141
+OpBranch %136
+%136 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%32 = OpFunction %bool None %34
+%35 = OpFunctionParameter %ulong
+%36 = OpFunctionParameter %uint
+%37 = OpLabel
+OpBranch %38
+%38 = OpLabel
+%40 = OpPhi %uint %uint_1 %37 %41 %39
+OpLoopMerge %43 %39 None
+OpBranch %39
+%39 = OpLabel
+%41 = OpIAdd %uint %40 %uint_1
+%50 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %41
+%51 = OpLoad %ulong %50
+%52 = OpUGreaterThan %bool %51 %35
+OpBranchConditional %52 %43 %38
+%43 = OpLabel
+%53 = OpISub %uint %41 %uint_1
+%54 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %53
+%55 = OpLoad %ulong %54
+%56 = OpISub %ulong %35 %55
+%57 = OpUConvert %ulong %36
+%58 = OpIAdd %ulong %56 %57
+%59 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %uint_0
+%60 = OpLoad %ulong %59
+%61 = OpUConvert %uint %60
+%62 = OpISub %uint %53 %uint_1
+%63 = OpIAdd %uint %62 %61
+%64 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %63
+%65 = OpLoad %ulong %64
+%66 = OpULessThanEqual %bool %58 %65
+OpReturnValue %66
+OpFunctionEnd
+%76 = OpFunction %void None %77
+%78 = OpFunctionParameter %uint
+%79 = OpFunctionParameter %uint
+%80 = OpFunctionParameter %uint
+%81 = OpFunctionParameter %uint
+%82 = OpLabel
+%88 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_0
+%91 = OpAtomicIAdd %uint %88 %uint_4 %uint_0 %uint_10
+%92 = OpIAdd %uint %91 %uint_10
+%93 = OpArrayLength %uint %86 1
+%94 = OpULessThanEqual %bool %92 %93
+OpSelectionMerge %95 None
+OpBranchConditional %94 %96 %95
+%96 = OpLabel
+%97 = OpIAdd %uint %91 %uint_0
+%98 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %97
+OpStore %98 %uint_10
+%100 = OpIAdd %uint %91 %uint_1
+%101 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %100
+OpStore %101 %uint_23
+%102 = OpIAdd %uint %91 %uint_2
+%103 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %102
+OpStore %103 %78
+%106 = OpIAdd %uint %91 %uint_3
+%107 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %106
+OpStore %107 %uint_5
+%111 = OpLoad %v3uint %gl_GlobalInvocationID
+%112 = OpCompositeExtract %uint %111 0
+%113 = OpCompositeExtract %uint %111 1
+%114 = OpCompositeExtract %uint %111 2
+%115 = OpIAdd %uint %91 %uint_4
+%116 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %115
+OpStore %116 %112
+%117 = OpIAdd %uint %91 %uint_5
+%118 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %117
+OpStore %118 %113
+%120 = OpIAdd %uint %91 %uint_6
+%121 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %120
+OpStore %121 %114
+%123 = OpIAdd %uint %91 %uint_7
+%124 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %123
+OpStore %124 %79
+%125 = OpIAdd %uint %91 %uint_8
+%126 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %125
+OpStore %126 %80
+%128 = OpIAdd %uint %91 %uint_9
+%129 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %128
+OpStore %129 %81
+OpBranch %95
+%95 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBuffAddrCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, 2u);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools

+ 13 - 8
3rdparty/spirv-tools/test/opt/pass_fixture.h

@@ -75,7 +75,9 @@ class PassTest : public TestT {
     const auto status = pass->Run(context());
 
     std::vector<uint32_t> binary;
-    context()->module()->ToBinary(&binary, skip_nop);
+    if (status != Pass::Status::Failure) {
+      context()->module()->ToBinary(&binary, skip_nop);
+    }
     return std::make_tuple(binary, status);
   }
 
@@ -241,15 +243,18 @@ class PassTest : public TestT {
     context()->set_preserve_spec_constants(
         OptimizerOptions()->preserve_spec_constants_);
 
-    manager_->Run(context());
+    auto status = manager_->Run(context());
+    EXPECT_NE(status, Pass::Status::Failure);
 
-    std::vector<uint32_t> binary;
-    context()->module()->ToBinary(&binary, /* skip_nop = */ false);
+    if (status != Pass::Status::Failure) {
+      std::vector<uint32_t> binary;
+      context()->module()->ToBinary(&binary, /* skip_nop = */ false);
 
-    std::string optimized;
-    SpirvTools tools(env_);
-    EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_));
-    EXPECT_EQ(expected, optimized);
+      std::string optimized;
+      SpirvTools tools(env_);
+      EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_));
+      EXPECT_EQ(expected, optimized);
+    }
   }
 
   void SetAssembleOptions(uint32_t assemble_options) {

+ 33 - 0
3rdparty/spirv-tools/test/opt/private_to_local_test.cpp

@@ -419,6 +419,39 @@ OpFunctionEnd
   SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
 }
 
+TEST_F(PrivateToLocalTest, IdBoundOverflow1) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginLowerLeft
+               OpSource HLSL 84
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypeStruct %7
+    %4194302 = OpTypeStruct %8 %8
+          %9 = OpTypeStruct %8 %8
+         %11 = OpTypePointer Private %7
+         %18 = OpTypeStruct %6 %9
+         %12 = OpVariable %11 Private
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %13 = OpLoad %7 %12
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<PrivateToLocalPass>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools

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

@@ -1621,7 +1621,7 @@ TEST_F(ScalarReplacementTest, TestAccessChainWithNoIndexes) {
 }
 
 // Test that id overflow is handled gracefully.
-TEST_F(ScalarReplacementTest, IdBoundOverflow) {
+TEST_F(ScalarReplacementTest, IdBoundOverflow1) {
   const std::string text = R"(
 OpCapability ImageQuery
 OpMemoryModel Logical GLSL450
@@ -1652,8 +1652,103 @@ OpFunctionEnd
       {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
       {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
   SetMessageConsumer(GetTestMessageConsumer(messages));
-  auto result =
-      SinglePassRunAndDisassemble<ScalarReplacementPass>(text, true, false);
+  auto result = SinglePassRunToBinary<ScalarReplacementPass>(text, true, false);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
+// Test that id overflow is handled gracefully.
+TEST_F(ScalarReplacementTest, IdBoundOverflow2) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %17
+OpExecutionMode %4 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypeStruct %7
+%9 = OpTypePointer Function %8
+%16 = OpTypePointer Output %7
+%21 = OpTypeInt 32 1
+%22 = OpConstant %21 0
+%23 = OpTypePointer Function %7
+%17 = OpVariable %16 Output
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%4194300 = OpVariable %23 Function
+%10 = OpVariable %9 Function
+%4194301 = OpAccessChain %23 %10 %22
+%4194302 = OpLoad %7 %4194301
+OpStore %4194300 %4194302
+%15 = OpLoad %7 %4194300
+OpStore %17 %15
+OpReturn
+OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<ScalarReplacementPass>(text, true, false);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
+// Test that id overflow is handled gracefully.
+TEST_F(ScalarReplacementTest, IdBoundOverflow3) {
+  const std::string text = R"(
+OpCapability InterpolationFunction
+OpExtension "z"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main"
+OpExecutionMode %4 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeStruct %6 %6
+%9 = OpTypePointer Function %7
+%18 = OpTypeFunction %7 %9
+%21 = OpTypeInt 32 0
+%22 = OpConstant %21 4293000676
+%4194302 = OpConstantNull %6
+%4 = OpFunction %2 Inline|Pure %3
+%786464 = OpLabel
+%4194298 = OpVariable %9 Function
+%10 = OpVariable %9 Function
+%4194299 = OpUDiv %21 %22 %22
+%4194300 = OpLoad %7 %10
+%50959 = OpLoad %7 %4194298
+OpKill
+OpFunctionEnd
+%1 = OpFunction %7 None %18
+%19 = OpFunctionParameter %9
+%147667 = OpLabel
+%2044391 = OpUDiv %21 %22 %22
+%25 = OpLoad %7 %19
+OpReturnValue %25
+OpFunctionEnd
+%4194295 = OpFunction %2 None %3
+%4194296 = OpLabel
+OpKill
+OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<ScalarReplacementPass>(text, true, false);
   EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
 }
 

+ 28 - 26
3rdparty/spirv-tools/test/opt/types_test.cpp

@@ -65,17 +65,18 @@ class SameTypeTest : public ::testing::Test {
 #define TestMultipleInstancesOfTheSameType(ty, ...) \
   TestMultipleInstancesOfTheSameTypeQualified(ty, Simple, __VA_ARGS__)
 
-TestMultipleInstancesOfTheSameType(Void);
-TestMultipleInstancesOfTheSameType(Bool);
-TestMultipleInstancesOfTheSameType(Integer, 32, true);
-TestMultipleInstancesOfTheSameType(Float, 64);
-TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3);
-TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4);
+// clang-format off
+TestMultipleInstancesOfTheSameType(Void)
+TestMultipleInstancesOfTheSameType(Bool)
+TestMultipleInstancesOfTheSameType(Integer, 32, true)
+TestMultipleInstancesOfTheSameType(Float, 64)
+TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3)
+TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4)
 TestMultipleInstancesOfTheSameType(Image, f64_t_.get(), SpvDimCube, 0, 0, 1, 1,
                                    SpvImageFormatRgb10A2,
-                                   SpvAccessQualifierWriteOnly);
-TestMultipleInstancesOfTheSameType(Sampler);
-TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get());
+                                   SpvAccessQualifierWriteOnly)
+TestMultipleInstancesOfTheSameType(Sampler)
+TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get())
 // There are three classes of arrays, based on the kinds of length information
 // they have.
 // 1. Array length is a constant or spec constant without spec ID, with literals
@@ -85,34 +86,35 @@ TestMultipleInstancesOfTheSameTypeQualified(Array, LenConstant, u32_t_.get(),
                                                               {
                                                                   0,
                                                                   9999,
-                                                              }});
+                                                              }})
 // 2. Array length is a spec constant with a given spec id.
 TestMultipleInstancesOfTheSameTypeQualified(Array, LenSpecId, u32_t_.get(),
-                                            Array::LengthInfo{42, {1, 99}});
+                                            Array::LengthInfo{42, {1, 99}})
 // 3. Array length is an OpSpecConstantOp expression
 TestMultipleInstancesOfTheSameTypeQualified(Array, LenDefiningId, u32_t_.get(),
-                                            Array::LengthInfo{42, {2, 42}});
+                                            Array::LengthInfo{42, {2, 42}})
 
-TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get());
+TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get())
 TestMultipleInstancesOfTheSameType(Struct, std::vector<const Type*>{
-                                               u32_t_.get(), f64_t_.get()});
-TestMultipleInstancesOfTheSameType(Opaque, "testing rocks");
-TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput);
+                                               u32_t_.get(), f64_t_.get()})
+TestMultipleInstancesOfTheSameType(Opaque, "testing rocks")
+TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput)
 TestMultipleInstancesOfTheSameType(Function, u32_t_.get(),
-                                   {f64_t_.get(), f64_t_.get()});
-TestMultipleInstancesOfTheSameType(Event);
-TestMultipleInstancesOfTheSameType(DeviceEvent);
-TestMultipleInstancesOfTheSameType(ReserveId);
-TestMultipleInstancesOfTheSameType(Queue);
-TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite);
-TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform);
-TestMultipleInstancesOfTheSameType(PipeStorage);
-TestMultipleInstancesOfTheSameType(NamedBarrier);
-TestMultipleInstancesOfTheSameType(AccelerationStructureNV);
+                                   {f64_t_.get(), f64_t_.get()})
+TestMultipleInstancesOfTheSameType(Event)
+TestMultipleInstancesOfTheSameType(DeviceEvent)
+TestMultipleInstancesOfTheSameType(ReserveId)
+TestMultipleInstancesOfTheSameType(Queue)
+TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite)
+TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform)
+TestMultipleInstancesOfTheSameType(PipeStorage)
+TestMultipleInstancesOfTheSameType(NamedBarrier)
+TestMultipleInstancesOfTheSameType(AccelerationStructureNV)
 #undef TestMultipleInstanceOfTheSameType
 #undef TestMultipleInstanceOfTheSameTypeQual
 
 std::vector<std::unique_ptr<Type>> GenerateAllTypes() {
+  // clang-format on
   // Types in this test case are only equal to themselves, nothing else.
   std::vector<std::unique_ptr<Type>> types;
 

+ 267 - 0
3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp

@@ -0,0 +1,267 @@
+// 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 "gmock/gmock.h"
+#include "test/opt/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using WrapOpKillTest = PassTest<::testing::Test>;
+
+TEST_F(WrapOpKillTest, SingleOpKill) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
+; CHECK: [[orig_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpUnreachable
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %5
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranch %12
+         %12 = OpLabel
+               OpBranchConditional %true %13 %10
+         %13 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %14 = OpFunctionCall %void %kill_
+               OpBranch %9
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+      %kill_ = OpFunction %void None %5
+         %15 = OpLabel
+               OpKill
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, MultipleOpKillInSameFunc) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
+; CHECK: [[orig_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpSelectionMerge
+; CHECK-NEXT: OpBranchConditional
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpUnreachable
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
+; CHECK-NEXT: OpUnreachable
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %5
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+               OpLoopMerge %10 %11 None
+               OpBranch %12
+         %12 = OpLabel
+               OpBranchConditional %true %13 %10
+         %13 = OpLabel
+               OpBranch %11
+         %11 = OpLabel
+         %14 = OpFunctionCall %void %kill_
+               OpBranch %9
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+      %kill_ = OpFunction %void None %5
+         %15 = OpLabel
+               OpSelectionMerge %16 None
+               OpBranchConditional %true %17 %18
+         %17 = OpLabel
+               OpKill
+         %18 = OpLabel
+               OpKill
+         %16 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, MultipleOpKillInDifferentFunc) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]]
+; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]]
+; CHECK: [[orig_kill1]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpUnreachable
+; CHECK: [[orig_kill2]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
+; CHECK-NEXT: OpUnreachable
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 330
+               OpName %main "main"
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %bool = OpTypeBool
+       %true = OpConstantTrue %bool
+       %main = OpFunction %void None %4
+          %7 = OpLabel
+               OpBranch %8
+          %8 = OpLabel
+               OpLoopMerge %9 %10 None
+               OpBranch %11
+         %11 = OpLabel
+               OpBranchConditional %true %12 %9
+         %12 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+         %13 = OpFunctionCall %void %14
+         %15 = OpFunctionCall %void %16
+               OpBranch %8
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd
+         %14 = OpFunction %void None %4
+         %17 = OpLabel
+               OpKill
+               OpFunctionEnd
+         %16 = OpFunction %void None %4
+         %18 = OpLabel
+               OpKill
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, IdBoundOverflow1) {
+  const std::string text = R"(
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main"
+OpExecutionMode %4 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpFunction %2 Pure|Const %3
+%4194302 = OpLabel
+OpKill
+OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
+TEST_F(WrapOpKillTest, IdBoundOverflow2) {
+  const std::string text = R"(
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main"
+OpExecutionMode %4 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpFunction %2 Pure|Const %3
+%4194301 = OpLabel
+OpKill
+OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
+TEST_F(WrapOpKillTest, IdBoundOverflow3) {
+  const std::string text = R"(
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main"
+OpExecutionMode %4 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpFunction %2 Pure|Const %3
+%4194300 = OpLabel
+OpKill
+OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::vector<Message> messages = {
+      {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
+  SetMessageConsumer(GetTestMessageConsumer(messages));
+  auto result = SinglePassRunToBinary<WrapOpKill>(text, true);
+  EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools

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

@@ -24,6 +24,7 @@ add_spvtools_unittest(TARGET reduce
         remove_block_test.cpp
         remove_function_test.cpp
         remove_opname_instruction_test.cpp
+        remove_relaxed_precision_decoration_test.cpp
         remove_selection_test.cpp
         remove_unreferenced_instruction_test.cpp
         structured_loop_to_selection_test.cpp

+ 177 - 0
3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp

@@ -0,0 +1,177 @@
+// 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/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
+
+#include "source/opt/build_module.h"
+#include "source/reduce/reduction_opportunity.h"
+#include "source/reduce/reduction_pass.h"
+#include "test/reduce/reduce_test_util.h"
+
+namespace spvtools {
+namespace reduce {
+namespace {
+
+TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) {
+  const std::string source = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %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, source, kReduceAssembleOption);
+  const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
+                       .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(0, ops.size());
+}
+
+TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) {
+  const std::string source = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "f"
+               OpName %12 "i"
+               OpName %16 "v"
+               OpName %19 "S"
+               OpMemberName %19 0 "a"
+               OpMemberName %19 1 "b"
+               OpMemberName %19 2 "c"
+               OpName %21 "s"
+               OpDecorate %8 RelaxedPrecision
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %16 RelaxedPrecision
+               OpDecorate %17 RelaxedPrecision
+               OpDecorate %18 RelaxedPrecision
+               OpMemberDecorate %19 0 RelaxedPrecision
+               OpMemberDecorate %19 1 RelaxedPrecision
+               OpMemberDecorate %19 2 RelaxedPrecision
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %23 RelaxedPrecision
+               OpDecorate %24 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 22
+         %14 = OpTypeVector %6 2
+         %15 = OpTypePointer Function %14
+         %19 = OpTypeStruct %10 %6 %14
+         %20 = OpTypePointer Function %19
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+         %21 = OpVariable %20 Function
+               OpStore %8 %9
+               OpStore %12 %13
+         %17 = OpLoad %6 %8
+         %18 = OpCompositeConstruct %14 %17 %17
+               OpStore %16 %18
+         %22 = OpLoad %10 %12
+         %23 = OpLoad %6 %8
+         %24 = OpLoad %14 %16
+         %25 = OpCompositeConstruct %19 %22 %23 %24
+               OpStore %21 %25
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context =
+      BuildModule(env, consumer, source, kReduceAssembleOption);
+  const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
+                       .GetAvailableOpportunities(context.get());
+  ASSERT_EQ(11, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+  }
+
+  const std::string expected = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "f"
+               OpName %12 "i"
+               OpName %16 "v"
+               OpName %19 "S"
+               OpMemberName %19 0 "a"
+               OpMemberName %19 1 "b"
+               OpMemberName %19 2 "c"
+               OpName %21 "s"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 2
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Function %10
+         %13 = OpConstant %10 22
+         %14 = OpTypeVector %6 2
+         %15 = OpTypePointer Function %14
+         %19 = OpTypeStruct %10 %6 %14
+         %20 = OpTypePointer Function %19
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %16 = OpVariable %15 Function
+         %21 = OpVariable %20 Function
+               OpStore %8 %9
+               OpStore %12 %13
+         %17 = OpLoad %6 %8
+         %18 = OpCompositeConstruct %14 %17 %17
+               OpStore %16 %18
+         %22 = OpLoad %10 %12
+         %23 = OpLoad %6 %8
+         %24 = OpLoad %14 %16
+         %25 = OpCompositeConstruct %19 %22 %23 %24
+               OpStore %21 %25
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(env, expected, context.get());
+}
+
+}  // namespace
+}  // namespace reduce
+}  // namespace spvtools

+ 5 - 1
3rdparty/spirv-tools/test/tools/opt/flags.py

@@ -57,7 +57,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4,
   """Tests that spirv-opt accepts all valid optimization flags."""
 
   flags = [
-      '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
+      '--wrap-opkill', '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
       '--convert-local-access-chains', '--copy-propagate-arrays',
       '--eliminate-dead-branches',
       '--eliminate-dead-code-aggressive', '--eliminate-dead-const',
@@ -76,6 +76,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4,
       '--unify-const'
   ]
   expected_passes = [
+      'wrap-opkill',
       'ccp',
       'cfg-cleanup',
       'combine-access-chains',
@@ -134,6 +135,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_4,
 
   flags = ['-O']
   expected_passes = [
+      'wrap-opkill',
       'eliminate-dead-branches',
       'merge-return',
       'inline-entry-points-exhaustive',
@@ -181,6 +183,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_4,
 
   flags = ['-Os']
   expected_passes = [
+      'wrap-opkill',
       'eliminate-dead-branches',
       'merge-return',
       'inline-entry-points-exhaustive',
@@ -221,6 +224,7 @@ class TestLegalizationPasses(expect.ValidObjectFile1_4,
 
   flags = ['--legalize-hlsl']
   expected_passes = [
+      'wrap-opkill',
       'eliminate-dead-branches',
       'merge-return',
       'inline-entry-points-exhaustive',

+ 16 - 0
3rdparty/spirv-tools/test/val/val_constants_test.cpp

@@ -442,6 +442,22 @@ OpMemoryModel Logical GLSL450
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
+TEST_F(ValidateConstant, NullMatrix) {
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%null_vector = OpConstantNull %v2float
+%null_matrix = OpConstantComposite %mat2x2 %null_vector %null_vector
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools

+ 42 - 15
3rdparty/spirv-tools/test/val/val_ext_inst_test.cpp

@@ -5315,10 +5315,10 @@ INSTANTIATE_TEST_SUITE_P(AllFractLike, ValidateOpenCLStdFractLike,
 
 TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) {
   const std::string body = R"(
-%var_f32 = OpVariable %f32_ptr_function Function
-%var_f32vec2 = OpVariable %f32vec2_ptr_function Function
-%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32
-%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_f32vec2
+%var_u32 = OpVariable %u32_ptr_function Function
+%var_u32vec2 = OpVariable %u32vec2_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32
+%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_u32vec2
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -5327,8 +5327,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) {
 
 TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) {
   const std::string body = R"(
-%var_f32 = OpVariable %f32_ptr_function Function
-%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_f32
+%var_u32 = OpVariable %u32_ptr_function Function
+%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_u32
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -5341,8 +5341,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) {
 
 TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) {
   const std::string body = R"(
-%var_f32 = OpVariable %f32_ptr_function Function
-%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_f32
+%var_u32 = OpVariable %f32_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_u32
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -5355,8 +5355,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) {
 
 TEST_F(ValidateExtInst, OpenCLStdRemquoYWrongType) {
   const std::string body = R"(
-%var_f32 = OpVariable %f32_ptr_function Function
-%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_f32
+%var_u32 = OpVariable %f32_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_u32
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -5395,17 +5395,44 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongStorageClass) {
 
 TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataType) {
   const std::string body = R"(
-%var_u32 = OpVariable %u32_ptr_function Function
-%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32
+%var_f32 = OpVariable %f32_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std remquo: "
+                        "expected data type of the pointer to be a 32-bit int "
+                        "scalar or vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataTypeWidth) {
+  const std::string body = R"(
+%var_u64 = OpVariable %u64_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u64
+)";
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std remquo: "
+                        "expected data type of the pointer to be a 32-bit int "
+                        "scalar or vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongNumberOfComponents) {
+  const std::string body = R"(
+%var_u32vec2 = OpVariable %u32vec2_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32vec2
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(
-          "OpenCL.std remquo: "
-          "expected data type of the pointer to be equal to Result Type"));
+      HasSubstr("OpenCL.std remquo: "
+                "expected data type of the pointer to have the same number "
+                "of components as Result Type"));
 }
 
 TEST_P(ValidateOpenCLStdFrexpLike, Success) {

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

@@ -1715,7 +1715,7 @@ TEST_F(ValidateIdWithMessage,
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("OpSpecConstantComposite Constituent <id> '7[%7]' is "
-                        "not a constant composite or undef."));
+                        "not a constant or undef."));
 }
 
 // Invalid: Composite contains a column that is *not* a vector (it's an array)