소스 검색

Updated spirv-tools.

Бранимир Караџић 6 년 전
부모
커밋
cfb5da28f0
29개의 변경된 파일2239개의 추가작업 그리고 2567개의 파일을 삭제
  1. 0 1
      3rdparty/spirv-tools/Android.mk
  2. 5 4
      3rdparty/spirv-tools/BUILD.gn
  3. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  4. 5 0
      3rdparty/spirv-tools/include/spirv-tools/instrument.hpp
  5. 0 14
      3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp
  6. 0 2
      3rdparty/spirv-tools/source/opt/CMakeLists.txt
  7. 0 596
      3rdparty/spirv-tools/source/opt/common_uniform_elim_pass.cpp
  8. 0 213
      3rdparty/spirv-tools/source/opt/common_uniform_elim_pass.h
  9. 2 39
      3rdparty/spirv-tools/source/opt/inline_pass.cpp
  10. 1 4
      3rdparty/spirv-tools/source/opt/inline_pass.h
  11. 5 5
      3rdparty/spirv-tools/source/opt/instruction.cpp
  12. 6 1
      3rdparty/spirv-tools/source/opt/instruction.h
  13. 28 1
      3rdparty/spirv-tools/source/opt/instrument_pass.cpp
  14. 2 1
      3rdparty/spirv-tools/source/opt/ir_context.cpp
  15. 43 35
      3rdparty/spirv-tools/source/opt/merge_return_pass.cpp
  16. 16 22
      3rdparty/spirv-tools/source/opt/merge_return_pass.h
  17. 0 11
      3rdparty/spirv-tools/source/opt/optimizer.cpp
  18. 0 1
      3rdparty/spirv-tools/source/opt/passes.h
  19. 37 0
      3rdparty/spirv-tools/source/val/validate_type.cpp
  20. 0 1
      3rdparty/spirv-tools/test/opt/CMakeLists.txt
  21. 0 1479
      3rdparty/spirv-tools/test/opt/common_uniform_elim_test.cpp
  22. 0 115
      3rdparty/spirv-tools/test/opt/inline_test.cpp
  23. 1944 0
      3rdparty/spirv-tools/test/opt/inst_bindless_check_test.cpp
  24. 0 1
      3rdparty/spirv-tools/test/opt/optimizer_test.cpp
  25. 56 0
      3rdparty/spirv-tools/test/opt/pass_merge_return_test.cpp
  26. 9 0
      3rdparty/spirv-tools/test/tools/expect.py
  27. 43 2
      3rdparty/spirv-tools/test/tools/opt/flags.py
  28. 20 0
      3rdparty/spirv-tools/test/val/val_id_test.cpp
  29. 16 18
      3rdparty/spirv-tools/tools/opt/opt.cpp

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

@@ -84,7 +84,6 @@ SPVTOOLS_OPT_SRC_FILES := \
 		source/opt/ccp_pass.cpp \
 		source/opt/ccp_pass.cpp \
 		source/opt/code_sink.cpp \
 		source/opt/code_sink.cpp \
 		source/opt/combine_access_chains.cpp \
 		source/opt/combine_access_chains.cpp \
-		source/opt/common_uniform_elim_pass.cpp \
 		source/opt/compact_ids_pass.cpp \
 		source/opt/compact_ids_pass.cpp \
 		source/opt/composite.cpp \
 		source/opt/composite.cpp \
 		source/opt/const_folding_rules.cpp \
 		source/opt/const_folding_rules.cpp \

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

@@ -469,8 +469,6 @@ static_library("spvtools_opt") {
     "source/opt/code_sink.h",
     "source/opt/code_sink.h",
     "source/opt/combine_access_chains.cpp",
     "source/opt/combine_access_chains.cpp",
     "source/opt/combine_access_chains.h",
     "source/opt/combine_access_chains.h",
-    "source/opt/common_uniform_elim_pass.cpp",
-    "source/opt/common_uniform_elim_pass.h",
     "source/opt/compact_ids_pass.cpp",
     "source/opt/compact_ids_pass.cpp",
     "source/opt/compact_ids_pass.h",
     "source/opt/compact_ids_pass.h",
     "source/opt/composite.cpp",
     "source/opt/composite.cpp",
@@ -670,6 +668,7 @@ static_library("spvtools_link") {
   ]
   ]
   deps = [
   deps = [
     ":spvtools",
     ":spvtools",
+    ":spvtools_opt",
     ":spvtools_val",
     ":spvtools_val",
   ]
   ]
   public_deps = [
   public_deps = [
@@ -736,6 +735,8 @@ static_library("spvtools_reduce") {
     "source/reduce/structured_loop_to_selection_reduction_opportunity.h",
     "source/reduce/structured_loop_to_selection_reduction_opportunity.h",
     "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp",
     "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp",
     "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h",
     "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h",
+    "source/spirv_reducer_options.cpp",
+    "source/spirv_reducer_options.h",
   ]
   ]
   deps = [
   deps = [
     ":spvtools",
     ":spvtools",
@@ -865,6 +866,7 @@ source_set("spvtools_software_version") {
   ]
   ]
   deps = [
   deps = [
     ":spvtools_build_version",
     ":spvtools_build_version",
+    ":spvtools_headers",
   ]
   ]
   configs += [ ":spvtools_internal_config" ]
   configs += [ ":spvtools_internal_config" ]
 }
 }
@@ -949,12 +951,11 @@ if (!is_ios) {
   # iOS does not allow std::system calls which spirv-reduce requires
   # iOS does not allow std::system calls which spirv-reduce requires
   executable("spirv-reduce") {
   executable("spirv-reduce") {
     sources = [
     sources = [
-      "source/spirv_reducer_options.cpp",
-      "source/spirv_reducer_options.h",
       "tools/reduce/reduce.cpp",
       "tools/reduce/reduce.cpp",
     ]
     ]
     deps = [
     deps = [
       ":spvtools",
       ":spvtools",
+      ":spvtools_opt",
       ":spvtools_reduce",
       ":spvtools_reduce",
       ":spvtools_software_version",
       ":spvtools_software_version",
       ":spvtools_util_cli_consumer",
       ":spvtools_util_cli_consumer",

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

@@ -1 +1 @@
-"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-82-g55adf4cf"
+"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-90-g76b75c40"

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

@@ -117,6 +117,11 @@ static const int kInstGeomOutPrimitiveId = kInstCommonOutCnt;
 static const int kInstGeomOutInvocationId = kInstCommonOutCnt + 1;
 static const int kInstGeomOutInvocationId = kInstCommonOutCnt + 1;
 static const int kInstGeomOutUnused = kInstCommonOutCnt + 2;
 static const int kInstGeomOutUnused = kInstCommonOutCnt + 2;
 
 
+// Ray Tracing Shader Output Record Offsets
+static const int kInstRayTracingOutLaunchIdX = kInstCommonOutCnt;
+static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1;
+static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
+
 // Size of Common and Stage-specific Members
 // Size of Common and Stage-specific Members
 static const int kInstStageOutCnt = kInstCommonOutCnt + 2;
 static const int kInstStageOutCnt = kInstCommonOutCnt + 2;
 static const int kInst2StageOutCnt = kInstCommonOutCnt + 3;
 static const int kInst2StageOutCnt = kInstCommonOutCnt + 3;

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

@@ -492,20 +492,6 @@ Optimizer::PassToken CreateInsertExtractElimPass();
 // inserts created by that pass.
 // inserts created by that pass.
 Optimizer::PassToken CreateDeadInsertElimPass();
 Optimizer::PassToken CreateDeadInsertElimPass();
 
 
-// Creates a pass to consolidate uniform references.
-// For each entry point function in the module, first change all constant index
-// access chain loads into equivalent composite extracts. Then consolidate
-// identical uniform loads into one uniform load. Finally, consolidate
-// identical uniform extracts into one uniform extract. This may require
-// moving a load or extract to a point which dominates all uses.
-//
-// This pass requires a module to have structured control flow ie shader
-// capability. It also requires logical addressing ie Addresses capability
-// is not enabled. It also currently does not support any extensions.
-//
-// This pass currently only optimizes loads with a single index.
-Optimizer::PassToken CreateCommonUniformElimPass();
-
 // Create aggressive dead code elimination pass
 // Create aggressive dead code elimination pass
 // This pass eliminates unused code from the module. In addition,
 // This pass eliminates unused code from the module. In addition,
 // it detects and eliminates code which may have spurious uses but which do
 // it detects and eliminates code which may have spurious uses but which do

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

@@ -22,7 +22,6 @@ set(SPIRV_TOOLS_OPT_SOURCES
   cfg.h
   cfg.h
   code_sink.h
   code_sink.h
   combine_access_chains.h
   combine_access_chains.h
-  common_uniform_elim_pass.h
   compact_ids_pass.h
   compact_ids_pass.h
   composite.h
   composite.h
   const_folding_rules.h
   const_folding_rules.h
@@ -123,7 +122,6 @@ set(SPIRV_TOOLS_OPT_SOURCES
   cfg.cpp
   cfg.cpp
   code_sink.cpp
   code_sink.cpp
   combine_access_chains.cpp
   combine_access_chains.cpp
-  common_uniform_elim_pass.cpp
   compact_ids_pass.cpp
   compact_ids_pass.cpp
   composite.cpp
   composite.cpp
   const_folding_rules.cpp
   const_folding_rules.cpp

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

@@ -1,596 +0,0 @@
-// Copyright (c) 2017 The Khronos Group Inc.
-// Copyright (c) 2017 Valve Corporation
-// Copyright (c) 2017 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 "source/opt/common_uniform_elim_pass.h"
-#include "source/cfa.h"
-#include "source/opt/ir_context.h"
-
-namespace spvtools {
-namespace opt {
-
-namespace {
-
-const uint32_t kAccessChainPtrIdInIdx = 0;
-const uint32_t kTypePointerStorageClassInIdx = 0;
-const uint32_t kTypePointerTypeIdInIdx = 1;
-const uint32_t kConstantValueInIdx = 0;
-const uint32_t kExtractCompositeIdInIdx = 0;
-const uint32_t kExtractIdx0InIdx = 1;
-const uint32_t kStorePtrIdInIdx = 0;
-const uint32_t kLoadPtrIdInIdx = 0;
-const uint32_t kCopyObjectOperandInIdx = 0;
-const uint32_t kTypeIntWidthInIdx = 0;
-
-}  // anonymous namespace
-
-bool CommonUniformElimPass::IsNonPtrAccessChain(const SpvOp opcode) const {
-  return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
-}
-
-bool CommonUniformElimPass::IsSamplerOrImageType(
-    const Instruction* typeInst) const {
-  switch (typeInst->opcode()) {
-    case SpvOpTypeSampler:
-    case SpvOpTypeImage:
-    case SpvOpTypeSampledImage:
-      return true;
-    default:
-      break;
-  }
-  if (typeInst->opcode() != SpvOpTypeStruct) return false;
-  // Return true if any member is a sampler or image
-  return !typeInst->WhileEachInId([this](const uint32_t* tid) {
-    const Instruction* compTypeInst = get_def_use_mgr()->GetDef(*tid);
-    if (IsSamplerOrImageType(compTypeInst)) {
-      return false;
-    }
-    return true;
-  });
-}
-
-bool CommonUniformElimPass::IsSamplerOrImageVar(uint32_t varId) const {
-  const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
-  assert(varInst->opcode() == SpvOpVariable);
-  const uint32_t varTypeId = varInst->type_id();
-  const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
-  const uint32_t varPteTypeId =
-      varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
-  Instruction* varPteTypeInst = get_def_use_mgr()->GetDef(varPteTypeId);
-  return IsSamplerOrImageType(varPteTypeInst);
-}
-
-Instruction* CommonUniformElimPass::GetPtr(Instruction* ip, uint32_t* objId) {
-  const SpvOp op = ip->opcode();
-  assert(op == SpvOpStore || op == SpvOpLoad);
-  *objId = ip->GetSingleWordInOperand(op == SpvOpStore ? kStorePtrIdInIdx
-                                                       : kLoadPtrIdInIdx);
-  Instruction* ptrInst = get_def_use_mgr()->GetDef(*objId);
-  while (ptrInst->opcode() == SpvOpCopyObject) {
-    *objId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
-    ptrInst = get_def_use_mgr()->GetDef(*objId);
-  }
-  Instruction* objInst = ptrInst;
-  while (objInst->opcode() != SpvOpVariable &&
-         objInst->opcode() != SpvOpFunctionParameter) {
-    if (IsNonPtrAccessChain(objInst->opcode())) {
-      *objId = objInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
-    } else {
-      assert(objInst->opcode() == SpvOpCopyObject);
-      *objId = objInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
-    }
-    objInst = get_def_use_mgr()->GetDef(*objId);
-  }
-  return ptrInst;
-}
-
-bool CommonUniformElimPass::IsVolatileStruct(uint32_t type_id) {
-  assert(get_def_use_mgr()->GetDef(type_id)->opcode() == SpvOpTypeStruct);
-  return !get_decoration_mgr()->WhileEachDecoration(
-      type_id, SpvDecorationVolatile, [](const Instruction&) { return false; });
-}
-
-bool CommonUniformElimPass::IsAccessChainToVolatileStructType(
-    const Instruction& AccessChainInst) {
-  assert(AccessChainInst.opcode() == SpvOpAccessChain);
-
-  uint32_t ptr_id = AccessChainInst.GetSingleWordInOperand(0);
-  const Instruction* ptr_inst = get_def_use_mgr()->GetDef(ptr_id);
-  uint32_t pointee_type_id = GetPointeeTypeId(ptr_inst);
-  const uint32_t num_operands = AccessChainInst.NumOperands();
-
-  // walk the type tree:
-  for (uint32_t idx = 3; idx < num_operands; ++idx) {
-    Instruction* pointee_type = get_def_use_mgr()->GetDef(pointee_type_id);
-
-    switch (pointee_type->opcode()) {
-      case SpvOpTypeMatrix:
-      case SpvOpTypeVector:
-      case SpvOpTypeArray:
-      case SpvOpTypeRuntimeArray:
-        pointee_type_id = pointee_type->GetSingleWordOperand(1);
-        break;
-      case SpvOpTypeStruct:
-        // check for volatile decorations:
-        if (IsVolatileStruct(pointee_type_id)) return true;
-
-        if (idx < num_operands - 1) {
-          const uint32_t index_id = AccessChainInst.GetSingleWordOperand(idx);
-          const Instruction* index_inst = get_def_use_mgr()->GetDef(index_id);
-          uint32_t index_value = index_inst->GetSingleWordOperand(
-              2);  // TODO: replace with GetUintValueFromConstant()
-          pointee_type_id = pointee_type->GetSingleWordInOperand(index_value);
-        }
-        break;
-      default:
-        assert(false && "Unhandled pointee type.");
-    }
-  }
-  return false;
-}
-
-bool CommonUniformElimPass::IsVolatileLoad(const Instruction& loadInst) {
-  assert(loadInst.opcode() == SpvOpLoad);
-  // Check if this Load instruction has Volatile Memory Access flag
-  if (loadInst.NumOperands() == 4) {
-    uint32_t memory_access_mask = loadInst.GetSingleWordOperand(3);
-    if (memory_access_mask & SpvMemoryAccessVolatileMask) return true;
-  }
-  // If we load a struct directly (result type is struct),
-  // check if the struct is decorated volatile
-  uint32_t type_id = loadInst.type_id();
-  if (get_def_use_mgr()->GetDef(type_id)->opcode() == SpvOpTypeStruct)
-    return IsVolatileStruct(type_id);
-  else
-    return false;
-}
-
-bool CommonUniformElimPass::IsUniformVar(uint32_t varId) {
-  const Instruction* varInst =
-      get_def_use_mgr()->id_to_defs().find(varId)->second;
-  if (varInst->opcode() != SpvOpVariable) return false;
-  const uint32_t varTypeId = varInst->type_id();
-  const Instruction* varTypeInst =
-      get_def_use_mgr()->id_to_defs().find(varTypeId)->second;
-  return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
-             SpvStorageClassUniform ||
-         varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
-             SpvStorageClassUniformConstant;
-}
-
-bool CommonUniformElimPass::HasUnsupportedDecorates(uint32_t id) const {
-  return !get_def_use_mgr()->WhileEachUser(id, [this](Instruction* user) {
-    if (IsNonTypeDecorate(user->opcode())) return false;
-    return true;
-  });
-}
-
-bool CommonUniformElimPass::HasOnlyNamesAndDecorates(uint32_t id) const {
-  return get_def_use_mgr()->WhileEachUser(id, [this](Instruction* user) {
-    SpvOp op = user->opcode();
-    if (op != SpvOpName && !IsNonTypeDecorate(op)) return false;
-    return true;
-  });
-}
-
-void CommonUniformElimPass::DeleteIfUseless(Instruction* inst) {
-  const uint32_t resId = inst->result_id();
-  assert(resId != 0);
-  if (HasOnlyNamesAndDecorates(resId)) {
-    context()->KillInst(inst);
-  }
-}
-
-Instruction* CommonUniformElimPass::ReplaceAndDeleteLoad(Instruction* loadInst,
-                                                         uint32_t replId,
-                                                         Instruction* ptrInst) {
-  const uint32_t loadId = loadInst->result_id();
-  context()->KillNamesAndDecorates(loadId);
-  (void)context()->ReplaceAllUsesWith(loadId, replId);
-  // remove load instruction
-  Instruction* next_instruction = context()->KillInst(loadInst);
-  // if access chain, see if it can be removed as well
-  if (IsNonPtrAccessChain(ptrInst->opcode())) DeleteIfUseless(ptrInst);
-  return next_instruction;
-}
-
-void CommonUniformElimPass::GenACLoadRepl(
-    const Instruction* ptrInst,
-    std::vector<std::unique_ptr<Instruction>>* newInsts, uint32_t* resultId) {
-  // Build and append Load
-  const uint32_t ldResultId = TakeNextId();
-  const uint32_t varId =
-      ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
-  const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
-  assert(varInst->opcode() == SpvOpVariable);
-  const uint32_t varPteTypeId = GetPointeeTypeId(varInst);
-  std::vector<Operand> load_in_operands;
-  load_in_operands.push_back(Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-                                     std::initializer_list<uint32_t>{varId}));
-  std::unique_ptr<Instruction> newLoad(new Instruction(
-      context(), SpvOpLoad, varPteTypeId, ldResultId, load_in_operands));
-  get_def_use_mgr()->AnalyzeInstDefUse(&*newLoad);
-  newInsts->emplace_back(std::move(newLoad));
-
-  // Build and append Extract
-  const uint32_t extResultId = TakeNextId();
-  const uint32_t ptrPteTypeId = GetPointeeTypeId(ptrInst);
-  std::vector<Operand> ext_in_opnds;
-  ext_in_opnds.push_back(Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-                                 std::initializer_list<uint32_t>{ldResultId}));
-  uint32_t iidIdx = 0;
-  ptrInst->ForEachInId([&iidIdx, &ext_in_opnds, this](const uint32_t* iid) {
-    if (iidIdx > 0) {
-      const Instruction* cInst = get_def_use_mgr()->GetDef(*iid);
-      uint32_t val = cInst->GetSingleWordInOperand(kConstantValueInIdx);
-      ext_in_opnds.push_back(
-          Operand(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-                  std::initializer_list<uint32_t>{val}));
-    }
-    ++iidIdx;
-  });
-  std::unique_ptr<Instruction> newExt(
-      new Instruction(context(), SpvOpCompositeExtract, ptrPteTypeId,
-                      extResultId, ext_in_opnds));
-  get_def_use_mgr()->AnalyzeInstDefUse(&*newExt);
-  newInsts->emplace_back(std::move(newExt));
-  *resultId = extResultId;
-}
-
-bool CommonUniformElimPass::IsConstantIndexAccessChain(Instruction* acp) {
-  uint32_t inIdx = 0;
-  return acp->WhileEachInId([&inIdx, this](uint32_t* tid) {
-    if (inIdx > 0) {
-      Instruction* opInst = get_def_use_mgr()->GetDef(*tid);
-      if (opInst->opcode() != SpvOpConstant) return false;
-    }
-    ++inIdx;
-    return true;
-  });
-}
-
-bool CommonUniformElimPass::UniformAccessChainConvert(Function* func) {
-  bool modified = false;
-  for (auto bi = func->begin(); bi != func->end(); ++bi) {
-    for (Instruction* inst = &*bi->begin(); inst; inst = inst->NextNode()) {
-      if (inst->opcode() != SpvOpLoad) continue;
-      uint32_t varId;
-      Instruction* ptrInst = GetPtr(inst, &varId);
-      if (!IsNonPtrAccessChain(ptrInst->opcode())) continue;
-      // Do not convert nested access chains
-      if (ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx) != varId)
-        continue;
-      if (!IsUniformVar(varId)) continue;
-      if (!IsConstantIndexAccessChain(ptrInst)) continue;
-      if (HasUnsupportedDecorates(inst->result_id())) continue;
-      if (HasUnsupportedDecorates(ptrInst->result_id())) continue;
-      if (IsVolatileLoad(*inst)) continue;
-      if (IsAccessChainToVolatileStructType(*ptrInst)) continue;
-      std::vector<std::unique_ptr<Instruction>> newInsts;
-      uint32_t replId;
-      GenACLoadRepl(ptrInst, &newInsts, &replId);
-      inst = ReplaceAndDeleteLoad(inst, replId, ptrInst);
-      assert(inst->opcode() != SpvOpPhi);
-      inst = inst->InsertBefore(std::move(newInsts));
-      modified = true;
-    }
-  }
-  return modified;
-}
-
-void CommonUniformElimPass::ComputeStructuredSuccessors(Function* func) {
-  block2structured_succs_.clear();
-  for (auto& blk : *func) {
-    // If header, make merge block first successor.
-    uint32_t mbid = blk.MergeBlockIdIfAny();
-    if (mbid != 0) {
-      block2structured_succs_[&blk].push_back(cfg()->block(mbid));
-      uint32_t cbid = blk.ContinueBlockIdIfAny();
-      if (cbid != 0) {
-        block2structured_succs_[&blk].push_back(cfg()->block(mbid));
-      }
-    }
-    // add true successors
-    const auto& const_blk = blk;
-    const_blk.ForEachSuccessorLabel([&blk, this](const uint32_t sbid) {
-      block2structured_succs_[&blk].push_back(cfg()->block(sbid));
-    });
-  }
-}
-
-void CommonUniformElimPass::ComputeStructuredOrder(
-    Function* func, std::list<BasicBlock*>* order) {
-  // Compute structured successors and do DFS
-  ComputeStructuredSuccessors(func);
-  auto ignore_block = [](cbb_ptr) {};
-  auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
-  auto get_structured_successors = [this](const BasicBlock* block) {
-    return &(block2structured_succs_[block]);
-  };
-  // TODO(greg-lunarg): Get rid of const_cast by making moving const
-  // out of the cfa.h prototypes and into the invoking code.
-  auto post_order = [&](cbb_ptr b) {
-    order->push_front(const_cast<BasicBlock*>(b));
-  };
-
-  order->clear();
-  CFA<BasicBlock>::DepthFirstTraversal(&*func->begin(),
-                                       get_structured_successors, ignore_block,
-                                       post_order, ignore_edge);
-}
-
-bool CommonUniformElimPass::CommonUniformLoadElimination(Function* func) {
-  // Process all blocks in structured order. This is just one way (the
-  // simplest?) to keep track of the most recent block outside of control
-  // flow, used to copy common instructions, guaranteed to dominate all
-  // following load sites.
-  std::list<BasicBlock*> structuredOrder;
-  ComputeStructuredOrder(func, &structuredOrder);
-  uniform2load_id_.clear();
-  bool modified = false;
-  // Find insertion point in first block to copy non-dominating loads.
-  auto insertItr = func->begin()->begin();
-  while (insertItr->opcode() == SpvOpVariable ||
-         insertItr->opcode() == SpvOpNop)
-    ++insertItr;
-  // Update insertItr until it will not be removed. Without this code,
-  // ReplaceAndDeleteLoad() can set |insertItr| as a dangling pointer.
-  while (IsUniformLoadToBeRemoved(&*insertItr)) ++insertItr;
-  uint32_t mergeBlockId = 0;
-  for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
-    BasicBlock* bp = *bi;
-    // Check if we are exiting outermost control construct. If so, remember
-    // new load insertion point. Trying to keep register pressure down.
-    if (mergeBlockId == bp->id()) {
-      mergeBlockId = 0;
-      insertItr = bp->begin();
-      while (insertItr->opcode() == SpvOpPhi) {
-        ++insertItr;
-      }
-
-      // Update insertItr until it will not be removed. Without this code,
-      // ReplaceAndDeleteLoad() can set |insertItr| as a dangling pointer.
-      while (IsUniformLoadToBeRemoved(&*insertItr)) ++insertItr;
-    }
-    for (Instruction* inst = &*bp->begin(); inst; inst = inst->NextNode()) {
-      if (inst->opcode() != SpvOpLoad) continue;
-      uint32_t varId;
-      Instruction* ptrInst = GetPtr(inst, &varId);
-      if (ptrInst->opcode() != SpvOpVariable) continue;
-      if (!IsUniformVar(varId)) continue;
-      if (IsSamplerOrImageVar(varId)) continue;
-      if (HasUnsupportedDecorates(inst->result_id())) continue;
-      if (IsVolatileLoad(*inst)) continue;
-      uint32_t replId;
-      const auto uItr = uniform2load_id_.find(varId);
-      if (uItr != uniform2load_id_.end()) {
-        replId = uItr->second;
-      } else {
-        if (mergeBlockId == 0) {
-          // Load is in dominating block; just remember it
-          uniform2load_id_[varId] = inst->result_id();
-          continue;
-        } else {
-          // Copy load into most recent dominating block and remember it
-          replId = TakeNextId();
-          std::unique_ptr<Instruction> newLoad(new Instruction(
-              context(), SpvOpLoad, inst->type_id(), replId,
-              {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}}));
-          get_def_use_mgr()->AnalyzeInstDefUse(&*newLoad);
-          insertItr = insertItr.InsertBefore(std::move(newLoad));
-          ++insertItr;
-          uniform2load_id_[varId] = replId;
-        }
-      }
-      inst = ReplaceAndDeleteLoad(inst, replId, ptrInst);
-      modified = true;
-    }
-    // If we are outside of any control construct and entering one, remember
-    // the id of the merge block
-    if (mergeBlockId == 0) {
-      mergeBlockId = bp->MergeBlockIdIfAny();
-    }
-  }
-  return modified;
-}
-
-bool CommonUniformElimPass::CommonUniformLoadElimBlock(Function* func) {
-  bool modified = false;
-  for (auto& blk : *func) {
-    uniform2load_id_.clear();
-    for (Instruction* inst = &*blk.begin(); inst; inst = inst->NextNode()) {
-      if (inst->opcode() != SpvOpLoad) continue;
-      uint32_t varId;
-      Instruction* ptrInst = GetPtr(inst, &varId);
-      if (ptrInst->opcode() != SpvOpVariable) continue;
-      if (!IsUniformVar(varId)) continue;
-      if (!IsSamplerOrImageVar(varId)) continue;
-      if (HasUnsupportedDecorates(inst->result_id())) continue;
-      if (IsVolatileLoad(*inst)) continue;
-      uint32_t replId;
-      const auto uItr = uniform2load_id_.find(varId);
-      if (uItr != uniform2load_id_.end()) {
-        replId = uItr->second;
-      } else {
-        uniform2load_id_[varId] = inst->result_id();
-        continue;
-      }
-      inst = ReplaceAndDeleteLoad(inst, replId, ptrInst);
-      modified = true;
-    }
-  }
-  return modified;
-}
-
-bool CommonUniformElimPass::CommonExtractElimination(Function* func) {
-  // Find all composite ids with duplicate extracts.
-  for (auto bi = func->begin(); bi != func->end(); ++bi) {
-    for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
-      if (ii->opcode() != SpvOpCompositeExtract) continue;
-      // TODO(greg-lunarg): Support multiple indices
-      if (ii->NumInOperands() > 2) continue;
-      if (HasUnsupportedDecorates(ii->result_id())) continue;
-      uint32_t compId = ii->GetSingleWordInOperand(kExtractCompositeIdInIdx);
-      uint32_t idx = ii->GetSingleWordInOperand(kExtractIdx0InIdx);
-      comp2idx2inst_[compId][idx].push_back(&*ii);
-    }
-  }
-  // For all defs of ids with duplicate extracts, insert new extracts
-  // after def, and replace and delete old extracts
-  bool modified = false;
-  for (auto bi = func->begin(); bi != func->end(); ++bi) {
-    for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
-      const auto cItr = comp2idx2inst_.find(ii->result_id());
-      if (cItr == comp2idx2inst_.end()) continue;
-      for (auto idxItr : cItr->second) {
-        if (idxItr.second.size() < 2) continue;
-        uint32_t replId = TakeNextId();
-        std::unique_ptr<Instruction> newExtract(
-            idxItr.second.front()->Clone(context()));
-        newExtract->SetResultId(replId);
-        get_def_use_mgr()->AnalyzeInstDefUse(&*newExtract);
-        ++ii;
-        ii = ii.InsertBefore(std::move(newExtract));
-        for (auto instItr : idxItr.second) {
-          uint32_t resId = instItr->result_id();
-          context()->KillNamesAndDecorates(resId);
-          (void)context()->ReplaceAllUsesWith(resId, replId);
-          context()->KillInst(instItr);
-        }
-        modified = true;
-      }
-    }
-  }
-  return modified;
-}
-
-bool CommonUniformElimPass::EliminateCommonUniform(Function* func) {
-  bool modified = false;
-  modified |= UniformAccessChainConvert(func);
-  modified |= CommonUniformLoadElimination(func);
-  modified |= CommonExtractElimination(func);
-
-  modified |= CommonUniformLoadElimBlock(func);
-  return modified;
-}
-
-void CommonUniformElimPass::Initialize() {
-  // Clear collections.
-  comp2idx2inst_.clear();
-
-  // Initialize extension whitelist
-  InitExtensions();
-}
-
-bool CommonUniformElimPass::AllExtensionsSupported() const {
-  // If any extension not in whitelist, return false
-  for (auto& ei : get_module()->extensions()) {
-    const char* extName =
-        reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
-    if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
-      return false;
-  }
-  return true;
-}
-
-Pass::Status CommonUniformElimPass::ProcessImpl() {
-  // Assumes all control flow structured.
-  // TODO(greg-lunarg): Do SSA rewrite for non-structured control flow
-  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
-    return Status::SuccessWithoutChange;
-  // Assumes logical addressing only
-  // TODO(greg-lunarg): Add support for physical addressing
-  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
-    return Status::SuccessWithoutChange;
-  if (context()->get_feature_mgr()->HasCapability(
-          SpvCapabilityVariablePointersStorageBuffer))
-    return Status::SuccessWithoutChange;
-  // Do not process if any disallowed extensions are enabled
-  if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
-  // Do not process if module contains OpGroupDecorate. Additional
-  // support required in KillNamesAndDecorates().
-  // TODO(greg-lunarg): Add support for OpGroupDecorate
-  for (auto& ai : get_module()->annotations())
-    if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
-  // If non-32-bit integer type in module, terminate processing
-  // TODO(): Handle non-32-bit integer constants in access chains
-  for (const Instruction& inst : get_module()->types_values())
-    if (inst.opcode() == SpvOpTypeInt &&
-        inst.GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
-      return Status::SuccessWithoutChange;
-  // Process entry point functions
-  ProcessFunction pfn = [this](Function* fp) {
-    return EliminateCommonUniform(fp);
-  };
-  bool modified = context()->ProcessEntryPointCallTree(pfn);
-  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
-}
-
-CommonUniformElimPass::CommonUniformElimPass() = default;
-
-Pass::Status CommonUniformElimPass::Process() {
-  Initialize();
-  return ProcessImpl();
-}
-
-void CommonUniformElimPass::InitExtensions() {
-  extensions_whitelist_.clear();
-  extensions_whitelist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      // SPV_KHR_variable_pointers
-      //   Currently do not support extended pointer expressions
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_GOOGLE_user_type",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_NV_ray_tracing",
-      "SPV_EXT_fragment_invocation_density",
-  });
-}
-
-}  // namespace opt
-}  // namespace spvtools

+ 0 - 213
3rdparty/spirv-tools/source/opt/common_uniform_elim_pass.h

@@ -1,213 +0,0 @@
-// Copyright (c) 2016 The Khronos Group Inc.
-// Copyright (c) 2016 Valve Corporation
-// Copyright (c) 2016 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 SOURCE_OPT_COMMON_UNIFORM_ELIM_PASS_H_
-#define SOURCE_OPT_COMMON_UNIFORM_ELIM_PASS_H_
-
-#include <algorithm>
-#include <list>
-#include <map>
-#include <memory>
-#include <queue>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "source/opt/basic_block.h"
-#include "source/opt/decoration_manager.h"
-#include "source/opt/def_use_manager.h"
-#include "source/opt/ir_context.h"
-#include "source/opt/module.h"
-#include "source/opt/pass.h"
-
-namespace spvtools {
-namespace opt {
-
-// See optimizer.hpp for documentation.
-class CommonUniformElimPass : public Pass {
-  using cbb_ptr = const BasicBlock*;
-
- public:
-  using GetBlocksFunction =
-      std::function<std::vector<BasicBlock*>*(const BasicBlock*)>;
-
-  CommonUniformElimPass();
-
-  const char* name() const override { return "eliminate-common-uniform"; }
-  Status Process() override;
-
- private:
-  // Returns true if |opcode| is a non-ptr access chain op
-  bool IsNonPtrAccessChain(const SpvOp opcode) const;
-
-  // Returns true if |typeInst| is a sampler or image type or a struct
-  // containing one, recursively.
-  bool IsSamplerOrImageType(const Instruction* typeInst) const;
-
-  // Returns true if |varId| is a variable containing a sampler or image.
-  bool IsSamplerOrImageVar(uint32_t varId) const;
-
-  // Given a load or store pointed at by |ip|, return the top-most
-  // non-CopyObj in its pointer operand. Also return the base pointer
-  // in |objId|.
-  Instruction* GetPtr(Instruction* ip, uint32_t* objId);
-
-  // Return true if variable is uniform
-  bool IsUniformVar(uint32_t varId);
-
-  // Given the type id for a struct type, checks if the struct type
-  // or any struct member is volatile decorated
-  bool IsVolatileStruct(uint32_t type_id);
-
-  // Given an OpAccessChain instruction, return true
-  // if the accessed variable belongs to a volatile
-  // decorated object or member of a struct type
-  bool IsAccessChainToVolatileStructType(const Instruction& AccessChainInst);
-
-  // Given an OpLoad instruction, return true if
-  // OpLoad has a Volatile Memory Access flag or if
-  // the resulting type is a volatile decorated struct
-  bool IsVolatileLoad(const Instruction& loadInst);
-
-  // Return true if any uses of |id| are decorate ops.
-  bool HasUnsupportedDecorates(uint32_t id) const;
-
-  // Return true if all uses of |id| are only name or decorate ops.
-  bool HasOnlyNamesAndDecorates(uint32_t id) const;
-
-  // Delete inst if it has no uses. Assumes inst has a resultId.
-  void DeleteIfUseless(Instruction* inst);
-
-  // Replace all instances of load's id with replId and delete load
-  // and its access chain, if any
-  Instruction* ReplaceAndDeleteLoad(Instruction* loadInst, uint32_t replId,
-                                    Instruction* ptrInst);
-
-  // For the (constant index) access chain ptrInst, create an
-  // equivalent load and extract
-  void GenACLoadRepl(const Instruction* ptrInst,
-                     std::vector<std::unique_ptr<Instruction>>* newInsts,
-                     uint32_t* resultId);
-
-  // Return true if all indices are constant
-  bool IsConstantIndexAccessChain(Instruction* acp);
-
-  // Convert all uniform access chain loads into load/extract.
-  bool UniformAccessChainConvert(Function* func);
-
-  // Compute structured successors for function |func|.
-  // A block's structured successors are the blocks it branches to
-  // together with its declared merge block if it has one.
-  // When order matters, the merge block always appears first.
-  // This assures correct depth first search in the presence of early
-  // returns and kills. If the successor vector contain duplicates
-  // if the merge block, they are safely ignored by DFS.
-  //
-  // TODO(dnovillo): This pass computes structured successors slightly different
-  // than the implementation in class Pass. Can this be re-factored?
-  void ComputeStructuredSuccessors(Function* func);
-
-  // Compute structured block order for |func| into |structuredOrder|. This
-  // order has the property that dominators come before all blocks they
-  // dominate and merge blocks come after all blocks that are in the control
-  // constructs of their header.
-  //
-  // TODO(dnovillo): This pass computes structured order slightly different
-  // than the implementation in class Pass. Can this be re-factored?
-  void ComputeStructuredOrder(Function* func, std::list<BasicBlock*>* order);
-
-  // Eliminate loads of uniform variables which have previously been loaded.
-  // If first load is in control flow, move it to first block of function.
-  // Most effective if preceded by UniformAccessChainRemoval().
-  bool CommonUniformLoadElimination(Function* func);
-
-  // Eliminate loads of uniform sampler and image variables which have
-  // previously
-  // been loaded in the same block for types whose loads cannot cross blocks.
-  bool CommonUniformLoadElimBlock(Function* func);
-
-  // Eliminate duplicated extracts of same id. Extract may be moved to same
-  // block as the id definition. This is primarily intended for extracts
-  // from uniform loads. Most effective if preceded by
-  // CommonUniformLoadElimination().
-  bool CommonExtractElimination(Function* func);
-
-  // For function |func|, first change all uniform constant index
-  // access chain loads into equivalent composite extracts. Then consolidate
-  // identical uniform loads into one uniform load. Finally, consolidate
-  // identical uniform extracts into one uniform extract. This may require
-  // moving a load or extract to a point which dominates all uses.
-  // Return true if func is modified.
-  //
-  // This pass requires the function to have structured control flow ie shader
-  // capability. It also requires logical addressing ie Addresses capability
-  // is not enabled. It also currently does not support any extensions.
-  //
-  // This function currently only optimizes loads with a single index.
-  bool EliminateCommonUniform(Function* func);
-
-  // Initialize extensions whitelist
-  void InitExtensions();
-
-  // Return true if all extensions in this module are allowed by this pass.
-  bool AllExtensionsSupported() const;
-
-  // Return true if |op| is a decorate for non-type instruction
-  inline bool IsNonTypeDecorate(uint32_t op) const {
-    return (op == SpvOpDecorate || op == SpvOpDecorateId);
-  }
-
-  // Return true if |inst| is an instruction that loads uniform variable and
-  // can be replaced with other uniform load instruction.
-  bool IsUniformLoadToBeRemoved(Instruction* inst) {
-    if (inst->opcode() == SpvOpLoad) {
-      uint32_t varId;
-      Instruction* ptrInst = GetPtr(inst, &varId);
-      if (ptrInst->opcode() == SpvOpVariable && IsUniformVar(varId) &&
-          !IsSamplerOrImageVar(varId) &&
-          !HasUnsupportedDecorates(inst->result_id()) && !IsVolatileLoad(*inst))
-        return true;
-    }
-    return false;
-  }
-
-  void Initialize();
-  Pass::Status ProcessImpl();
-
-  // Map from uniform variable id to its common load id
-  std::unordered_map<uint32_t, uint32_t> uniform2load_id_;
-
-  // Map of extract composite ids to map of indices to insts
-  // TODO(greg-lunarg): Consider std::vector.
-  std::unordered_map<uint32_t,
-                     std::unordered_map<uint32_t, std::list<Instruction*>>>
-      comp2idx2inst_;
-
-  // Extensions supported by this pass.
-  std::unordered_set<std::string> extensions_whitelist_;
-
-  // Map from block to its structured successor blocks. See
-  // ComputeStructuredSuccessors() for definition.
-  std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
-      block2structured_succs_;
-};
-
-}  // namespace opt
-}  // namespace spvtools
-
-#endif  // SOURCE_OPT_COMMON_UNIFORM_ELIM_PASS_H_

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

@@ -629,39 +629,12 @@ bool InlinePass::GenInlineCode(
   return true;
   return true;
 }
 }
 
 
-bool InlinePass::IsInlinableFunctionCall(Instruction* inst) {
+bool InlinePass::IsInlinableFunctionCall(const Instruction* inst) {
   if (inst->opcode() != SpvOp::SpvOpFunctionCall) return false;
   if (inst->opcode() != SpvOp::SpvOpFunctionCall) return false;
   const uint32_t calleeFnId =
   const uint32_t calleeFnId =
       inst->GetSingleWordOperand(kSpvFunctionCallFunctionId);
       inst->GetSingleWordOperand(kSpvFunctionCallFunctionId);
   const auto ci = inlinable_.find(calleeFnId);
   const auto ci = inlinable_.find(calleeFnId);
-  if (ci == inlinable_.cend()) {
-    return false;
-  }
-
-  if (funcs_with_opkill_.count(calleeFnId) == 0) {
-    return true;
-  }
-
-  // We cannot inline into a continue construct if the function has an OpKill.
-  auto* cfg_analysis = context()->GetStructuredCFGAnalysis();
-  BasicBlock* bb = context()->get_instr_block(inst);
-  uint32_t loop_header_id = cfg_analysis->ContainingLoop(bb->id());
-  if (loop_header_id == 0) {
-    // Not in a loop, so we can inline.
-    return true;
-  }
-  BasicBlock* loop_header_bb = context()->get_instr_block(loop_header_id);
-  uint32_t loop_continue =
-      loop_header_bb->GetLoopMergeInst()->GetSingleWordOperand(1);
-
-  Function* caller_func = bb->GetParent();
-  DominatorAnalysis* dom = context()->GetDominatorAnalysis(caller_func);
-  if (dom->Dominates(loop_continue, bb->id())) {
-    // The function call is the continue construct and the callee contains an
-    // OpKill.
-    return false;
-  }
-  return true;
+  return ci != inlinable_.cend();
 }
 }
 
 
 void InlinePass::UpdateSucceedingPhis(
 void InlinePass::UpdateSucceedingPhis(
@@ -738,9 +711,6 @@ bool InlinePass::IsInlinableFunction(Function* func) {
   // the returns as a branch to the loop's merge block. However, this can only
   // the returns as a branch to the loop's merge block. However, this can only
   // done validly if the return was not in a loop in the original function.
   // done validly if the return was not in a loop in the original function.
   // Also remember functions with multiple (early) returns.
   // Also remember functions with multiple (early) returns.
-
-  // Do not inline functions with an OpKill because they may be inlined into a
-  // continue construct.
   AnalyzeReturns(func);
   AnalyzeReturns(func);
   if (no_return_in_loop_.find(func->result_id()) == no_return_in_loop_.cend()) {
   if (no_return_in_loop_.find(func->result_id()) == no_return_in_loop_.cend()) {
     return false;
     return false;
@@ -771,13 +741,6 @@ void InlinePass::InitializeInline() {
     }
     }
     // Compute inlinability
     // Compute inlinability
     if (IsInlinableFunction(&fn)) inlinable_.insert(fn.result_id());
     if (IsInlinableFunction(&fn)) inlinable_.insert(fn.result_id());
-
-    bool has_opkill = !fn.WhileEachInst(
-        [](Instruction* inst) { return inst->opcode() != SpvOpKill; });
-
-    if (has_opkill) {
-      funcs_with_opkill_.insert(fn.result_id());
-    }
   }
   }
 }
 }
 
 

+ 1 - 4
3rdparty/spirv-tools/source/opt/inline_pass.h

@@ -122,7 +122,7 @@ class InlinePass : public Pass {
                      UptrVectorIterator<BasicBlock> call_block_itr);
                      UptrVectorIterator<BasicBlock> call_block_itr);
 
 
   // Return true if |inst| is a function call that can be inlined.
   // Return true if |inst| is a function call that can be inlined.
-  bool IsInlinableFunctionCall(Instruction* inst);
+  bool IsInlinableFunctionCall(const Instruction* inst);
 
 
   // Return true if |func| does not have a return that is
   // Return true if |func| does not have a return that is
   // nested in a structured if, switch or loop.
   // nested in a structured if, switch or loop.
@@ -159,9 +159,6 @@ class InlinePass : public Pass {
   // Set of ids of functions with no returns in loop
   // Set of ids of functions with no returns in loop
   std::set<uint32_t> no_return_in_loop_;
   std::set<uint32_t> no_return_in_loop_;
 
 
-  // Set of ids of functions with no returns in loop
-  std::unordered_set<uint32_t> funcs_with_opkill_;
-
   // Set of ids of inlinable functions
   // Set of ids of inlinable functions
   std::set<uint32_t> inlinable_;
   std::set<uint32_t> inlinable_;
 
 

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

@@ -372,6 +372,11 @@ uint32_t Instruction::GetTypeComponent(uint32_t element) const {
   return subtype;
   return subtype;
 }
 }
 
 
+Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
+  i.get()->InsertBefore(this);
+  return i.release();
+}
+
 Instruction* Instruction::InsertBefore(
 Instruction* Instruction::InsertBefore(
     std::vector<std::unique_ptr<Instruction>>&& list) {
     std::vector<std::unique_ptr<Instruction>>&& list) {
   Instruction* first_node = list.front().get();
   Instruction* first_node = list.front().get();
@@ -382,11 +387,6 @@ Instruction* Instruction::InsertBefore(
   return first_node;
   return first_node;
 }
 }
 
 
-Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
-  i.get()->InsertBefore(this);
-  return i.release();
-}
-
 bool Instruction::IsValidBasePointer() const {
 bool Instruction::IsValidBasePointer() const {
   uint32_t tid = type_id();
   uint32_t tid = type_id();
   if (tid == 0) {
   if (tid == 0) {

+ 6 - 1
3rdparty/spirv-tools/source/opt/instruction.h

@@ -398,8 +398,13 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
   inline bool operator!=(const Instruction&) const;
   inline bool operator!=(const Instruction&) const;
   inline bool operator<(const Instruction&) const;
   inline bool operator<(const Instruction&) const;
 
 
-  Instruction* InsertBefore(std::vector<std::unique_ptr<Instruction>>&& list);
+  // Takes ownership of the instruction owned by |i| and inserts it immediately
+  // before |this|. Returns the insterted instruction.
   Instruction* InsertBefore(std::unique_ptr<Instruction>&& i);
   Instruction* InsertBefore(std::unique_ptr<Instruction>&& i);
+  // Takes ownership of the instructions in |list| and inserts them in order
+  // immediately before |this|.  Returns the first inserted instruction.
+  // Assumes the list is non-empty.
+  Instruction* InsertBefore(std::vector<std::unique_ptr<Instruction>>&& list);
   using utils::IntrusiveNodeBase<Instruction>::InsertBefore;
   using utils::IntrusiveNodeBase<Instruction>::InsertBefore;
 
 
   // Returns true if |this| is an instruction defining a constant, but not a
   // Returns true if |this| is an instruction defining a constant, but not a

+ 28 - 1
3rdparty/spirv-tools/source/opt/instrument_pass.cpp

@@ -264,6 +264,28 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
         GenFragCoordEltDebugOutputCode(
         GenFragCoordEltDebugOutputCode(
             base_offset_id, uint_frag_coord_inst->result_id(), u, builder);
             base_offset_id, uint_frag_coord_inst->result_id(), u, builder);
     } break;
     } break;
+    case SpvExecutionModelRayGenerationNV:
+    case SpvExecutionModelIntersectionNV:
+    case SpvExecutionModelAnyHitNV:
+    case SpvExecutionModelClosestHitNV:
+    case SpvExecutionModelMissNV:
+    case SpvExecutionModelCallableNV: {
+      // Load and store LaunchIdNV.
+      uint32_t launch_id = GenVarLoad(
+          context()->GetBuiltinInputVarId(SpvBuiltInLaunchIdNV), builder);
+      Instruction* x_launch_inst = builder->AddIdLiteralOp(
+          GetUintId(), SpvOpCompositeExtract, launch_id, 0);
+      Instruction* y_launch_inst = builder->AddIdLiteralOp(
+          GetUintId(), SpvOpCompositeExtract, launch_id, 1);
+      Instruction* z_launch_inst = builder->AddIdLiteralOp(
+          GetUintId(), SpvOpCompositeExtract, launch_id, 2);
+      GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdX,
+                              x_launch_inst->result_id(), builder);
+      GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdY,
+                              y_launch_inst->result_id(), builder);
+      GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdZ,
+                              z_launch_inst->result_id(), builder);
+    } break;
     default: { assert(false && "unsupported stage"); } break;
     default: { assert(false && "unsupported stage"); } break;
   }
   }
 }
 }
@@ -843,7 +865,12 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
       stage != SpvExecutionModelGeometry &&
       stage != SpvExecutionModelGeometry &&
       stage != SpvExecutionModelGLCompute &&
       stage != SpvExecutionModelGLCompute &&
       stage != SpvExecutionModelTessellationControl &&
       stage != SpvExecutionModelTessellationControl &&
-      stage != SpvExecutionModelTessellationEvaluation)
+      stage != SpvExecutionModelTessellationEvaluation &&
+      stage != SpvExecutionModelRayGenerationNV &&
+      stage != SpvExecutionModelIntersectionNV &&
+      stage != SpvExecutionModelAnyHitNV &&
+      stage != SpvExecutionModelClosestHitNV &&
+      stage != SpvExecutionModelMissNV && stage != SpvExecutionModelCallableNV)
     return false;
     return false;
   // Add together the roots of all entry points
   // Add together the roots of all entry points
   std::queue<uint32_t> roots;
   std::queue<uint32_t> roots;

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

@@ -683,7 +683,8 @@ uint32_t IRContext::GetBuiltinInputVarId(uint32_t builtin) {
         reg_type = type_mgr->GetRegisteredType(&uint_ty);
         reg_type = type_mgr->GetRegisteredType(&uint_ty);
         break;
         break;
       }
       }
-      case SpvBuiltInGlobalInvocationId: {
+      case SpvBuiltInGlobalInvocationId:
+      case SpvBuiltInLaunchIdNV: {
         analysis::Integer uint_ty(32, false);
         analysis::Integer uint_ty(32, false);
         analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
         analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
         analysis::Vector v3uint_ty(reg_uint_ty, 3);
         analysis::Vector v3uint_ty(reg_uint_ty, 3);

+ 43 - 35
3rdparty/spirv-tools/source/opt/merge_return_pass.cpp

@@ -224,6 +224,7 @@ void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
   return_inst->SetOpcode(SpvOpBranch);
   return_inst->SetOpcode(SpvOpBranch);
   return_inst->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {target}}});
   return_inst->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {target}}});
   context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst);
   context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst);
+  new_edges_[target_block].insert(block->id());
   cfg()->AddEdge(block->id(), target);
   cfg()->AddEdge(block->id(), target);
 }
 }
 
 
@@ -236,28 +237,18 @@ void MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source,
     context()->UpdateDefUse(inst);
     context()->UpdateDefUse(inst);
   });
   });
 
 
-  const auto& target_pred = cfg()->preds(target->id());
-  if (target_pred.size() == 1) {
-    MarkForNewPhiNodes(target, context()->get_instr_block(target_pred[0]));
-  } else {
-    // If the loop contained a break and a return, OpPhi instructions may be
-    // required starting from the dominator of the loop merge.
-    DominatorAnalysis* dom_tree =
-        context()->GetDominatorAnalysis(target->GetParent());
-    auto idom = dom_tree->ImmediateDominator(target);
-    if (idom) {
-      MarkForNewPhiNodes(target, idom);
-    }
-  }
+  // Store the immediate dominator for this block in case new phi nodes will be
+  // needed later.
+  RecordImmediateDominator(target);
 }
 }
 
 
 void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
 void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
                                             Instruction& inst) {
                                             Instruction& inst) {
   DominatorAnalysis* dom_tree =
   DominatorAnalysis* dom_tree =
       context()->GetDominatorAnalysis(merge_block->GetParent());
       context()->GetDominatorAnalysis(merge_block->GetParent());
-  BasicBlock* inst_bb = context()->get_instr_block(&inst);
 
 
   if (inst.result_id() != 0) {
   if (inst.result_id() != 0) {
+    BasicBlock* inst_bb = context()->get_instr_block(&inst);
     std::vector<Instruction*> users_to_update;
     std::vector<Instruction*> users_to_update;
     context()->get_def_use_mgr()->ForEachUser(
     context()->get_def_use_mgr()->ForEachUser(
         &inst,
         &inst,
@@ -295,12 +286,13 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
         IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
         IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
     uint32_t undef_id = Type2Undef(inst.type_id());
     uint32_t undef_id = Type2Undef(inst.type_id());
     std::vector<uint32_t> phi_operands;
     std::vector<uint32_t> phi_operands;
+    const std::set<uint32_t>& new_edges = new_edges_[merge_block];
 
 
     // Add the OpPhi operands. If the predecessor is a return block use undef,
     // Add the OpPhi operands. If the predecessor is a return block use undef,
     // otherwise use |inst|'s id.
     // otherwise use |inst|'s id.
     std::vector<uint32_t> preds = cfg()->preds(merge_block->id());
     std::vector<uint32_t> preds = cfg()->preds(merge_block->id());
     for (uint32_t pred_id : preds) {
     for (uint32_t pred_id : preds) {
-      if (return_blocks_.count(pred_id)) {
+      if (new_edges.count(pred_id)) {
         phi_operands.push_back(undef_id);
         phi_operands.push_back(undef_id);
       } else {
       } else {
         phi_operands.push_back(inst.result_id());
         phi_operands.push_back(inst.result_id());
@@ -417,6 +409,8 @@ bool MergeReturnPass::BreakFromConstruct(
   auto old_body_id = TakeNextId();
   auto old_body_id = TakeNextId();
   BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter);
   BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter);
   predicated->insert(old_body);
   predicated->insert(old_body);
+  cfg()->AddEdges(old_body);
+
   // If a return block is being split, mark the new body block also as a return
   // If a return block is being split, mark the new body block also as a return
   // block.
   // block.
   if (return_blocks_.count(block->id())) {
   if (return_blocks_.count(block->id())) {
@@ -456,14 +450,15 @@ bool MergeReturnPass::BreakFromConstruct(
   builder.AddConditionalBranch(load_id, merge_block->id(), old_body->id(),
   builder.AddConditionalBranch(load_id, merge_block->id(), old_body->id(),
                                old_body->id());
                                old_body->id());
 
 
-  // 3. Update OpPhi instructions in |merge_block|.
-  BasicBlock* merge_original_pred = MarkedSinglePred(merge_block);
-  if (merge_original_pred == nullptr) {
-    UpdatePhiNodes(block, merge_block);
-  } else if (merge_original_pred == block) {
-    MarkForNewPhiNodes(merge_block, old_body);
+  if (!new_edges_[merge_block].insert(block->id()).second) {
+    // It is possible that we already inserted a new edge to the merge block.
+    // If so, that edge now goes from |old_body| to |merge_block|.
+    new_edges_[merge_block].insert(old_body->id());
   }
   }
 
 
+  // 3. Update OpPhi instructions in |merge_block|.
+  UpdatePhiNodes(block, merge_block);
+
   // 4. Update the CFG.  We do this after updating the OpPhi instructions
   // 4. Update the CFG.  We do this after updating the OpPhi instructions
   // because |UpdatePhiNodes| assumes the edge from |block| has not been added
   // because |UpdatePhiNodes| assumes the edge from |block| has not been added
   // to the CFG yet.
   // to the CFG yet.
@@ -659,26 +654,37 @@ void MergeReturnPass::MergeReturnBlocks(
 }
 }
 
 
 void MergeReturnPass::AddNewPhiNodes() {
 void MergeReturnPass::AddNewPhiNodes() {
-  DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(function_);
   std::list<BasicBlock*> order;
   std::list<BasicBlock*> order;
   cfg()->ComputeStructuredOrder(function_, &*function_->begin(), &order);
   cfg()->ComputeStructuredOrder(function_, &*function_->begin(), &order);
 
 
   for (BasicBlock* bb : order) {
   for (BasicBlock* bb : order) {
-    BasicBlock* dominator = dom_tree->ImmediateDominator(bb);
-    if (dominator) {
-      AddNewPhiNodes(bb, new_merge_nodes_[bb], dominator->id());
-    }
+    AddNewPhiNodes(bb);
   }
   }
 }
 }
 
 
-void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb, BasicBlock* pred,
-                                     uint32_t header_id) {
+void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) {
+  // New phi nodes are needed for any id whose definition used to dominate |bb|,
+  // but no longer dominates |bb|.  These are found by walking the dominator
+  // tree starting at the original immediate dominator of |bb| and ending at its
+  // current dominator.
+
+  // Because we are walking the updated dominator tree it is important that the
+  // new phi nodes for the original dominators of |bb| have already been added.
+  // Otherwise some ids might be missed.  Consider the case where bb1 dominates
+  // bb2, and bb2 dominates bb3.  Suppose there are changes such that bb1 no
+  // longer dominates bb2 and the same for bb2 and bb3.  This algorithm will not
+  // look at the ids defined in bb1.  However, calling |AddNewPhiNodes(bb2)|
+  // first will add a phi node in bb2 for that value.  Then a call to
+  // |AddNewPhiNodes(bb3)| will process that value by processing the phi in bb2.
   DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(function_);
   DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(function_);
-  // Insert as a stopping point.  We do not have to add anything in the block
-  // or above because the header dominates |bb|.
 
 
-  BasicBlock* current_bb = pred;
-  while (current_bb != nullptr && current_bb->id() != header_id) {
+  BasicBlock* dominator = dom_tree->ImmediateDominator(bb);
+  if (dominator == nullptr) {
+    return;
+  }
+
+  BasicBlock* current_bb = new_merge_nodes_[bb];
+  while (current_bb != nullptr && current_bb != dominator) {
     for (Instruction& inst : *current_bb) {
     for (Instruction& inst : *current_bb) {
       CreatePhiNodesForInst(bb, inst);
       CreatePhiNodesForInst(bb, inst);
     }
     }
@@ -686,9 +692,11 @@ void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb, BasicBlock* pred,
   }
   }
 }
 }
 
 
-void MergeReturnPass::MarkForNewPhiNodes(BasicBlock* block,
-                                         BasicBlock* single_original_pred) {
-  new_merge_nodes_[block] = single_original_pred;
+void MergeReturnPass::RecordImmediateDominator(BasicBlock* block) {
+  DominatorAnalysis* dom_tree =
+      context()->GetDominatorAnalysis(block->GetParent());
+  auto idom = dom_tree->ImmediateDominator(block);
+  new_merge_nodes_[block] = idom;
 }
 }
 
 
 void MergeReturnPass::InsertAfterElement(BasicBlock* element,
 void MergeReturnPass::InsertAfterElement(BasicBlock* element,

+ 16 - 22
3rdparty/spirv-tools/source/opt/merge_return_pass.h

@@ -251,31 +251,19 @@ class MergeReturnPass : public MemPass {
   // there are no unreachable blocks in the control flow graph.
   // there are no unreachable blocks in the control flow graph.
   void AddNewPhiNodes();
   void AddNewPhiNodes();
 
 
-  // Creates any new phi nodes that are needed in |bb| now that |pred| is no
-  // longer the only block that preceedes |bb|.  |header_id| is the id of the
-  // basic block for the loop or selection construct that merges at |bb|.
-  void AddNewPhiNodes(BasicBlock* bb, BasicBlock* pred, uint32_t header_id);
+  // Creates any new phi nodes that are needed in |bb|.  |AddNewPhiNodes| must
+  // have already been called on the original dominators of |bb|.
+  void AddNewPhiNodes(BasicBlock* bb);
 
 
   // Saves |block| to a list of basic block that will require OpPhi nodes to be
   // Saves |block| to a list of basic block that will require OpPhi nodes to be
   // added by calling |AddNewPhiNodes|.  It is assumed that |block| used to have
   // added by calling |AddNewPhiNodes|.  It is assumed that |block| used to have
   // a single predecessor, |single_original_pred|, but now has more.
   // a single predecessor, |single_original_pred|, but now has more.
-  void MarkForNewPhiNodes(BasicBlock* block, BasicBlock* single_original_pred);
-
-  // Return the original single predcessor of |block| if it was flagged as
-  // having a single predecessor.  |nullptr| is returned otherwise.
-  BasicBlock* MarkedSinglePred(BasicBlock* block) {
-    auto it = new_merge_nodes_.find(block);
-    if (it != new_merge_nodes_.end()) {
-      return it->second;
-    } else {
-      return nullptr;
-    }
-  }
+  void RecordImmediateDominator(BasicBlock* block);
 
 
   // Modifies existing OpPhi instruction in |target| block to account for the
   // Modifies existing OpPhi instruction in |target| block to account for the
   // new edge from |new_source|.  The value for that edge will be an Undef. If
   // new edge from |new_source|.  The value for that edge will be an Undef. If
   // |target| only had a single predecessor, then it is marked as needing new
   // |target| only had a single predecessor, then it is marked as needing new
-  // phi nodes.  See |MarkForNewPhiNodes|.
+  // phi nodes.  See |RecordImmediateDominator|.
   //
   //
   // The CFG must not include the edge from |new_source| to |target| yet.
   // The CFG must not include the edge from |new_source| to |target| yet.
   void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
   void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
@@ -301,6 +289,11 @@ class MergeReturnPass : public MemPass {
   // |merge_target| as the merge node.
   // |merge_target| as the merge node.
   void CreateDummyLoop(BasicBlock* merge_target);
   void CreateDummyLoop(BasicBlock* merge_target);
 
 
+  // Returns true if |function| has an unreachable block that is not a continue
+  // target that simply branches back to the header, or a merge block containing
+  // 1 instruction which is OpUnreachable.
+  bool HasNontrivialUnreachableBlocks(Function* function);
+
   // A stack used to keep track of the innermost contain loop and selection
   // A stack used to keep track of the innermost contain loop and selection
   // constructs.
   // constructs.
   std::vector<StructuredControlState> state_;
   std::vector<StructuredControlState> state_;
@@ -324,12 +317,13 @@ class MergeReturnPass : public MemPass {
   // after processing the current function.
   // after processing the current function.
   BasicBlock* final_return_block_;
   BasicBlock* final_return_block_;
 
 
-  // This map contains the set of nodes that use to have a single predcessor,
-  // but now have more.  They will need new OpPhi nodes.  For each of the nodes,
-  // it is mapped to it original single predcessor.  It is assumed there are no
-  // values that will need a phi on the new edges.
+  // This is a map from a node to its original immediate dominator.  This is
+  // used to determine which values will require a new phi node.
   std::unordered_map<BasicBlock*, BasicBlock*> new_merge_nodes_;
   std::unordered_map<BasicBlock*, BasicBlock*> new_merge_nodes_;
-  bool HasNontrivialUnreachableBlocks(Function* function);
+
+  // A map from a basic block, bb, to the set of basic blocks which represent
+  // the new edges that reach |bb|.
+  std::unordered_map<BasicBlock*, std::set<uint32_t>> new_edges_;
 
 
   // Contains all return blocks that are merged. This is set is populated while
   // Contains all return blocks that are merged. This is set is populated while
   // processing structured blocks and used to properly construct OpPhi
   // processing structured blocks and used to properly construct OpPhi

+ 0 - 11
3rdparty/spirv-tools/source/opt/optimizer.cpp

@@ -186,8 +186,6 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
       .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateDeadBranchElimPass())
       .RegisterPass(CreateBlockMergePass())
       .RegisterPass(CreateBlockMergePass())
       .RegisterPass(CreateSimplificationPass());
       .RegisterPass(CreateSimplificationPass());
-  // Currently exposing driver bugs resulting in crashes (#946)
-  // .RegisterPass(CreateCommonUniformElimPass())
 }
 }
 
 
 Optimizer& Optimizer::RegisterSizePasses() {
 Optimizer& Optimizer::RegisterSizePasses() {
@@ -215,8 +213,6 @@ Optimizer& Optimizer::RegisterSizePasses() {
       .RegisterPass(CreateDeadInsertElimPass())
       .RegisterPass(CreateDeadInsertElimPass())
       .RegisterPass(CreateRedundancyEliminationPass())
       .RegisterPass(CreateRedundancyEliminationPass())
       .RegisterPass(CreateCFGCleanupPass())
       .RegisterPass(CreateCFGCleanupPass())
-      // Currently exposing driver bugs resulting in crashes (#946)
-      // .RegisterPass(CreateCommonUniformElimPass())
       .RegisterPass(CreateAggressiveDCEPass());
       .RegisterPass(CreateAggressiveDCEPass());
 }
 }
 
 
@@ -340,8 +336,6 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
     RegisterPass(CreateEliminateDeadFunctionsPass());
     RegisterPass(CreateEliminateDeadFunctionsPass());
   } else if (pass_name == "eliminate-local-multi-store") {
   } else if (pass_name == "eliminate-local-multi-store") {
     RegisterPass(CreateLocalMultiStoreElimPass());
     RegisterPass(CreateLocalMultiStoreElimPass());
-  } else if (pass_name == "eliminate-common-uniform") {
-    RegisterPass(CreateCommonUniformElimPass());
   } else if (pass_name == "eliminate-dead-const") {
   } else if (pass_name == "eliminate-dead-const") {
     RegisterPass(CreateEliminateDeadConstantPass());
     RegisterPass(CreateEliminateDeadConstantPass());
   } else if (pass_name == "eliminate-dead-inserts") {
   } else if (pass_name == "eliminate-dead-inserts") {
@@ -713,11 +707,6 @@ Optimizer::PassToken CreateRedundantLineInfoElimPass() {
       MakeUnique<opt::ProcessLinesPass>(opt::kLinesEliminateDeadLines));
       MakeUnique<opt::ProcessLinesPass>(opt::kLinesEliminateDeadLines));
 }
 }
 
 
-Optimizer::PassToken CreateCommonUniformElimPass() {
-  return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::CommonUniformElimPass>());
-}
-
 Optimizer::PassToken CreateCompactIdsPass() {
 Optimizer::PassToken CreateCompactIdsPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
   return MakeUnique<Optimizer::PassToken::Impl>(
       MakeUnique<opt::CompactIdsPass>());
       MakeUnique<opt::CompactIdsPass>());

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

@@ -23,7 +23,6 @@
 #include "source/opt/cfg_cleanup_pass.h"
 #include "source/opt/cfg_cleanup_pass.h"
 #include "source/opt/code_sink.h"
 #include "source/opt/code_sink.h"
 #include "source/opt/combine_access_chains.h"
 #include "source/opt/combine_access_chains.h"
-#include "source/opt/common_uniform_elim_pass.h"
 #include "source/opt/compact_ids_pass.h"
 #include "source/opt/compact_ids_pass.h"
 #include "source/opt/copy_prop_arrays.h"
 #include "source/opt/copy_prop_arrays.h"
 #include "source/opt/dead_branch_elim_pass.h"
 #include "source/opt/dead_branch_elim_pass.h"

+ 37 - 0
3rdparty/spirv-tools/source/val/validate_type.cpp

@@ -190,6 +190,35 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _,
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 
+bool ContainsOpaqueType(ValidationState_t& _, const Instruction* str) {
+  const size_t elem_type_index = 1;
+  uint32_t elem_type_id;
+  Instruction* elem_type;
+
+  if (spvOpcodeIsBaseOpaqueType(str->opcode())) {
+    return true;
+  }
+
+  switch (str->opcode()) {
+    case SpvOpTypeArray:
+    case SpvOpTypeRuntimeArray:
+      elem_type_id = str->GetOperandAs<uint32_t>(elem_type_index);
+      elem_type = _.FindDef(elem_type_id);
+      return ContainsOpaqueType(_, elem_type);
+    case SpvOpTypeStruct:
+      for (size_t member_type_index = 1;
+           member_type_index < str->operands().size(); ++member_type_index) {
+        auto member_type_id = str->GetOperandAs<uint32_t>(member_type_index);
+        auto member_type = _.FindDef(member_type_id);
+        if (ContainsOpaqueType(_, member_type)) return true;
+      }
+      break;
+    default:
+      break;
+  }
+  return false;
+}
+
 spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
 spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
   const uint32_t struct_id = inst->GetOperandAs<uint32_t>(0);
   const uint32_t struct_id = inst->GetOperandAs<uint32_t>(0);
   for (size_t member_type_index = 1;
   for (size_t member_type_index = 1;
@@ -289,6 +318,14 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
   if (num_builtin_members > 0) {
   if (num_builtin_members > 0) {
     _.RegisterStructTypeWithBuiltInMember(struct_id);
     _.RegisterStructTypeWithBuiltInMember(struct_id);
   }
   }
+
+  if (spvIsVulkanEnv(_.context()->target_env) &&
+      !_.options()->before_hlsl_legalization && ContainsOpaqueType(_, inst)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "In " << spvLogStringForEnv(_.context()->target_env)
+           << ", OpTypeStruct must not contain an opaque type.";
+  }
+
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 

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

@@ -24,7 +24,6 @@ add_spvtools_unittest(TARGET opt
        cfg_test.cpp
        cfg_test.cpp
        code_sink_test.cpp
        code_sink_test.cpp
        combine_access_chains_test.cpp
        combine_access_chains_test.cpp
-       common_uniform_elim_test.cpp
        compact_ids_test.cpp
        compact_ids_test.cpp
        constant_manager_test.cpp
        constant_manager_test.cpp
        copy_prop_array_test.cpp
        copy_prop_array_test.cpp

+ 0 - 1479
3rdparty/spirv-tools/test/opt/common_uniform_elim_test.cpp

@@ -1,1479 +0,0 @@
-// Copyright (c) 2017 Valve Corporation
-// Copyright (c) 2017 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 <string>
-
-#include "test/opt/pass_fixture.h"
-
-namespace spvtools {
-namespace opt {
-namespace {
-
-using CommonUniformElimTest = PassTest<::testing::Test>;
-
-TEST_F(CommonUniformElimTest, Basic1) {
-  // Note: This test exemplifies the following:
-  // - Common uniform (%_) load floated to nearest non-controlled block
-  // - Common extract (g_F) floated to non-controlled block
-  // - Non-common extract (g_F2) not floated, but common uniform load shared
-  //
-  // #version 140
-  // in vec4 BaseColor;
-  // in float fi;
-  //
-  // layout(std140) uniform U_t
-  // {
-  //     float g_F;
-  //     float g_F2;
-  // } ;
-  //
-  // void main()
-  // {
-  //     vec4 v = BaseColor;
-  //     if (fi > 0) {
-  //       v = v * g_F;
-  //     }
-  //     else {
-  //       float f2 = g_F2 - g_F;
-  //       v = v * f2;
-  //     }
-  //     gl_FragColor = v;
-  // }
-
-  const std::string predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %fi "fi"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_F"
-OpMemberName %U_t 1 "g_F2"
-OpName %_ ""
-OpName %f2 "f2"
-OpName %gl_FragColor "gl_FragColor"
-OpMemberDecorate %U_t 0 Offset 0
-OpMemberDecorate %U_t 1 Offset 4
-OpDecorate %U_t Block
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%11 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%fi = OpVariable %_ptr_Input_float Input
-%float_0 = OpConstant %float 0
-%bool = OpTypeBool
-%U_t = OpTypeStruct %float %float
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%_ptr_Function_float = OpTypePointer Function %float
-%int_1 = OpConstant %int 1
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %11
-%26 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%f2 = OpVariable %_ptr_Function_float Function
-%27 = OpLoad %v4float %BaseColor
-OpStore %v %27
-%28 = OpLoad %float %fi
-%29 = OpFOrdGreaterThan %bool %28 %float_0
-OpSelectionMerge %30 None
-OpBranchConditional %29 %31 %32
-%31 = OpLabel
-%33 = OpLoad %v4float %v
-%34 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%35 = OpLoad %float %34
-%36 = OpVectorTimesScalar %v4float %33 %35
-OpStore %v %36
-OpBranch %30
-%32 = OpLabel
-%37 = OpAccessChain %_ptr_Uniform_float %_ %int_1
-%38 = OpLoad %float %37
-%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%40 = OpLoad %float %39
-%41 = OpFSub %float %38 %40
-OpStore %f2 %41
-%42 = OpLoad %v4float %v
-%43 = OpLoad %float %f2
-%44 = OpVectorTimesScalar %v4float %42 %43
-OpStore %v %44
-OpBranch %30
-%30 = OpLabel
-%45 = OpLoad %v4float %v
-OpStore %gl_FragColor %45
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %11
-%26 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%f2 = OpVariable %_ptr_Function_float Function
-%52 = OpLoad %U_t %_
-%53 = OpCompositeExtract %float %52 0
-%27 = OpLoad %v4float %BaseColor
-OpStore %v %27
-%28 = OpLoad %float %fi
-%29 = OpFOrdGreaterThan %bool %28 %float_0
-OpSelectionMerge %30 None
-OpBranchConditional %29 %31 %32
-%31 = OpLabel
-%33 = OpLoad %v4float %v
-%36 = OpVectorTimesScalar %v4float %33 %53
-OpStore %v %36
-OpBranch %30
-%32 = OpLabel
-%49 = OpCompositeExtract %float %52 1
-%41 = OpFSub %float %49 %53
-OpStore %f2 %41
-%42 = OpLoad %v4float %v
-%43 = OpLoad %float %f2
-%44 = OpVectorTimesScalar %v4float %42 %43
-OpStore %v %44
-OpBranch %30
-%30 = OpLabel
-%45 = OpLoad %v4float %v
-OpStore %gl_FragColor %45
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<CommonUniformElimPass>(predefs + before,
-                                               predefs + after, true, true);
-}
-
-TEST_F(CommonUniformElimTest, Basic2) {
-  // Note: This test exemplifies the following:
-  // - Common uniform (%_) load floated to nearest non-controlled block
-  // - Common extract (g_F) floated to non-controlled block
-  // - Non-common extract (g_F2) not floated, but common uniform load shared
-  //
-  // #version 140
-  // in vec4 BaseColor;
-  // in float fi;
-  // in float fi2;
-  //
-  // layout(std140) uniform U_t
-  // {
-  //     float g_F;
-  //     float g_F2;
-  // } ;
-  //
-  // void main()
-  // {
-  //     float f = fi;
-  //     if (f < 0)
-  //       f = -f;
-  //     if (fi2 > 0) {
-  //       f = f * g_F;
-  //     }
-  //     else {
-  //       f = g_F2 - g_F;
-  //     }
-  //     gl_FragColor = f * BaseColor;
-  // }
-
-  const std::string predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %fi %fi2 %gl_FragColor %BaseColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-OpName %main "main"
-OpName %f "f"
-OpName %fi "fi"
-OpName %fi2 "fi2"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_F"
-OpMemberName %U_t 1 "g_F2"
-OpName %_ ""
-OpName %gl_FragColor "gl_FragColor"
-OpName %BaseColor "BaseColor"
-OpMemberDecorate %U_t 0 Offset 0
-OpMemberDecorate %U_t 1 Offset 4
-OpDecorate %U_t Block
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%11 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Function_float = OpTypePointer Function %float
-%_ptr_Input_float = OpTypePointer Input %float
-%fi = OpVariable %_ptr_Input_float Input
-%float_0 = OpConstant %float 0
-%bool = OpTypeBool
-%fi2 = OpVariable %_ptr_Input_float Input
-%U_t = OpTypeStruct %float %float
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%int_1 = OpConstant %int 1
-%v4float = OpTypeVector %float 4
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %11
-%25 = OpLabel
-%f = OpVariable %_ptr_Function_float Function
-%26 = OpLoad %float %fi
-OpStore %f %26
-%27 = OpLoad %float %f
-%28 = OpFOrdLessThan %bool %27 %float_0
-OpSelectionMerge %29 None
-OpBranchConditional %28 %30 %29
-%30 = OpLabel
-%31 = OpLoad %float %f
-%32 = OpFNegate %float %31
-OpStore %f %32
-OpBranch %29
-%29 = OpLabel
-%33 = OpLoad %float %fi2
-%34 = OpFOrdGreaterThan %bool %33 %float_0
-OpSelectionMerge %35 None
-OpBranchConditional %34 %36 %37
-%36 = OpLabel
-%38 = OpLoad %float %f
-%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%40 = OpLoad %float %39
-%41 = OpFMul %float %38 %40
-OpStore %f %41
-OpBranch %35
-%37 = OpLabel
-%42 = OpAccessChain %_ptr_Uniform_float %_ %int_1
-%43 = OpLoad %float %42
-%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%45 = OpLoad %float %44
-%46 = OpFSub %float %43 %45
-OpStore %f %46
-OpBranch %35
-%35 = OpLabel
-%47 = OpLoad %v4float %BaseColor
-%48 = OpLoad %float %f
-%49 = OpVectorTimesScalar %v4float %47 %48
-OpStore %gl_FragColor %49
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %11
-%25 = OpLabel
-%f = OpVariable %_ptr_Function_float Function
-%26 = OpLoad %float %fi
-OpStore %f %26
-%27 = OpLoad %float %f
-%28 = OpFOrdLessThan %bool %27 %float_0
-OpSelectionMerge %29 None
-OpBranchConditional %28 %30 %29
-%30 = OpLabel
-%31 = OpLoad %float %f
-%32 = OpFNegate %float %31
-OpStore %f %32
-OpBranch %29
-%29 = OpLabel
-%56 = OpLoad %U_t %_
-%57 = OpCompositeExtract %float %56 0
-%33 = OpLoad %float %fi2
-%34 = OpFOrdGreaterThan %bool %33 %float_0
-OpSelectionMerge %35 None
-OpBranchConditional %34 %36 %37
-%36 = OpLabel
-%38 = OpLoad %float %f
-%41 = OpFMul %float %38 %57
-OpStore %f %41
-OpBranch %35
-%37 = OpLabel
-%53 = OpCompositeExtract %float %56 1
-%46 = OpFSub %float %53 %57
-OpStore %f %46
-OpBranch %35
-%35 = OpLabel
-%47 = OpLoad %v4float %BaseColor
-%48 = OpLoad %float %f
-%49 = OpVectorTimesScalar %v4float %47 %48
-OpStore %gl_FragColor %49
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<CommonUniformElimPass>(predefs + before,
-                                               predefs + after, true, true);
-}
-
-TEST_F(CommonUniformElimTest, Basic3) {
-  // Note: This test exemplifies the following:
-  // - Existing common uniform (%_) load kept in place and shared
-  //
-  // #version 140
-  // in vec4 BaseColor;
-  // in float fi;
-  //
-  // layout(std140) uniform U_t
-  // {
-  //     bool g_B;
-  //     float g_F;
-  // } ;
-  //
-  // void main()
-  // {
-  //     vec4 v = BaseColor;
-  //     if (g_B)
-  //       v = v * g_F;
-  //     gl_FragColor = v;
-  // }
-
-  const std::string predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor %fi
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_B"
-OpMemberName %U_t 1 "g_F"
-OpName %_ ""
-OpName %gl_FragColor "gl_FragColor"
-OpName %fi "fi"
-OpMemberDecorate %U_t 0 Offset 0
-OpMemberDecorate %U_t 1 Offset 4
-OpDecorate %U_t Block
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%uint = OpTypeInt 32 0
-%U_t = OpTypeStruct %uint %float
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%bool = OpTypeBool
-%uint_0 = OpConstant %uint 0
-%int_1 = OpConstant %int 1
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-%_ptr_Input_float = OpTypePointer Input %float
-%fi = OpVariable %_ptr_Input_float Input
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %10
-%26 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%27 = OpLoad %v4float %BaseColor
-OpStore %v %27
-%28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
-%29 = OpLoad %uint %28
-%30 = OpINotEqual %bool %29 %uint_0
-OpSelectionMerge %31 None
-OpBranchConditional %30 %32 %31
-%32 = OpLabel
-%33 = OpLoad %v4float %v
-%34 = OpAccessChain %_ptr_Uniform_float %_ %int_1
-%35 = OpLoad %float %34
-%36 = OpVectorTimesScalar %v4float %33 %35
-OpStore %v %36
-OpBranch %31
-%31 = OpLabel
-%37 = OpLoad %v4float %v
-OpStore %gl_FragColor %37
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %10
-%26 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%27 = OpLoad %v4float %BaseColor
-OpStore %v %27
-%38 = OpLoad %U_t %_
-%39 = OpCompositeExtract %uint %38 0
-%30 = OpINotEqual %bool %39 %uint_0
-OpSelectionMerge %31 None
-OpBranchConditional %30 %32 %31
-%32 = OpLabel
-%33 = OpLoad %v4float %v
-%41 = OpCompositeExtract %float %38 1
-%36 = OpVectorTimesScalar %v4float %33 %41
-OpStore %v %36
-OpBranch %31
-%31 = OpLabel
-%37 = OpLoad %v4float %v
-OpStore %gl_FragColor %37
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<CommonUniformElimPass>(predefs + before,
-                                               predefs + after, true, true);
-}
-
-TEST_F(CommonUniformElimTest, Loop) {
-  // Note: This test exemplifies the following:
-  // - Common extract (g_F) shared between two loops
-  // #version 140
-  // in vec4 BC;
-  // in vec4 BC2;
-  //
-  // layout(std140) uniform U_t
-  // {
-  //     float g_F;
-  // } ;
-  //
-  // void main()
-  // {
-  //     vec4 v = BC;
-  //     for (int i = 0; i < 4; i++)
-  //       v[i] = v[i] / g_F;
-  //     vec4 v2 = BC2;
-  //     for (int i = 0; i < 4; i++)
-  //       v2[i] = v2[i] * g_F;
-  //     gl_FragColor = v + v2;
-  // }
-
-  const std::string predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BC %BC2 %gl_FragColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-OpName %main "main"
-OpName %v "v"
-OpName %BC "BC"
-OpName %i "i"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_F"
-OpName %_ ""
-OpName %v2 "v2"
-OpName %BC2 "BC2"
-OpName %i_0 "i"
-OpName %gl_FragColor "gl_FragColor"
-OpMemberDecorate %U_t 0 Offset 0
-OpDecorate %U_t Block
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%13 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BC = OpVariable %_ptr_Input_v4float Input
-%int = OpTypeInt 32 1
-%_ptr_Function_int = OpTypePointer Function %int
-%int_0 = OpConstant %int 0
-%int_4 = OpConstant %int 4
-%bool = OpTypeBool
-%_ptr_Function_float = OpTypePointer Function %float
-%U_t = OpTypeStruct %float
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%int_1 = OpConstant %int 1
-%BC2 = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %13
-%28 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%i = OpVariable %_ptr_Function_int Function
-%v2 = OpVariable %_ptr_Function_v4float Function
-%i_0 = OpVariable %_ptr_Function_int Function
-%29 = OpLoad %v4float %BC
-OpStore %v %29
-OpStore %i %int_0
-OpBranch %30
-%30 = OpLabel
-OpLoopMerge %31 %32 None
-OpBranch %33
-%33 = OpLabel
-%34 = OpLoad %int %i
-%35 = OpSLessThan %bool %34 %int_4
-OpBranchConditional %35 %36 %31
-%36 = OpLabel
-%37 = OpLoad %int %i
-%38 = OpLoad %int %i
-%39 = OpAccessChain %_ptr_Function_float %v %38
-%40 = OpLoad %float %39
-%41 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%42 = OpLoad %float %41
-%43 = OpFDiv %float %40 %42
-%44 = OpAccessChain %_ptr_Function_float %v %37
-OpStore %44 %43
-OpBranch %32
-%32 = OpLabel
-%45 = OpLoad %int %i
-%46 = OpIAdd %int %45 %int_1
-OpStore %i %46
-OpBranch %30
-%31 = OpLabel
-%47 = OpLoad %v4float %BC2
-OpStore %v2 %47
-OpStore %i_0 %int_0
-OpBranch %48
-%48 = OpLabel
-OpLoopMerge %49 %50 None
-OpBranch %51
-%51 = OpLabel
-%52 = OpLoad %int %i_0
-%53 = OpSLessThan %bool %52 %int_4
-OpBranchConditional %53 %54 %49
-%54 = OpLabel
-%55 = OpLoad %int %i_0
-%56 = OpLoad %int %i_0
-%57 = OpAccessChain %_ptr_Function_float %v2 %56
-%58 = OpLoad %float %57
-%59 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%60 = OpLoad %float %59
-%61 = OpFMul %float %58 %60
-%62 = OpAccessChain %_ptr_Function_float %v2 %55
-OpStore %62 %61
-OpBranch %50
-%50 = OpLabel
-%63 = OpLoad %int %i_0
-%64 = OpIAdd %int %63 %int_1
-OpStore %i_0 %64
-OpBranch %48
-%49 = OpLabel
-%65 = OpLoad %v4float %v
-%66 = OpLoad %v4float %v2
-%67 = OpFAdd %v4float %65 %66
-OpStore %gl_FragColor %67
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %13
-%28 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%i = OpVariable %_ptr_Function_int Function
-%v2 = OpVariable %_ptr_Function_v4float Function
-%i_0 = OpVariable %_ptr_Function_int Function
-%72 = OpLoad %U_t %_
-%73 = OpCompositeExtract %float %72 0
-%29 = OpLoad %v4float %BC
-OpStore %v %29
-OpStore %i %int_0
-OpBranch %30
-%30 = OpLabel
-OpLoopMerge %31 %32 None
-OpBranch %33
-%33 = OpLabel
-%34 = OpLoad %int %i
-%35 = OpSLessThan %bool %34 %int_4
-OpBranchConditional %35 %36 %31
-%36 = OpLabel
-%37 = OpLoad %int %i
-%38 = OpLoad %int %i
-%39 = OpAccessChain %_ptr_Function_float %v %38
-%40 = OpLoad %float %39
-%43 = OpFDiv %float %40 %73
-%44 = OpAccessChain %_ptr_Function_float %v %37
-OpStore %44 %43
-OpBranch %32
-%32 = OpLabel
-%45 = OpLoad %int %i
-%46 = OpIAdd %int %45 %int_1
-OpStore %i %46
-OpBranch %30
-%31 = OpLabel
-%47 = OpLoad %v4float %BC2
-OpStore %v2 %47
-OpStore %i_0 %int_0
-OpBranch %48
-%48 = OpLabel
-OpLoopMerge %49 %50 None
-OpBranch %51
-%51 = OpLabel
-%52 = OpLoad %int %i_0
-%53 = OpSLessThan %bool %52 %int_4
-OpBranchConditional %53 %54 %49
-%54 = OpLabel
-%55 = OpLoad %int %i_0
-%56 = OpLoad %int %i_0
-%57 = OpAccessChain %_ptr_Function_float %v2 %56
-%58 = OpLoad %float %57
-%61 = OpFMul %float %58 %73
-%62 = OpAccessChain %_ptr_Function_float %v2 %55
-OpStore %62 %61
-OpBranch %50
-%50 = OpLabel
-%63 = OpLoad %int %i_0
-%64 = OpIAdd %int %63 %int_1
-OpStore %i_0 %64
-OpBranch %48
-%49 = OpLabel
-%65 = OpLoad %v4float %v
-%66 = OpLoad %v4float %v2
-%67 = OpFAdd %v4float %65 %66
-OpStore %gl_FragColor %67
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<CommonUniformElimPass>(predefs + before,
-                                               predefs + after, true, true);
-}
-
-TEST_F(CommonUniformElimTest, Volatile1) {
-  // Note: This test exemplifies the following:
-  // - Same test as Basic1 with the exception that
-  //   the Load of g_F in else-branch is volatile
-  // - Common uniform (%_) load floated to nearest non-controlled block
-  //
-  // #version 140
-  // in vec4 BaseColor;
-  // in float fi;
-  //
-  // layout(std140) uniform U_t
-  // {
-  //     float g_F;
-  //     float g_F2;
-  // } ;
-  //
-  // void main()
-  // {
-  //     vec4 v = BaseColor;
-  //     if (fi > 0) {
-  //       v = v * g_F;
-  //     }
-  //     else {
-  //       float f2 = g_F2 - g_F;
-  //       v = v * f2;
-  //     }
-  //     gl_FragColor = v;
-  // }
-
-  const std::string predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %fi "fi"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_F"
-OpMemberName %U_t 1 "g_F2"
-OpName %_ ""
-OpName %f2 "f2"
-OpName %gl_FragColor "gl_FragColor"
-OpMemberDecorate %U_t 0 Offset 0
-OpMemberDecorate %U_t 1 Offset 4
-OpDecorate %U_t Block
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%11 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%fi = OpVariable %_ptr_Input_float Input
-%float_0 = OpConstant %float 0
-%bool = OpTypeBool
-%U_t = OpTypeStruct %float %float
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%_ptr_Function_float = OpTypePointer Function %float
-%int_1 = OpConstant %int 1
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %11
-%26 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%f2 = OpVariable %_ptr_Function_float Function
-%27 = OpLoad %v4float %BaseColor
-OpStore %v %27
-%28 = OpLoad %float %fi
-%29 = OpFOrdGreaterThan %bool %28 %float_0
-OpSelectionMerge %30 None
-OpBranchConditional %29 %31 %32
-%31 = OpLabel
-%33 = OpLoad %v4float %v
-%34 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%35 = OpLoad %float %34
-%36 = OpVectorTimesScalar %v4float %33 %35
-OpStore %v %36
-OpBranch %30
-%32 = OpLabel
-%37 = OpAccessChain %_ptr_Uniform_float %_ %int_1
-%38 = OpLoad %float %37
-%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%40 = OpLoad %float %39 Volatile
-%41 = OpFSub %float %38 %40
-OpStore %f2 %41
-%42 = OpLoad %v4float %v
-%43 = OpLoad %float %f2
-%44 = OpVectorTimesScalar %v4float %42 %43
-OpStore %v %44
-OpBranch %30
-%30 = OpLabel
-%45 = OpLoad %v4float %v
-OpStore %gl_FragColor %45
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %11
-%26 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%f2 = OpVariable %_ptr_Function_float Function
-%50 = OpLoad %U_t %_
-%27 = OpLoad %v4float %BaseColor
-OpStore %v %27
-%28 = OpLoad %float %fi
-%29 = OpFOrdGreaterThan %bool %28 %float_0
-OpSelectionMerge %30 None
-OpBranchConditional %29 %31 %32
-%31 = OpLabel
-%33 = OpLoad %v4float %v
-%47 = OpCompositeExtract %float %50 0
-%36 = OpVectorTimesScalar %v4float %33 %47
-OpStore %v %36
-OpBranch %30
-%32 = OpLabel
-%49 = OpCompositeExtract %float %50 1
-%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%40 = OpLoad %float %39 Volatile
-%41 = OpFSub %float %49 %40
-OpStore %f2 %41
-%42 = OpLoad %v4float %v
-%43 = OpLoad %float %f2
-%44 = OpVectorTimesScalar %v4float %42 %43
-OpStore %v %44
-OpBranch %30
-%30 = OpLabel
-%45 = OpLoad %v4float %v
-OpStore %gl_FragColor %45
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<CommonUniformElimPass>(predefs + before,
-                                               predefs + after, true, true);
-}
-
-TEST_F(CommonUniformElimTest, Volatile2) {
-  // Note: This test exemplifies the following:
-  // - Same test as Basic1 with the exception that
-  //   U_t is Volatile.
-  // - No optimizations are applied
-  //
-  // #version 430
-  // in vec4 BaseColor;
-  // in float fi;
-  //
-  // layout(std430) volatile buffer U_t
-  // {
-  //   float g_F;
-  //   float g_F2;
-  // };
-  //
-  //
-  // void main(void)
-  // {
-  //   vec4 v = BaseColor;
-  //   if (fi > 0) {
-  //     v = v * g_F;
-  //   } else {
-  //     float f2 = g_F2 - g_F;
-  //     v = v * f2;
-  //   }
-  // }
-
-  const std::string text =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %fi
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 430
-OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %fi "fi"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_F"
-OpMemberName %U_t 1 "g_F2"
-OpName %_ ""
-OpName %f2 "f2"
-OpDecorate %BaseColor Location 0
-OpDecorate %fi Location 0
-OpMemberDecorate %U_t 0 Volatile
-OpMemberDecorate %U_t 0 Offset 0
-OpMemberDecorate %U_t 1 Volatile
-OpMemberDecorate %U_t 1 Offset 4
-OpDecorate %U_t BufferBlock
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%fi = OpVariable %_ptr_Input_float Input
-%float_0 = OpConstant %float 0
-%bool = OpTypeBool
-%U_t = OpTypeStruct %float %float
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%_ptr_Function_float = OpTypePointer Function %float
-%int_1 = OpConstant %int 1
-%main = OpFunction %void None %3
-%5 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%f2 = OpVariable %_ptr_Function_float Function
-%12 = OpLoad %v4float %BaseColor
-OpStore %v %12
-%15 = OpLoad %float %fi
-%18 = OpFOrdGreaterThan %bool %15 %float_0
-OpSelectionMerge %20 None
-OpBranchConditional %18 %19 %31
-%19 = OpLabel
-%21 = OpLoad %v4float %v
-%28 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%29 = OpLoad %float %28
-%30 = OpVectorTimesScalar %v4float %21 %29
-OpStore %v %30
-OpBranch %20
-%31 = OpLabel
-%35 = OpAccessChain %_ptr_Uniform_float %_ %int_1
-%36 = OpLoad %float %35
-%37 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%38 = OpLoad %float %37
-%39 = OpFSub %float %36 %38
-OpStore %f2 %39
-%40 = OpLoad %v4float %v
-%41 = OpLoad %float %f2
-%42 = OpVectorTimesScalar %v4float %40 %41
-OpStore %v %42
-OpBranch %20
-%20 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  Pass::Status res = std::get<1>(
-      SinglePassRunAndDisassemble<CommonUniformElimPass>(text, true, false));
-  EXPECT_EQ(res, Pass::Status::SuccessWithoutChange);
-}
-
-TEST_F(CommonUniformElimTest, Volatile3) {
-  // Note: This test exemplifies the following:
-  // - Same test as Volatile2 with the exception that
-  //   the nested struct S is volatile
-  // - No optimizations are applied
-  //
-  // #version 430
-  // in vec4 BaseColor;
-  // in float fi;
-  //
-  // struct S {
-  //   volatile float a;
-  // };
-  //
-  // layout(std430) buffer U_t
-  // {
-  //   S g_F;
-  //   S g_F2;
-  // };
-  //
-  //
-  // void main(void)
-  // {
-  //   vec4 v = BaseColor;
-  //   if (fi > 0) {
-  //     v = v * g_F.a;
-  //   } else {
-  //     float f2 = g_F2.a - g_F.a;
-  //     v = v * f2;
-  //   }
-  // }
-
-  const std::string text =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %fi
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 430
-OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %fi "fi"
-OpName %S "S"
-OpMemberName %S 0 "a"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_F"
-OpMemberName %U_t 1 "g_F2"
-OpName %_ ""
-OpName %f2 "f2"
-OpDecorate %BaseColor Location 0
-OpDecorate %fi Location 0
-OpMemberDecorate %S 0 Offset 0
-OpMemberDecorate %S 0 Volatile
-OpMemberDecorate %U_t 0 Offset 0
-OpMemberDecorate %U_t 1 Offset 4
-OpDecorate %U_t BufferBlock
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%fi = OpVariable %_ptr_Input_float Input
-%float_0 = OpConstant %float 0
-%bool = OpTypeBool
-%S = OpTypeStruct %float
-%U_t = OpTypeStruct %S %S
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%_ptr_Function_float = OpTypePointer Function %float
-%int_1 = OpConstant %int 1
-%main = OpFunction %void None %3
-%5 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%f2 = OpVariable %_ptr_Function_float Function
-%12 = OpLoad %v4float %BaseColor
-OpStore %v %12
-%15 = OpLoad %float %fi
-%18 = OpFOrdGreaterThan %bool %15 %float_0
-OpSelectionMerge %20 None
-OpBranchConditional %18 %19 %32
-%19 = OpLabel
-%21 = OpLoad %v4float %v
-%29 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_0
-%30 = OpLoad %float %29
-%31 = OpVectorTimesScalar %v4float %21 %30
-OpStore %v %31
-OpBranch %20
-%32 = OpLabel
-%36 = OpAccessChain %_ptr_Uniform_float %_ %int_1 %int_0
-%37 = OpLoad %float %36
-%38 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_0
-%39 = OpLoad %float %38
-%40 = OpFSub %float %37 %39
-OpStore %f2 %40
-%41 = OpLoad %v4float %v
-%42 = OpLoad %float %f2
-%43 = OpVectorTimesScalar %v4float %41 %42
-OpStore %v %43
-OpBranch %20
-%20 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  Pass::Status res = std::get<1>(
-      SinglePassRunAndDisassemble<CommonUniformElimPass>(text, true, false));
-  EXPECT_EQ(res, Pass::Status::SuccessWithoutChange);
-}
-
-TEST_F(CommonUniformElimTest, IteratorDanglingPointer) {
-  // Note: This test exemplifies the following:
-  // - Existing common uniform (%_) load kept in place and shared
-  //
-  // #version 140
-  // in vec4 BaseColor;
-  // in float fi;
-  //
-  // layout(std140) uniform U_t
-  // {
-  //     bool g_B;
-  //     float g_F;
-  // } ;
-  //
-  // uniform float alpha;
-  // uniform bool alpha_B;
-  //
-  // void main()
-  // {
-  //     vec4 v = BaseColor;
-  //     if (g_B) {
-  //       v = v * g_F;
-  //       if (alpha_B)
-  //         v = v * alpha;
-  //       else
-  //         v = v * fi;
-  //     }
-  //     gl_FragColor = v;
-  // }
-
-  const std::string predefs =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor %fi
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_B"
-OpMemberName %U_t 1 "g_F"
-OpName %alpha "alpha"
-OpName %alpha_B "alpha_B"
-OpName %_ ""
-OpName %gl_FragColor "gl_FragColor"
-OpName %fi "fi"
-OpMemberDecorate %U_t 0 Offset 0
-OpMemberDecorate %U_t 1 Offset 4
-OpDecorate %U_t Block
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%12 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%uint = OpTypeInt 32 0
-%U_t = OpTypeStruct %uint %float
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%bool = OpTypeBool
-%uint_0 = OpConstant %uint 0
-%int_1 = OpConstant %int 1
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-%_ptr_Input_float = OpTypePointer Input %float
-%fi = OpVariable %_ptr_Input_float Input
-%alpha = OpVariable %_ptr_Uniform_float Uniform
-%alpha_B = OpVariable %_ptr_Uniform_uint Uniform
-)";
-
-  const std::string before =
-      R"(%main = OpFunction %void None %12
-%26 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%27 = OpLoad %v4float %BaseColor
-OpStore %v %27
-%28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
-%29 = OpLoad %uint %28
-%30 = OpINotEqual %bool %29 %uint_0
-OpSelectionMerge %31 None
-OpBranchConditional %30 %31 %32
-%32 = OpLabel
-%47 = OpLoad %v4float %v
-OpStore %gl_FragColor %47
-OpReturn
-%31 = OpLabel
-%33 = OpAccessChain %_ptr_Uniform_float %_ %int_1
-%34 = OpLoad %float %33
-%35 = OpLoad %v4float %v
-%36 = OpVectorTimesScalar %v4float %35 %34
-OpStore %v %36
-%37 = OpLoad %uint %alpha_B
-%38 = OpIEqual %bool %37 %uint_0
-OpSelectionMerge %43 None
-OpBranchConditional %38 %43 %39
-%39 = OpLabel
-%40 = OpLoad %float %alpha
-%41 = OpLoad %v4float %v
-%42 = OpVectorTimesScalar %v4float %41 %40
-OpStore %v %42
-OpBranch %50
-%50 = OpLabel
-%51 = OpLoad %v4float %v
-OpStore %gl_FragColor %51
-OpReturn
-%43 = OpLabel
-%44 = OpLoad %float %fi
-%45 = OpLoad %v4float %v
-%46 = OpVectorTimesScalar %v4float %45 %44
-OpStore %v %46
-OpBranch %60
-%60 = OpLabel
-%61 = OpLoad %v4float %v
-OpStore %gl_FragColor %61
-OpReturn
-OpFunctionEnd
-)";
-
-  const std::string after =
-      R"(%main = OpFunction %void None %12
-%28 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%29 = OpLoad %v4float %BaseColor
-OpStore %v %29
-%54 = OpLoad %U_t %_
-%55 = OpCompositeExtract %uint %54 0
-%32 = OpINotEqual %bool %55 %uint_0
-OpSelectionMerge %33 None
-OpBranchConditional %32 %33 %34
-%34 = OpLabel
-%35 = OpLoad %v4float %v
-OpStore %gl_FragColor %35
-OpReturn
-%33 = OpLabel
-%58 = OpLoad %float %alpha
-%57 = OpCompositeExtract %float %54 1
-%38 = OpLoad %v4float %v
-%39 = OpVectorTimesScalar %v4float %38 %57
-OpStore %v %39
-%40 = OpLoad %uint %alpha_B
-%41 = OpIEqual %bool %40 %uint_0
-OpSelectionMerge %42 None
-OpBranchConditional %41 %42 %43
-%43 = OpLabel
-%45 = OpLoad %v4float %v
-%46 = OpVectorTimesScalar %v4float %45 %58
-OpStore %v %46
-OpBranch %47
-%47 = OpLabel
-%48 = OpLoad %v4float %v
-OpStore %gl_FragColor %48
-OpReturn
-%42 = OpLabel
-%49 = OpLoad %float %fi
-%50 = OpLoad %v4float %v
-%51 = OpVectorTimesScalar %v4float %50 %49
-OpStore %v %51
-OpBranch %52
-%52 = OpLabel
-%53 = OpLoad %v4float %v
-OpStore %gl_FragColor %53
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<CommonUniformElimPass>(predefs + before,
-                                               predefs + after, true, true);
-}
-
-TEST_F(CommonUniformElimTest, MixedConstantAndNonConstantIndexes) {
-  const std::string text = R"(
-; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Uniform
-; CHECK: %501 = OpLabel
-; CHECK: [[ld:%\w+]] = OpLoad
-; CHECK-NOT: OpCompositeExtract {{%\w+}} {{%\w+}} 0 2 484
-; CHECK: OpAccessChain {{%\w+}} [[var]] %int_0 %int_2 [[ld]]
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "ringeffectLayer_px" %gl_FragCoord %178 %182
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource HLSL 500
-               OpDecorate %_arr_v4float_uint_10 ArrayStride 16
-               OpMemberDecorate %_struct_20 0 Offset 0
-               OpMemberDecorate %_struct_20 1 Offset 16
-               OpMemberDecorate %_struct_20 2 Offset 32
-               OpMemberDecorate %_struct_21 0 Offset 0
-               OpDecorate %_struct_21 Block
-               OpDecorate %23 DescriptorSet 0
-               OpDecorate %gl_FragCoord BuiltIn FragCoord
-               OpDecorate %178 Location 0
-               OpDecorate %182 Location 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-    %v2float = OpTypeVector %float 2
-%_ptr_Function_v2float = OpTypePointer Function %v2float
-       %uint = OpTypeInt 32 0
-    %uint_10 = OpConstant %uint 10
-%_arr_v4float_uint_10 = OpTypeArray %v4float %uint_10
- %_struct_20 = OpTypeStruct %v4float %v4float %_arr_v4float_uint_10
- %_struct_21 = OpTypeStruct %_struct_20
-%_ptr_Uniform__struct_21 = OpTypePointer Uniform %_struct_21
-         %23 = OpVariable %_ptr_Uniform__struct_21 Uniform
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-     %uint_3 = OpConstant %uint 3
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-    %float_0 = OpConstant %float 0
-         %43 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
-%_ptr_Function_int = OpTypePointer Function %int
-      %int_5 = OpConstant %int 5
-       %bool = OpTypeBool
-      %int_1 = OpConstant %int 1
-      %int_2 = OpConstant %int 2
-     %uint_5 = OpConstant %uint 5
-%_arr_v2float_uint_5 = OpTypeArray %v2float %uint_5
-%_ptr_Function__arr_v2float_uint_5 = OpTypePointer Function %_arr_v2float_uint_5
-         %82 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%_ptr_UniformConstant_82 = OpTypePointer UniformConstant %82
-         %86 = OpTypeSampler
-%_ptr_UniformConstant_86 = OpTypePointer UniformConstant %86
-         %90 = OpTypeSampledImage %82
-    %v3float = OpTypeVector %float 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-        %178 = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-        %182 = OpVariable %_ptr_Output_v4float Output
-          %4 = OpFunction %void None %3
-          %5 = OpLabel
-        %483 = OpVariable %_ptr_Function_v4float Function
-        %484 = OpVariable %_ptr_Function_int Function
-        %486 = OpVariable %_ptr_Function__arr_v2float_uint_5 Function
-        %179 = OpLoad %v4float %178
-        %493 = OpAccessChain %_ptr_Uniform_float %23 %int_0 %int_0 %uint_3
-        %494 = OpLoad %float %493
-               OpStore %483 %43
-               OpStore %484 %int_0
-               OpBranch %495
-        %495 = OpLabel
-               OpLoopMerge %496 %497 None
-               OpBranch %498
-        %498 = OpLabel
-        %499 = OpLoad %int %484
-        %500 = OpSLessThan %bool %499 %int_5
-               OpBranchConditional %500 %501 %496
-        %501 = OpLabel
-        %504 = OpVectorShuffle %v2float %179 %179 0 1
-        %505 = OpLoad %int %484
-        %506 = OpAccessChain %_ptr_Uniform_v4float %23 %int_0 %int_2 %505
-        %507 = OpLoad %v4float %506
-        %508 = OpVectorShuffle %v2float %507 %507 0 1
-        %509 = OpFAdd %v2float %504 %508
-        %512 = OpAccessChain %_ptr_Uniform_v4float %23 %int_0 %int_1
-        %513 = OpLoad %v4float %512
-        %514 = OpVectorShuffle %v2float %513 %513 0 1
-        %517 = OpVectorShuffle %v2float %513 %513 2 3
-        %518 = OpExtInst %v2float %1 FClamp %509 %514 %517
-        %519 = OpAccessChain %_ptr_Function_v2float %486 %505
-               OpStore %519 %518
-               OpBranch %497
-        %497 = OpLabel
-        %520 = OpLoad %int %484
-        %521 = OpIAdd %int %520 %int_1
-               OpStore %484 %521
-               OpBranch %495
-        %496 = OpLabel
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<CommonUniformElimPass>(text, true);
-}
-
-TEST_F(CommonUniformElimTest, LoadPlacedAfterPhi) {
-  const std::string text = R"(
-; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Uniform
-; CHECK: OpSelectionMerge [[merge:%\w+]]
-; CHECK: [[merge]] = OpLabel
-; CHECK-NEXT: OpPhi
-; CHECK-NEXT: OpLoad {{%\w+}} [[var]]
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %2 "main"
-               OpExecutionMode %2 OriginUpperLeft
-               OpSource ESSL 310
-               OpMemberDecorate %_struct_3 0 Offset 0
-               OpDecorate %_struct_3 Block
-               OpDecorate %4 DescriptorSet 0
-               OpDecorate %4 Binding 0
-       %void = OpTypeVoid
-          %6 = OpTypeFunction %void
-       %bool = OpTypeBool
-      %false = OpConstantFalse %bool
-       %uint = OpTypeInt 32 0
-     %v2uint = OpTypeVector %uint 2
-  %_struct_3 = OpTypeStruct %v2uint
-%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
-          %4 = OpVariable %_ptr_Uniform__struct_3 Uniform
-     %uint_0 = OpConstant %uint 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-     %uint_2 = OpConstant %uint 2
-          %2 = OpFunction %void None %6
-         %15 = OpLabel
-               OpSelectionMerge %16 None
-               OpBranchConditional %false %17 %16
-         %17 = OpLabel
-               OpBranch %16
-         %16 = OpLabel
-         %18 = OpPhi %bool %false %15 %false %17
-               OpSelectionMerge %19 None
-               OpBranchConditional %false %20 %21
-         %20 = OpLabel
-         %22 = OpAccessChain %_ptr_Uniform_uint %4 %uint_0 %uint_0
-         %23 = OpLoad %uint %22
-               OpBranch %19
-         %21 = OpLabel
-               OpBranch %19
-         %19 = OpLabel
-               OpReturn
-               OpFunctionEnd
-)";
-
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndMatch<CommonUniformElimPass>(text, true);
-}
-
-TEST_F(CommonUniformElimTest, TestVariablePointer) {
-  // Same test a basic1 except the variable pointers capability has been added.
-  // This should stop the transformation from running.
-  const std::string test =
-      R"(OpCapability Shader
-OpCapability VariablePointers
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %fi "fi"
-OpName %U_t "U_t"
-OpMemberName %U_t 0 "g_F"
-OpMemberName %U_t 1 "g_F2"
-OpName %_ ""
-OpName %f2 "f2"
-OpName %gl_FragColor "gl_FragColor"
-OpMemberDecorate %U_t 0 Offset 0
-OpMemberDecorate %U_t 1 Offset 4
-OpDecorate %U_t Block
-OpDecorate %_ DescriptorSet 0
-%void = OpTypeVoid
-%11 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Input_float = OpTypePointer Input %float
-%fi = OpVariable %_ptr_Input_float Input
-%float_0 = OpConstant %float 0
-%bool = OpTypeBool
-%U_t = OpTypeStruct %float %float
-%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
-%_ = OpVariable %_ptr_Uniform_U_t Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%_ptr_Function_float = OpTypePointer Function %float
-%int_1 = OpConstant %int 1
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-%main = OpFunction %void None %11
-%26 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%f2 = OpVariable %_ptr_Function_float Function
-%27 = OpLoad %v4float %BaseColor
-OpStore %v %27
-%28 = OpLoad %float %fi
-%29 = OpFOrdGreaterThan %bool %28 %float_0
-OpSelectionMerge %30 None
-OpBranchConditional %29 %31 %32
-%31 = OpLabel
-%33 = OpLoad %v4float %v
-%34 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%35 = OpLoad %float %34
-%36 = OpVectorTimesScalar %v4float %33 %35
-OpStore %v %36
-OpBranch %30
-%32 = OpLabel
-%37 = OpAccessChain %_ptr_Uniform_float %_ %int_1
-%38 = OpLoad %float %37
-%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
-%40 = OpLoad %float %39
-%41 = OpFSub %float %38 %40
-OpStore %f2 %41
-%42 = OpLoad %v4float %v
-%43 = OpLoad %float %f2
-%44 = OpVectorTimesScalar %v4float %42 %43
-OpStore %v %44
-OpBranch %30
-%30 = OpLabel
-%45 = OpLoad %v4float %v
-OpStore %gl_FragColor %45
-OpReturn
-OpFunctionEnd
-)";
-
-  SinglePassRunAndCheck<CommonUniformElimPass>(test, test, true, true);
-}
-
-// TODO(greg-lunarg): Add tests to verify handling of these cases:
-//
-//    Disqualifying cases: extensions, decorations, non-logical addressing,
-//      non-structured control flow
-//    Others?
-
-}  // namespace
-}  // namespace opt
-}  // namespace spvtools

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

@@ -3112,121 +3112,6 @@ OpFunctionEnd
   SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
   SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
 }
 }
 
 
-TEST_F(InlineTest, DontInlineFuncWithOpKill) {
-  const std::string test =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 330
-OpName %main "main"
-OpName %kill_ "kill("
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%main = OpFunction %void None %3
-%5 = OpLabel
-OpBranch %9
-%9 = OpLabel
-OpLoopMerge %11 %12 None
-OpBranch %13
-%13 = OpLabel
-OpBranchConditional %true %10 %11
-%10 = OpLabel
-OpBranch %12
-%12 = OpLabel
-%16 = OpFunctionCall %void %kill_
-OpBranch %9
-%11 = OpLabel
-OpReturn
-OpFunctionEnd
-%kill_ = OpFunction %void None %3
-%7 = OpLabel
-OpKill
-OpFunctionEnd
-)";
-
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
-}
-
-TEST_F(InlineTest, InlineFuncWithOpKill) {
-  const std::string before =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 330
-OpName %main "main"
-OpName %kill_ "kill("
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%main = OpFunction %void None %3
-%5 = OpLabel
-OpBranch %9
-%9 = OpLabel
-OpLoopMerge %11 %12 None
-OpBranch %13
-%13 = OpLabel
-OpBranchConditional %true %10 %11
-%10 = OpLabel
-%16 = OpFunctionCall %void %kill_
-OpBranch %12
-%12 = OpLabel
-OpBranch %9
-%11 = OpLabel
-OpReturn
-OpFunctionEnd
-%kill_ = OpFunction %void None %3
-%7 = OpLabel
-OpKill
-OpFunctionEnd
-)";
-  const std::string after =
-      R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 330
-OpName %main "main"
-OpName %kill_ "kill("
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%main = OpFunction %void None %3
-%5 = OpLabel
-OpBranch %9
-%9 = OpLabel
-OpLoopMerge %11 %12 None
-OpBranch %13
-%13 = OpLabel
-OpBranchConditional %true %10 %11
-%10 = OpLabel
-OpKill
-%17 = OpLabel
-OpBranch %12
-%12 = OpLabel
-OpBranch %9
-%11 = OpLabel
-OpReturn
-OpFunctionEnd
-%kill_ = OpFunction %void None %3
-%7 = OpLabel
-OpKill
-OpFunctionEnd
-)";
-
-  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
-  SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
-}
-
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //
 //    Empty modules
 //    Empty modules

+ 1944 - 0
3rdparty/spirv-tools/test/opt/inst_bindless_check_test.cpp

@@ -8598,6 +8598,1950 @@ OpFunctionEnd
       true, 7u, 23u, true, true, 2u);
       true, 7u, 23u, true, true, 2u);
 }
 }
 
 
+TEST_F(InstBindlessTest,
+       InstBoundsRayGenerationInitLoadVariableSizedSampledImagesArray) {
+  // #version 460
+  // #extension GL_EXT_nonuniform_qualifier : require
+  // #extension GL_NV_ray_tracing : require
+  //
+  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  //   uint index;
+  //   float red;
+  // } sbo;
+  //
+  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  //
+  // void main()
+  // {
+  //    sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationNV %main "main"
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+%void = OpTypeVoid
+)";
+
+  const std::string defs_after =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationNV %main "main" %89
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_39 Block
+OpMemberDecorate %_struct_39 0 Offset 0
+OpDecorate %41 DescriptorSet 7
+OpDecorate %41 Binding 1
+OpDecorate %_struct_63 Block
+OpMemberDecorate %_struct_63 0 Offset 0
+OpMemberDecorate %_struct_63 1 Offset 4
+OpDecorate %65 DescriptorSet 7
+OpDecorate %65 Binding 0
+OpDecorate %89 BuiltIn LaunchIdNV
+%void = OpTypeVoid
+)";
+
+  const std::string func_before =
+      R"(%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%20 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%uint_1 = OpConstant %uint 1
+%34 = OpTypeFunction %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_39 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
+%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%bool = OpTypeBool
+%57 = OpTypeFunction %void %uint %uint %uint %uint
+%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
+%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
+%uint_10 = OpConstant %uint 10
+%uint_4 = OpConstant %uint 4
+%uint_23 = OpConstant %uint 23
+%uint_2 = OpConstant %uint 2
+%uint_5313 = OpConstant %uint 5313
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%89 = OpVariable %_ptr_Input_v3uint Input
+%uint_5 = OpConstant %uint 5
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_8 = OpConstant %uint 8
+%uint_9 = OpConstant %uint 9
+%uint_51 = OpConstant %uint 51
+%113 = OpConstantNull %v4float
+%116 = OpTypeFunction %uint %uint %uint %uint %uint
+%uint_48 = OpConstant %uint 48
+%141 = OpConstantNull %uint
+%uint_54 = OpConstant %uint 54
+%main = OpFunction %void None %7
+%24 = OpLabel
+%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%134 = OpINotEqual %bool %133 %uint_0
+OpSelectionMerge %135 None
+OpBranchConditional %134 %136 %137
+%136 = OpLabel
+%138 = OpLoad %uint %25
+OpBranch %135
+%137 = OpLabel
+%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
+OpBranch %135
+%135 = OpLabel
+%142 = OpPhi %uint %138 %136 %141 %137
+%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+%28 = OpLoad %13 %27
+%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
+%50 = OpULessThan %bool %142 %48
+OpSelectionMerge %51 None
+OpBranchConditional %50 %52 %53
+%52 = OpLabel
+%54 = OpLoad %13 %27
+%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
+%144 = OpINotEqual %bool %143 %uint_0
+OpSelectionMerge %145 None
+OpBranchConditional %144 %146 %147
+%146 = OpLabel
+%148 = OpLoad %13 %27
+%149 = OpImageRead %v4float %148 %20
+OpBranch %145
+%147 = OpLabel
+%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
+OpBranch %145
+%145 = OpLabel
+%151 = OpPhi %v4float %149 %146 %113 %147
+OpBranch %51
+%53 = OpLabel
+%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
+OpBranch %51
+%51 = OpLabel
+%114 = OpPhi %v4float %151 %145 %113 %53
+%30 = OpCompositeExtract %float %114 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%153 = OpINotEqual %bool %152 %uint_0
+OpSelectionMerge %154 None
+OpBranchConditional %153 %155 %156
+%155 = OpLabel
+OpStore %31 %30
+OpBranch %154
+%156 = OpLabel
+%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
+OpBranch %154
+%154 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%33 = OpFunction %uint None %34
+%35 = OpFunctionParameter %uint
+%36 = OpFunctionParameter %uint
+%37 = OpLabel
+%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
+%44 = OpLoad %uint %43
+%45 = OpIAdd %uint %44 %36
+%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
+%47 = OpLoad %uint %46
+OpReturnValue %47
+OpFunctionEnd
+%56 = OpFunction %void None %57
+%58 = OpFunctionParameter %uint
+%59 = OpFunctionParameter %uint
+%60 = OpFunctionParameter %uint
+%61 = OpFunctionParameter %uint
+%62 = OpLabel
+%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
+%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
+%70 = OpIAdd %uint %69 %uint_10
+%71 = OpArrayLength %uint %65 1
+%72 = OpULessThanEqual %bool %70 %71
+OpSelectionMerge %73 None
+OpBranchConditional %72 %74 %73
+%74 = OpLabel
+%75 = OpIAdd %uint %69 %uint_0
+%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
+OpStore %76 %uint_10
+%78 = OpIAdd %uint %69 %uint_1
+%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
+OpStore %79 %uint_23
+%81 = OpIAdd %uint %69 %uint_2
+%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
+OpStore %82 %58
+%85 = OpIAdd %uint %69 %uint_3
+%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
+OpStore %86 %uint_5313
+%90 = OpLoad %v3uint %89
+%91 = OpCompositeExtract %uint %90 0
+%92 = OpCompositeExtract %uint %90 1
+%93 = OpCompositeExtract %uint %90 2
+%94 = OpIAdd %uint %69 %uint_4
+%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
+OpStore %95 %91
+%97 = OpIAdd %uint %69 %uint_5
+%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
+OpStore %98 %92
+%100 = OpIAdd %uint %69 %uint_6
+%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
+OpStore %101 %93
+%103 = OpIAdd %uint %69 %uint_7
+%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
+OpStore %104 %59
+%106 = OpIAdd %uint %69 %uint_8
+%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
+OpStore %107 %60
+%109 = OpIAdd %uint %69 %uint_9
+%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
+OpStore %110 %61
+OpBranch %73
+%73 = OpLabel
+OpReturn
+OpFunctionEnd
+%115 = OpFunction %uint None %116
+%117 = OpFunctionParameter %uint
+%118 = OpFunctionParameter %uint
+%119 = OpFunctionParameter %uint
+%120 = OpFunctionParameter %uint
+%121 = OpLabel
+%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
+%123 = OpLoad %uint %122
+%124 = OpIAdd %uint %123 %118
+%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
+%126 = OpLoad %uint %125
+%127 = OpIAdd %uint %126 %119
+%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
+%129 = OpLoad %uint %128
+%130 = OpIAdd %uint %129 %120
+%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
+%132 = OpLoad %uint %131
+OpReturnValue %132
+OpFunctionEnd
+)";
+
+  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBindlessCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, true, true, 2u);
+}
+
+TEST_F(InstBindlessTest,
+       InstBoundsIntersectionInitLoadVariableSizedSampledImagesArray) {
+  // #version 460
+  // #extension GL_EXT_nonuniform_qualifier : require
+  // #extension GL_NV_ray_tracing : require
+  //
+  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  //   uint index;
+  //   float red;
+  // } sbo;
+  //
+  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  //
+  // void main()
+  // {
+  //    sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint IntersectionNV %main "main"
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+%void = OpTypeVoid
+)";
+
+  const std::string defs_after =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint IntersectionNV %main "main" %89
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_39 Block
+OpMemberDecorate %_struct_39 0 Offset 0
+OpDecorate %41 DescriptorSet 7
+OpDecorate %41 Binding 1
+OpDecorate %_struct_63 Block
+OpMemberDecorate %_struct_63 0 Offset 0
+OpMemberDecorate %_struct_63 1 Offset 4
+OpDecorate %65 DescriptorSet 7
+OpDecorate %65 Binding 0
+OpDecorate %89 BuiltIn LaunchIdNV
+%void = OpTypeVoid
+)";
+
+  const std::string func_before =
+      R"(%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%20 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%uint_1 = OpConstant %uint 1
+%34 = OpTypeFunction %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_39 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
+%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%bool = OpTypeBool
+%57 = OpTypeFunction %void %uint %uint %uint %uint
+%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
+%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
+%uint_10 = OpConstant %uint 10
+%uint_4 = OpConstant %uint 4
+%uint_23 = OpConstant %uint 23
+%uint_2 = OpConstant %uint 2
+%uint_5314 = OpConstant %uint 5314
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%89 = OpVariable %_ptr_Input_v3uint Input
+%uint_5 = OpConstant %uint 5
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_8 = OpConstant %uint 8
+%uint_9 = OpConstant %uint 9
+%uint_51 = OpConstant %uint 51
+%113 = OpConstantNull %v4float
+%116 = OpTypeFunction %uint %uint %uint %uint %uint
+%uint_48 = OpConstant %uint 48
+%141 = OpConstantNull %uint
+%uint_54 = OpConstant %uint 54
+%main = OpFunction %void None %7
+%24 = OpLabel
+%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%134 = OpINotEqual %bool %133 %uint_0
+OpSelectionMerge %135 None
+OpBranchConditional %134 %136 %137
+%136 = OpLabel
+%138 = OpLoad %uint %25
+OpBranch %135
+%137 = OpLabel
+%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
+OpBranch %135
+%135 = OpLabel
+%142 = OpPhi %uint %138 %136 %141 %137
+%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+%28 = OpLoad %13 %27
+%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
+%50 = OpULessThan %bool %142 %48
+OpSelectionMerge %51 None
+OpBranchConditional %50 %52 %53
+%52 = OpLabel
+%54 = OpLoad %13 %27
+%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
+%144 = OpINotEqual %bool %143 %uint_0
+OpSelectionMerge %145 None
+OpBranchConditional %144 %146 %147
+%146 = OpLabel
+%148 = OpLoad %13 %27
+%149 = OpImageRead %v4float %148 %20
+OpBranch %145
+%147 = OpLabel
+%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
+OpBranch %145
+%145 = OpLabel
+%151 = OpPhi %v4float %149 %146 %113 %147
+OpBranch %51
+%53 = OpLabel
+%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
+OpBranch %51
+%51 = OpLabel
+%114 = OpPhi %v4float %151 %145 %113 %53
+%30 = OpCompositeExtract %float %114 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%153 = OpINotEqual %bool %152 %uint_0
+OpSelectionMerge %154 None
+OpBranchConditional %153 %155 %156
+%155 = OpLabel
+OpStore %31 %30
+OpBranch %154
+%156 = OpLabel
+%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
+OpBranch %154
+%154 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%33 = OpFunction %uint None %34
+%35 = OpFunctionParameter %uint
+%36 = OpFunctionParameter %uint
+%37 = OpLabel
+%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
+%44 = OpLoad %uint %43
+%45 = OpIAdd %uint %44 %36
+%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
+%47 = OpLoad %uint %46
+OpReturnValue %47
+OpFunctionEnd
+%56 = OpFunction %void None %57
+%58 = OpFunctionParameter %uint
+%59 = OpFunctionParameter %uint
+%60 = OpFunctionParameter %uint
+%61 = OpFunctionParameter %uint
+%62 = OpLabel
+%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
+%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
+%70 = OpIAdd %uint %69 %uint_10
+%71 = OpArrayLength %uint %65 1
+%72 = OpULessThanEqual %bool %70 %71
+OpSelectionMerge %73 None
+OpBranchConditional %72 %74 %73
+%74 = OpLabel
+%75 = OpIAdd %uint %69 %uint_0
+%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
+OpStore %76 %uint_10
+%78 = OpIAdd %uint %69 %uint_1
+%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
+OpStore %79 %uint_23
+%81 = OpIAdd %uint %69 %uint_2
+%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
+OpStore %82 %58
+%85 = OpIAdd %uint %69 %uint_3
+%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
+OpStore %86 %uint_5314
+%90 = OpLoad %v3uint %89
+%91 = OpCompositeExtract %uint %90 0
+%92 = OpCompositeExtract %uint %90 1
+%93 = OpCompositeExtract %uint %90 2
+%94 = OpIAdd %uint %69 %uint_4
+%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
+OpStore %95 %91
+%97 = OpIAdd %uint %69 %uint_5
+%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
+OpStore %98 %92
+%100 = OpIAdd %uint %69 %uint_6
+%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
+OpStore %101 %93
+%103 = OpIAdd %uint %69 %uint_7
+%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
+OpStore %104 %59
+%106 = OpIAdd %uint %69 %uint_8
+%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
+OpStore %107 %60
+%109 = OpIAdd %uint %69 %uint_9
+%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
+OpStore %110 %61
+OpBranch %73
+%73 = OpLabel
+OpReturn
+OpFunctionEnd
+%115 = OpFunction %uint None %116
+%117 = OpFunctionParameter %uint
+%118 = OpFunctionParameter %uint
+%119 = OpFunctionParameter %uint
+%120 = OpFunctionParameter %uint
+%121 = OpLabel
+%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
+%123 = OpLoad %uint %122
+%124 = OpIAdd %uint %123 %118
+%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
+%126 = OpLoad %uint %125
+%127 = OpIAdd %uint %126 %119
+%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
+%129 = OpLoad %uint %128
+%130 = OpIAdd %uint %129 %120
+%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
+%132 = OpLoad %uint %131
+OpReturnValue %132
+OpFunctionEnd
+)";
+
+  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBindlessCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, true, true, 2u);
+}
+
+TEST_F(InstBindlessTest,
+       InstBoundsAnyHitInitLoadVariableSizedSampledImagesArray) {
+  // #version 460
+  // #extension GL_EXT_nonuniform_qualifier : require
+  // #extension GL_NV_ray_tracing : require
+  //
+  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  //   uint index;
+  //   float red;
+  // } sbo;
+  //
+  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  //
+  // void main()
+  // {
+  //    sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint AnyHitNV %main "main"
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+%void = OpTypeVoid
+)";
+
+  const std::string defs_after =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint AnyHitNV %main "main" %89
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_39 Block
+OpMemberDecorate %_struct_39 0 Offset 0
+OpDecorate %41 DescriptorSet 7
+OpDecorate %41 Binding 1
+OpDecorate %_struct_63 Block
+OpMemberDecorate %_struct_63 0 Offset 0
+OpMemberDecorate %_struct_63 1 Offset 4
+OpDecorate %65 DescriptorSet 7
+OpDecorate %65 Binding 0
+OpDecorate %89 BuiltIn LaunchIdNV
+%void = OpTypeVoid
+)";
+
+  const std::string func_before =
+      R"(%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%20 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%uint_1 = OpConstant %uint 1
+%34 = OpTypeFunction %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_39 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
+%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%bool = OpTypeBool
+%57 = OpTypeFunction %void %uint %uint %uint %uint
+%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
+%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
+%uint_10 = OpConstant %uint 10
+%uint_4 = OpConstant %uint 4
+%uint_23 = OpConstant %uint 23
+%uint_2 = OpConstant %uint 2
+%uint_5315 = OpConstant %uint 5315
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%89 = OpVariable %_ptr_Input_v3uint Input
+%uint_5 = OpConstant %uint 5
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_8 = OpConstant %uint 8
+%uint_9 = OpConstant %uint 9
+%uint_51 = OpConstant %uint 51
+%113 = OpConstantNull %v4float
+%116 = OpTypeFunction %uint %uint %uint %uint %uint
+%uint_48 = OpConstant %uint 48
+%141 = OpConstantNull %uint
+%uint_54 = OpConstant %uint 54
+%main = OpFunction %void None %7
+%24 = OpLabel
+%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%134 = OpINotEqual %bool %133 %uint_0
+OpSelectionMerge %135 None
+OpBranchConditional %134 %136 %137
+%136 = OpLabel
+%138 = OpLoad %uint %25
+OpBranch %135
+%137 = OpLabel
+%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
+OpBranch %135
+%135 = OpLabel
+%142 = OpPhi %uint %138 %136 %141 %137
+%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+%28 = OpLoad %13 %27
+%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
+%50 = OpULessThan %bool %142 %48
+OpSelectionMerge %51 None
+OpBranchConditional %50 %52 %53
+%52 = OpLabel
+%54 = OpLoad %13 %27
+%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
+%144 = OpINotEqual %bool %143 %uint_0
+OpSelectionMerge %145 None
+OpBranchConditional %144 %146 %147
+%146 = OpLabel
+%148 = OpLoad %13 %27
+%149 = OpImageRead %v4float %148 %20
+OpBranch %145
+%147 = OpLabel
+%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
+OpBranch %145
+%145 = OpLabel
+%151 = OpPhi %v4float %149 %146 %113 %147
+OpBranch %51
+%53 = OpLabel
+%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
+OpBranch %51
+%51 = OpLabel
+%114 = OpPhi %v4float %151 %145 %113 %53
+%30 = OpCompositeExtract %float %114 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%153 = OpINotEqual %bool %152 %uint_0
+OpSelectionMerge %154 None
+OpBranchConditional %153 %155 %156
+%155 = OpLabel
+OpStore %31 %30
+OpBranch %154
+%156 = OpLabel
+%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
+OpBranch %154
+%154 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%33 = OpFunction %uint None %34
+%35 = OpFunctionParameter %uint
+%36 = OpFunctionParameter %uint
+%37 = OpLabel
+%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
+%44 = OpLoad %uint %43
+%45 = OpIAdd %uint %44 %36
+%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
+%47 = OpLoad %uint %46
+OpReturnValue %47
+OpFunctionEnd
+%56 = OpFunction %void None %57
+%58 = OpFunctionParameter %uint
+%59 = OpFunctionParameter %uint
+%60 = OpFunctionParameter %uint
+%61 = OpFunctionParameter %uint
+%62 = OpLabel
+%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
+%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
+%70 = OpIAdd %uint %69 %uint_10
+%71 = OpArrayLength %uint %65 1
+%72 = OpULessThanEqual %bool %70 %71
+OpSelectionMerge %73 None
+OpBranchConditional %72 %74 %73
+%74 = OpLabel
+%75 = OpIAdd %uint %69 %uint_0
+%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
+OpStore %76 %uint_10
+%78 = OpIAdd %uint %69 %uint_1
+%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
+OpStore %79 %uint_23
+%81 = OpIAdd %uint %69 %uint_2
+%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
+OpStore %82 %58
+%85 = OpIAdd %uint %69 %uint_3
+%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
+OpStore %86 %uint_5315
+%90 = OpLoad %v3uint %89
+%91 = OpCompositeExtract %uint %90 0
+%92 = OpCompositeExtract %uint %90 1
+%93 = OpCompositeExtract %uint %90 2
+%94 = OpIAdd %uint %69 %uint_4
+%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
+OpStore %95 %91
+%97 = OpIAdd %uint %69 %uint_5
+%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
+OpStore %98 %92
+%100 = OpIAdd %uint %69 %uint_6
+%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
+OpStore %101 %93
+%103 = OpIAdd %uint %69 %uint_7
+%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
+OpStore %104 %59
+%106 = OpIAdd %uint %69 %uint_8
+%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
+OpStore %107 %60
+%109 = OpIAdd %uint %69 %uint_9
+%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
+OpStore %110 %61
+OpBranch %73
+%73 = OpLabel
+OpReturn
+OpFunctionEnd
+%115 = OpFunction %uint None %116
+%117 = OpFunctionParameter %uint
+%118 = OpFunctionParameter %uint
+%119 = OpFunctionParameter %uint
+%120 = OpFunctionParameter %uint
+%121 = OpLabel
+%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
+%123 = OpLoad %uint %122
+%124 = OpIAdd %uint %123 %118
+%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
+%126 = OpLoad %uint %125
+%127 = OpIAdd %uint %126 %119
+%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
+%129 = OpLoad %uint %128
+%130 = OpIAdd %uint %129 %120
+%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
+%132 = OpLoad %uint %131
+OpReturnValue %132
+OpFunctionEnd
+)";
+
+  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBindlessCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, true, true, 2u);
+}
+
+TEST_F(InstBindlessTest,
+       InstBoundsClosestHitInitLoadVariableSizedSampledImagesArray) {
+  // #version 460
+  // #extension GL_EXT_nonuniform_qualifier : require
+  // #extension GL_NV_ray_tracing : require
+  //
+  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  //   uint index;
+  //   float red;
+  // } sbo;
+  //
+  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  //
+  // void main()
+  // {
+  //    sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint ClosestHitNV %main "main"
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+%void = OpTypeVoid
+)";
+
+  const std::string defs_after =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint ClosestHitNV %main "main" %89
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_39 Block
+OpMemberDecorate %_struct_39 0 Offset 0
+OpDecorate %41 DescriptorSet 7
+OpDecorate %41 Binding 1
+OpDecorate %_struct_63 Block
+OpMemberDecorate %_struct_63 0 Offset 0
+OpMemberDecorate %_struct_63 1 Offset 4
+OpDecorate %65 DescriptorSet 7
+OpDecorate %65 Binding 0
+OpDecorate %89 BuiltIn LaunchIdNV
+%void = OpTypeVoid
+)";
+
+  const std::string func_before =
+      R"(%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%20 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%uint_1 = OpConstant %uint 1
+%34 = OpTypeFunction %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_39 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
+%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%bool = OpTypeBool
+%57 = OpTypeFunction %void %uint %uint %uint %uint
+%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
+%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
+%uint_10 = OpConstant %uint 10
+%uint_4 = OpConstant %uint 4
+%uint_23 = OpConstant %uint 23
+%uint_2 = OpConstant %uint 2
+%uint_5316 = OpConstant %uint 5316
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%89 = OpVariable %_ptr_Input_v3uint Input
+%uint_5 = OpConstant %uint 5
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_8 = OpConstant %uint 8
+%uint_9 = OpConstant %uint 9
+%uint_51 = OpConstant %uint 51
+%113 = OpConstantNull %v4float
+%116 = OpTypeFunction %uint %uint %uint %uint %uint
+%uint_48 = OpConstant %uint 48
+%141 = OpConstantNull %uint
+%uint_54 = OpConstant %uint 54
+%main = OpFunction %void None %7
+%24 = OpLabel
+%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%134 = OpINotEqual %bool %133 %uint_0
+OpSelectionMerge %135 None
+OpBranchConditional %134 %136 %137
+%136 = OpLabel
+%138 = OpLoad %uint %25
+OpBranch %135
+%137 = OpLabel
+%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
+OpBranch %135
+%135 = OpLabel
+%142 = OpPhi %uint %138 %136 %141 %137
+%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+%28 = OpLoad %13 %27
+%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
+%50 = OpULessThan %bool %142 %48
+OpSelectionMerge %51 None
+OpBranchConditional %50 %52 %53
+%52 = OpLabel
+%54 = OpLoad %13 %27
+%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
+%144 = OpINotEqual %bool %143 %uint_0
+OpSelectionMerge %145 None
+OpBranchConditional %144 %146 %147
+%146 = OpLabel
+%148 = OpLoad %13 %27
+%149 = OpImageRead %v4float %148 %20
+OpBranch %145
+%147 = OpLabel
+%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
+OpBranch %145
+%145 = OpLabel
+%151 = OpPhi %v4float %149 %146 %113 %147
+OpBranch %51
+%53 = OpLabel
+%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
+OpBranch %51
+%51 = OpLabel
+%114 = OpPhi %v4float %151 %145 %113 %53
+%30 = OpCompositeExtract %float %114 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%153 = OpINotEqual %bool %152 %uint_0
+OpSelectionMerge %154 None
+OpBranchConditional %153 %155 %156
+%155 = OpLabel
+OpStore %31 %30
+OpBranch %154
+%156 = OpLabel
+%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
+OpBranch %154
+%154 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%33 = OpFunction %uint None %34
+%35 = OpFunctionParameter %uint
+%36 = OpFunctionParameter %uint
+%37 = OpLabel
+%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
+%44 = OpLoad %uint %43
+%45 = OpIAdd %uint %44 %36
+%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
+%47 = OpLoad %uint %46
+OpReturnValue %47
+OpFunctionEnd
+%56 = OpFunction %void None %57
+%58 = OpFunctionParameter %uint
+%59 = OpFunctionParameter %uint
+%60 = OpFunctionParameter %uint
+%61 = OpFunctionParameter %uint
+%62 = OpLabel
+%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
+%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
+%70 = OpIAdd %uint %69 %uint_10
+%71 = OpArrayLength %uint %65 1
+%72 = OpULessThanEqual %bool %70 %71
+OpSelectionMerge %73 None
+OpBranchConditional %72 %74 %73
+%74 = OpLabel
+%75 = OpIAdd %uint %69 %uint_0
+%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
+OpStore %76 %uint_10
+%78 = OpIAdd %uint %69 %uint_1
+%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
+OpStore %79 %uint_23
+%81 = OpIAdd %uint %69 %uint_2
+%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
+OpStore %82 %58
+%85 = OpIAdd %uint %69 %uint_3
+%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
+OpStore %86 %uint_5316
+%90 = OpLoad %v3uint %89
+%91 = OpCompositeExtract %uint %90 0
+%92 = OpCompositeExtract %uint %90 1
+%93 = OpCompositeExtract %uint %90 2
+%94 = OpIAdd %uint %69 %uint_4
+%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
+OpStore %95 %91
+%97 = OpIAdd %uint %69 %uint_5
+%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
+OpStore %98 %92
+%100 = OpIAdd %uint %69 %uint_6
+%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
+OpStore %101 %93
+%103 = OpIAdd %uint %69 %uint_7
+%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
+OpStore %104 %59
+%106 = OpIAdd %uint %69 %uint_8
+%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
+OpStore %107 %60
+%109 = OpIAdd %uint %69 %uint_9
+%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
+OpStore %110 %61
+OpBranch %73
+%73 = OpLabel
+OpReturn
+OpFunctionEnd
+%115 = OpFunction %uint None %116
+%117 = OpFunctionParameter %uint
+%118 = OpFunctionParameter %uint
+%119 = OpFunctionParameter %uint
+%120 = OpFunctionParameter %uint
+%121 = OpLabel
+%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
+%123 = OpLoad %uint %122
+%124 = OpIAdd %uint %123 %118
+%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
+%126 = OpLoad %uint %125
+%127 = OpIAdd %uint %126 %119
+%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
+%129 = OpLoad %uint %128
+%130 = OpIAdd %uint %129 %120
+%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
+%132 = OpLoad %uint %131
+OpReturnValue %132
+OpFunctionEnd
+)";
+
+  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBindlessCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, true, true, 2u);
+}
+
+TEST_F(InstBindlessTest,
+       InstBoundsMissInitLoadVariableSizedSampledImagesArray) {
+  // #version 460
+  // #extension GL_EXT_nonuniform_qualifier : require
+  // #extension GL_NV_ray_tracing : require
+  //
+  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  //   uint index;
+  //   float red;
+  // } sbo;
+  //
+  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  //
+  // void main()
+  // {
+  //    sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint MissNV %main "main"
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+%void = OpTypeVoid
+)";
+
+  const std::string defs_after =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint MissNV %main "main" %89
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_39 Block
+OpMemberDecorate %_struct_39 0 Offset 0
+OpDecorate %41 DescriptorSet 7
+OpDecorate %41 Binding 1
+OpDecorate %_struct_63 Block
+OpMemberDecorate %_struct_63 0 Offset 0
+OpMemberDecorate %_struct_63 1 Offset 4
+OpDecorate %65 DescriptorSet 7
+OpDecorate %65 Binding 0
+OpDecorate %89 BuiltIn LaunchIdNV
+%void = OpTypeVoid
+)";
+
+  const std::string func_before =
+      R"(%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%20 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%uint_1 = OpConstant %uint 1
+%34 = OpTypeFunction %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_39 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
+%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%bool = OpTypeBool
+%57 = OpTypeFunction %void %uint %uint %uint %uint
+%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
+%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
+%uint_10 = OpConstant %uint 10
+%uint_4 = OpConstant %uint 4
+%uint_23 = OpConstant %uint 23
+%uint_2 = OpConstant %uint 2
+%uint_5317 = OpConstant %uint 5317
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%89 = OpVariable %_ptr_Input_v3uint Input
+%uint_5 = OpConstant %uint 5
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_8 = OpConstant %uint 8
+%uint_9 = OpConstant %uint 9
+%uint_51 = OpConstant %uint 51
+%113 = OpConstantNull %v4float
+%116 = OpTypeFunction %uint %uint %uint %uint %uint
+%uint_48 = OpConstant %uint 48
+%141 = OpConstantNull %uint
+%uint_54 = OpConstant %uint 54
+%main = OpFunction %void None %7
+%24 = OpLabel
+%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%134 = OpINotEqual %bool %133 %uint_0
+OpSelectionMerge %135 None
+OpBranchConditional %134 %136 %137
+%136 = OpLabel
+%138 = OpLoad %uint %25
+OpBranch %135
+%137 = OpLabel
+%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
+OpBranch %135
+%135 = OpLabel
+%142 = OpPhi %uint %138 %136 %141 %137
+%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+%28 = OpLoad %13 %27
+%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
+%50 = OpULessThan %bool %142 %48
+OpSelectionMerge %51 None
+OpBranchConditional %50 %52 %53
+%52 = OpLabel
+%54 = OpLoad %13 %27
+%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
+%144 = OpINotEqual %bool %143 %uint_0
+OpSelectionMerge %145 None
+OpBranchConditional %144 %146 %147
+%146 = OpLabel
+%148 = OpLoad %13 %27
+%149 = OpImageRead %v4float %148 %20
+OpBranch %145
+%147 = OpLabel
+%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
+OpBranch %145
+%145 = OpLabel
+%151 = OpPhi %v4float %149 %146 %113 %147
+OpBranch %51
+%53 = OpLabel
+%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
+OpBranch %51
+%51 = OpLabel
+%114 = OpPhi %v4float %151 %145 %113 %53
+%30 = OpCompositeExtract %float %114 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%153 = OpINotEqual %bool %152 %uint_0
+OpSelectionMerge %154 None
+OpBranchConditional %153 %155 %156
+%155 = OpLabel
+OpStore %31 %30
+OpBranch %154
+%156 = OpLabel
+%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
+OpBranch %154
+%154 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%33 = OpFunction %uint None %34
+%35 = OpFunctionParameter %uint
+%36 = OpFunctionParameter %uint
+%37 = OpLabel
+%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
+%44 = OpLoad %uint %43
+%45 = OpIAdd %uint %44 %36
+%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
+%47 = OpLoad %uint %46
+OpReturnValue %47
+OpFunctionEnd
+%56 = OpFunction %void None %57
+%58 = OpFunctionParameter %uint
+%59 = OpFunctionParameter %uint
+%60 = OpFunctionParameter %uint
+%61 = OpFunctionParameter %uint
+%62 = OpLabel
+%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
+%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
+%70 = OpIAdd %uint %69 %uint_10
+%71 = OpArrayLength %uint %65 1
+%72 = OpULessThanEqual %bool %70 %71
+OpSelectionMerge %73 None
+OpBranchConditional %72 %74 %73
+%74 = OpLabel
+%75 = OpIAdd %uint %69 %uint_0
+%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
+OpStore %76 %uint_10
+%78 = OpIAdd %uint %69 %uint_1
+%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
+OpStore %79 %uint_23
+%81 = OpIAdd %uint %69 %uint_2
+%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
+OpStore %82 %58
+%85 = OpIAdd %uint %69 %uint_3
+%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
+OpStore %86 %uint_5317
+%90 = OpLoad %v3uint %89
+%91 = OpCompositeExtract %uint %90 0
+%92 = OpCompositeExtract %uint %90 1
+%93 = OpCompositeExtract %uint %90 2
+%94 = OpIAdd %uint %69 %uint_4
+%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
+OpStore %95 %91
+%97 = OpIAdd %uint %69 %uint_5
+%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
+OpStore %98 %92
+%100 = OpIAdd %uint %69 %uint_6
+%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
+OpStore %101 %93
+%103 = OpIAdd %uint %69 %uint_7
+%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
+OpStore %104 %59
+%106 = OpIAdd %uint %69 %uint_8
+%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
+OpStore %107 %60
+%109 = OpIAdd %uint %69 %uint_9
+%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
+OpStore %110 %61
+OpBranch %73
+%73 = OpLabel
+OpReturn
+OpFunctionEnd
+%115 = OpFunction %uint None %116
+%117 = OpFunctionParameter %uint
+%118 = OpFunctionParameter %uint
+%119 = OpFunctionParameter %uint
+%120 = OpFunctionParameter %uint
+%121 = OpLabel
+%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
+%123 = OpLoad %uint %122
+%124 = OpIAdd %uint %123 %118
+%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
+%126 = OpLoad %uint %125
+%127 = OpIAdd %uint %126 %119
+%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
+%129 = OpLoad %uint %128
+%130 = OpIAdd %uint %129 %120
+%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
+%132 = OpLoad %uint %131
+OpReturnValue %132
+OpFunctionEnd
+)";
+
+  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBindlessCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, true, true, 2u);
+}
+
+TEST_F(InstBindlessTest,
+       InstBoundsCallableInitLoadVariableSizedSampledImagesArray) {
+  // #version 460
+  // #extension GL_EXT_nonuniform_qualifier : require
+  // #extension GL_NV_ray_tracing : require
+  //
+  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  //   uint index;
+  //   float red;
+  // } sbo;
+  //
+  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  //
+  // void main()
+  // {
+  //    sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint CallableNV %main "main"
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+%void = OpTypeVoid
+)";
+
+  const std::string defs_after =
+      R"(OpCapability RuntimeDescriptorArrayEXT
+OpCapability RayTracingNV
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint CallableNV %main "main" %89
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_NV_ray_tracing"
+OpName %main "main"
+OpName %StorageBuffer "StorageBuffer"
+OpMemberName %StorageBuffer 0 "index"
+OpMemberName %StorageBuffer 1 "red"
+OpName %sbo "sbo"
+OpName %images "images"
+OpMemberDecorate %StorageBuffer 0 Offset 0
+OpMemberDecorate %StorageBuffer 1 Offset 4
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_39 Block
+OpMemberDecorate %_struct_39 0 Offset 0
+OpDecorate %41 DescriptorSet 7
+OpDecorate %41 Binding 1
+OpDecorate %_struct_63 Block
+OpMemberDecorate %_struct_63 0 Offset 0
+OpMemberDecorate %_struct_63 1 Offset 4
+OpDecorate %65 DescriptorSet 7
+OpDecorate %65 Binding 0
+OpDecorate %89 BuiltIn LaunchIdNV
+%void = OpTypeVoid
+)";
+
+  const std::string func_before =
+      R"(%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%StorageBuffer = OpTypeStruct %uint %float
+%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
+%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%_runtimearr_13 = OpTypeRuntimeArray %13
+%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
+%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%20 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%uint_1 = OpConstant %uint 1
+%34 = OpTypeFunction %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_39 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
+%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%bool = OpTypeBool
+%57 = OpTypeFunction %void %uint %uint %uint %uint
+%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
+%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
+%uint_10 = OpConstant %uint 10
+%uint_4 = OpConstant %uint 4
+%uint_23 = OpConstant %uint 23
+%uint_2 = OpConstant %uint 2
+%uint_5318 = OpConstant %uint 5318
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%89 = OpVariable %_ptr_Input_v3uint Input
+%uint_5 = OpConstant %uint 5
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_8 = OpConstant %uint 8
+%uint_9 = OpConstant %uint 9
+%uint_51 = OpConstant %uint 51
+%113 = OpConstantNull %v4float
+%116 = OpTypeFunction %uint %uint %uint %uint %uint
+%uint_48 = OpConstant %uint 48
+%141 = OpConstantNull %uint
+%uint_54 = OpConstant %uint 54
+%main = OpFunction %void None %7
+%24 = OpLabel
+%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%134 = OpINotEqual %bool %133 %uint_0
+OpSelectionMerge %135 None
+OpBranchConditional %134 %136 %137
+%136 = OpLabel
+%138 = OpLoad %uint %25
+OpBranch %135
+%137 = OpLabel
+%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
+OpBranch %135
+%135 = OpLabel
+%142 = OpPhi %uint %138 %136 %141 %137
+%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+%28 = OpLoad %13 %27
+%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
+%50 = OpULessThan %bool %142 %48
+OpSelectionMerge %51 None
+OpBranchConditional %50 %52 %53
+%52 = OpLabel
+%54 = OpLoad %13 %27
+%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
+%144 = OpINotEqual %bool %143 %uint_0
+OpSelectionMerge %145 None
+OpBranchConditional %144 %146 %147
+%146 = OpLabel
+%148 = OpLoad %13 %27
+%149 = OpImageRead %v4float %148 %20
+OpBranch %145
+%147 = OpLabel
+%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
+OpBranch %145
+%145 = OpLabel
+%151 = OpPhi %v4float %149 %146 %113 %147
+OpBranch %51
+%53 = OpLabel
+%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
+OpBranch %51
+%51 = OpLabel
+%114 = OpPhi %v4float %151 %145 %113 %53
+%30 = OpCompositeExtract %float %114 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
+%153 = OpINotEqual %bool %152 %uint_0
+OpSelectionMerge %154 None
+OpBranchConditional %153 %155 %156
+%155 = OpLabel
+OpStore %31 %30
+OpBranch %154
+%156 = OpLabel
+%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
+OpBranch %154
+%154 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%33 = OpFunction %uint None %34
+%35 = OpFunctionParameter %uint
+%36 = OpFunctionParameter %uint
+%37 = OpLabel
+%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
+%44 = OpLoad %uint %43
+%45 = OpIAdd %uint %44 %36
+%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
+%47 = OpLoad %uint %46
+OpReturnValue %47
+OpFunctionEnd
+%56 = OpFunction %void None %57
+%58 = OpFunctionParameter %uint
+%59 = OpFunctionParameter %uint
+%60 = OpFunctionParameter %uint
+%61 = OpFunctionParameter %uint
+%62 = OpLabel
+%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
+%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
+%70 = OpIAdd %uint %69 %uint_10
+%71 = OpArrayLength %uint %65 1
+%72 = OpULessThanEqual %bool %70 %71
+OpSelectionMerge %73 None
+OpBranchConditional %72 %74 %73
+%74 = OpLabel
+%75 = OpIAdd %uint %69 %uint_0
+%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
+OpStore %76 %uint_10
+%78 = OpIAdd %uint %69 %uint_1
+%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
+OpStore %79 %uint_23
+%81 = OpIAdd %uint %69 %uint_2
+%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
+OpStore %82 %58
+%85 = OpIAdd %uint %69 %uint_3
+%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
+OpStore %86 %uint_5318
+%90 = OpLoad %v3uint %89
+%91 = OpCompositeExtract %uint %90 0
+%92 = OpCompositeExtract %uint %90 1
+%93 = OpCompositeExtract %uint %90 2
+%94 = OpIAdd %uint %69 %uint_4
+%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
+OpStore %95 %91
+%97 = OpIAdd %uint %69 %uint_5
+%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
+OpStore %98 %92
+%100 = OpIAdd %uint %69 %uint_6
+%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
+OpStore %101 %93
+%103 = OpIAdd %uint %69 %uint_7
+%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
+OpStore %104 %59
+%106 = OpIAdd %uint %69 %uint_8
+%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
+OpStore %107 %60
+%109 = OpIAdd %uint %69 %uint_9
+%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
+OpStore %110 %61
+OpBranch %73
+%73 = OpLabel
+OpReturn
+OpFunctionEnd
+%115 = OpFunction %uint None %116
+%117 = OpFunctionParameter %uint
+%118 = OpFunctionParameter %uint
+%119 = OpFunctionParameter %uint
+%120 = OpFunctionParameter %uint
+%121 = OpLabel
+%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
+%123 = OpLoad %uint %122
+%124 = OpIAdd %uint %123 %118
+%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
+%126 = OpLoad %uint %125
+%127 = OpIAdd %uint %126 %119
+%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
+%129 = OpLoad %uint %128
+%130 = OpIAdd %uint %129 %120
+%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
+%132 = OpLoad %uint %131
+OpReturnValue %132
+OpFunctionEnd
+)";
+
+  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBindlessCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, true, true, 2u);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //
 //   Compute shader
 //   Compute shader

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

@@ -163,7 +163,6 @@ TEST(Optimizer, CanRegisterPassesFromFlags) {
       "--eliminate-dead-branches",
       "--eliminate-dead-branches",
       "--eliminate-dead-functions",
       "--eliminate-dead-functions",
       "--eliminate-local-multi-store",
       "--eliminate-local-multi-store",
-      "--eliminate-common-uniform",
       "--eliminate-dead-const",
       "--eliminate-dead-const",
       "--eliminate-dead-inserts",
       "--eliminate-dead-inserts",
       "--eliminate-dead-variables",
       "--eliminate-dead-variables",

+ 56 - 0
3rdparty/spirv-tools/test/opt/pass_merge_return_test.cpp

@@ -1724,6 +1724,62 @@ OpFunctionEnd
   SinglePassRunAndMatch<MergeReturnPass>(predefs + caller + callee, true);
   SinglePassRunAndMatch<MergeReturnPass>(predefs + caller + callee, true);
 }
 }
 
 
+TEST_F(MergeReturnPassTest, MergeToMergeBranch) {
+  const std::string text =
+      R"(
+; CHECK: [[new_undef:%\w+]] = OpUndef %uint
+; CHECK: OpLoopMerge
+; CHECK: OpLoopMerge [[merge1:%\w+]]
+; CHECK: OpLoopMerge [[merge2:%\w+]]
+; CHECK: [[merge1]] = OpLabel
+; CHECK-NEXT: OpPhi %uint [[new_undef]] [[merge2]]
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main"
+               OpExecutionMode %2 LocalSize 100 1 1
+               OpSource ESSL 310
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+     %uint_0 = OpConstant %uint 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+         %13 = OpUndef %bool
+          %2 = OpFunction %void None %4
+         %14 = OpLabel
+               OpBranch %15
+         %15 = OpLabel
+               OpLoopMerge %16 %17 None
+               OpBranch %18
+         %18 = OpLabel
+               OpLoopMerge %19 %20 None
+               OpBranchConditional %13 %21 %19
+         %21 = OpLabel
+               OpReturn
+         %20 = OpLabel
+               OpBranch %18
+         %19 = OpLabel
+         %22 = OpUndef %uint
+               OpBranch %23
+         %23 = OpLabel
+               OpBranch %16
+         %17 = OpLabel
+               OpBranch %15
+         %16 = OpLabel
+         %24 = OpCopyObject %uint %22
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<MergeReturnPass>(text, true);
+}
+
 }  // namespace
 }  // namespace
 }  // namespace opt
 }  // namespace opt
 }  // namespace spvtools
 }  // namespace spvtools

+ 9 - 0
3rdparty/spirv-tools/test/tools/expect.py

@@ -79,6 +79,15 @@ class ReturnCodeIsZero(SpirvTest):
     return True, ''
     return True, ''
 
 
 
 
+class ReturnCodeIsNonZero(SpirvTest):
+  """Mixin class for checking that the return code is not zero."""
+
+  def check_return_code_is_nonzero(self, status):
+    if not status.returncode:
+      return False, 'return code is 0'
+    return True, ''
+
+
 class NoOutputOnStdout(SpirvTest):
 class NoOutputOnStdout(SpirvTest):
   """Mixin class for checking that there is no output on stdout."""
   """Mixin class for checking that there is no output on stdout."""
 
 

+ 43 - 2
3rdparty/spirv-tools/test/tools/opt/flags.py

@@ -59,7 +59,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4,
   flags = [
   flags = [
       '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
       '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
       '--convert-local-access-chains', '--copy-propagate-arrays',
       '--convert-local-access-chains', '--copy-propagate-arrays',
-      '--eliminate-common-uniform', '--eliminate-dead-branches',
+      '--eliminate-dead-branches',
       '--eliminate-dead-code-aggressive', '--eliminate-dead-const',
       '--eliminate-dead-code-aggressive', '--eliminate-dead-const',
       '--eliminate-dead-functions', '--eliminate-dead-inserts',
       '--eliminate-dead-functions', '--eliminate-dead-inserts',
       '--eliminate-dead-variables', '--eliminate-insert-extract',
       '--eliminate-dead-variables', '--eliminate-insert-extract',
@@ -82,7 +82,6 @@ class TestValidPassFlags(expect.ValidObjectFile1_4,
       'compact-ids',
       'compact-ids',
       'convert-local-access-chains',
       'convert-local-access-chains',
       'copy-propagate-arrays',
       'copy-propagate-arrays',
-      'eliminate-common-uniform',
       'eliminate-dead-branches',
       'eliminate-dead-branches',
       'eliminate-dead-code-aggressive',
       'eliminate-dead-code-aggressive',
       'eliminate-dead-const',
       'eliminate-dead-const',
@@ -332,3 +331,45 @@ class TestLoopPeelingThresholdArgsInvalidNumber(expect.ErrorMessageSubstr):
 
 
   spirv_args = ['--loop-peeling-threshold=a10f']
   spirv_args = ['--loop-peeling-threshold=a10f']
   expected_error_substr = 'must have a positive integer argument'
   expected_error_substr = 'must have a positive integer argument'
+
+@inside_spirv_testsuite('SpirvOptFlags')
+class TestWebGPUToVulkanThenVulkanToWebGPUIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
+  """Tests Vulkan->WebGPU flag cannot be used after WebGPU->Vulkan flag."""
+
+  spirv_args = ['--webgpu-to-vulkan', '--vulkan-to-webgpu']
+  expected_error_substr = 'Cannot use both'
+
+@inside_spirv_testsuite('SpirvOptFlags')
+class TestVulkanToWebGPUThenWebGPUToVulkanIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
+  """Tests WebGPU->Vulkan flag cannot be used after Vulkan->WebGPU flag."""
+
+  spirv_args = ['--vulkan-to-webgpu', '--webgpu-to-vulkan']
+  expected_error_substr = 'Cannot use both'
+
+@inside_spirv_testsuite('SpirvOptFlags')
+class TestTargetEnvThenVulkanToWebGPUIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
+  """Tests Vulkan->WebGPU flag cannot be used after target env flag."""
+
+  spirv_args = ['--target-env=opengl4.0', '--vulkan-to-webgpu']
+  expected_error_substr = 'defines the target environment'
+
+@inside_spirv_testsuite('SpirvOptFlags')
+class TestVulkanToWebGPUThenTargetEnvIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
+  """Tests target env flag cannot be used after Vulkan->WebGPU flag."""
+
+  spirv_args = ['--vulkan-to-webgpu', '--target-env=opengl4.0']
+  expected_error_substr = 'defines the target environment'
+
+@inside_spirv_testsuite('SpirvOptFlags')
+class TestTargetEnvThenWebGPUToVulkanIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
+  """Tests WebGPU->Vulkan flag cannot be used after target env flag."""
+
+  spirv_args = ['--target-env=opengl4.0', '--webgpu-to-vulkan']
+  expected_error_substr = 'defines the target environment'
+
+@inside_spirv_testsuite('SpirvOptFlags')
+class TestWebGPUToVulkanThenTargetEnvIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
+  """Tests target env flag cannot be used after WebGPU->Vulkan flag."""
+
+  spirv_args = ['--webgpu-to-vulkan', '--target-env=opengl4.0']
+  expected_error_substr = 'defines the target environment'

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

@@ -937,6 +937,26 @@ TEST_F(ValidateIdWithMessage, OpTypeStructMemberTypeBad) {
                         "a type."));
                         "a type."));
 }
 }
 
 
+TEST_F(ValidateIdWithMessage, OpTypeStructOpaqueTypeBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+          %1 = OpTypeSampler
+          %2 = OpTypeStruct %1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpTypeStruct must not contain an opaque type"));
+}
+
 TEST_F(ValidateIdWithMessage, OpTypePointerGood) {
 TEST_F(ValidateIdWithMessage, OpTypePointerGood) {
   std::string spirv = kGLSL450MemoryModel + R"(
   std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeInt 32 0
 %1 = OpTypeInt 32 0

+ 16 - 18
3rdparty/spirv-tools/tools/opt/opt.cpp

@@ -147,13 +147,6 @@ Options (in lexicographical order):)",
                around known issues with some Vulkan drivers for initialize
                around known issues with some Vulkan drivers for initialize
                variables.)");
                variables.)");
   printf(R"(
   printf(R"(
-  --eliminate-common-uniform
-               Perform load/load elimination for duplicate uniform values.
-               Converts any constant index access chain uniform loads into
-               its equivalent load and extract. Some loads will be moved
-               to facilitate sharing. Performed only on entry point
-               call tree functions.)");
-  printf(R"(
   --eliminate-dead-branches
   --eliminate-dead-branches
                Convert conditional branches with constant condition to the
                Convert conditional branches with constant condition to the
                indicated unconditional brranch. Delete all resulting dead
                indicated unconditional brranch. Delete all resulting dead
@@ -728,16 +721,17 @@ OptStatus ParseFlags(int argc, const char** argv,
                                              max_id_bound);
                                              max_id_bound);
       } else if (0 == strncmp(cur_arg,
       } else if (0 == strncmp(cur_arg,
                               "--target-env=", sizeof("--target-env=") - 1)) {
                               "--target-env=", sizeof("--target-env=") - 1)) {
+        target_env_set = true;
         if (vulkan_to_webgpu_set) {
         if (vulkan_to_webgpu_set) {
           spvtools::Error(opt_diagnostic, nullptr, {},
           spvtools::Error(opt_diagnostic, nullptr, {},
-                          "Cannot use both --vulkan-to-webgpu and --target-env "
-                          "at the same time");
+                          "--vulkan-to-webgpu defines the target environment, "
+                          "so --target-env cannot be set at the same time");
           return {OPT_STOP, 1};
           return {OPT_STOP, 1};
         }
         }
         if (webgpu_to_vulkan_set) {
         if (webgpu_to_vulkan_set) {
           spvtools::Error(opt_diagnostic, nullptr, {},
           spvtools::Error(opt_diagnostic, nullptr, {},
-                          "Cannot use both --webgpu-to-vulkan and --target-env "
-                          "at the same time");
+                          "--webgpu-to-vulkan defines the target environment, "
+                          "so --target-env cannot be set at the same time");
           return {OPT_STOP, 1};
           return {OPT_STOP, 1};
         }
         }
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
@@ -750,32 +744,36 @@ OptStatus ParseFlags(int argc, const char** argv,
         }
         }
         optimizer->SetTargetEnv(target_env);
         optimizer->SetTargetEnv(target_env);
       } else if (0 == strcmp(cur_arg, "--vulkan-to-webgpu")) {
       } else if (0 == strcmp(cur_arg, "--vulkan-to-webgpu")) {
+        vulkan_to_webgpu_set = true;
         if (target_env_set) {
         if (target_env_set) {
           spvtools::Error(opt_diagnostic, nullptr, {},
           spvtools::Error(opt_diagnostic, nullptr, {},
-                          "Cannot use both --vulkan-to-webgpu and --target-env "
-                          "at the same time");
+                          "--vulkan-to-webgpu defines the target environment, "
+                          "so --target-env cannot be set at the same time");
           return {OPT_STOP, 1};
           return {OPT_STOP, 1};
         }
         }
         if (webgpu_to_vulkan_set) {
         if (webgpu_to_vulkan_set) {
           spvtools::Error(opt_diagnostic, nullptr, {},
           spvtools::Error(opt_diagnostic, nullptr, {},
-                          "Cannot use both --vulkan-to-webgpu and "
-                          "--webgpu-to-vulkan at the same time");
+                          "Cannot use both --webgpu-to-vulkan and "
+                          "--vulkan-to-webgpu at the same time, invoke twice "
+                          "if you are wanting to go to and from");
           return {OPT_STOP, 1};
           return {OPT_STOP, 1};
         }
         }
 
 
         optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
         optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
         optimizer->RegisterVulkanToWebGPUPasses();
         optimizer->RegisterVulkanToWebGPUPasses();
       } else if (0 == strcmp(cur_arg, "--webgpu-to-vulkan")) {
       } else if (0 == strcmp(cur_arg, "--webgpu-to-vulkan")) {
+        webgpu_to_vulkan_set = true;
         if (target_env_set) {
         if (target_env_set) {
           spvtools::Error(opt_diagnostic, nullptr, {},
           spvtools::Error(opt_diagnostic, nullptr, {},
-                          "Cannot use both --webgpu-to-vulkan and --target-env "
-                          "at the same time");
+                          "--webgpu-to-vulkan defines the target environment, "
+                          "so --target-env cannot be set at the same time");
           return {OPT_STOP, 1};
           return {OPT_STOP, 1};
         }
         }
         if (vulkan_to_webgpu_set) {
         if (vulkan_to_webgpu_set) {
           spvtools::Error(opt_diagnostic, nullptr, {},
           spvtools::Error(opt_diagnostic, nullptr, {},
                           "Cannot use both --webgpu-to-vulkan and "
                           "Cannot use both --webgpu-to-vulkan and "
-                          "--vulkan-to-webgpu at the same time");
+                          "--vulkan-to-webgpu at the same time, invoke twice "
+                          "if you are wanting to go to and from");
           return {OPT_STOP, 1};
           return {OPT_STOP, 1};
         }
         }