Explorar o código

Updated spirv-tools.

Бранимир Караџић %!s(int64=6) %!d(string=hai) anos
pai
achega
cfb5da28f0
Modificáronse 29 ficheiros con 2239 adicións e 2567 borrados
  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/code_sink.cpp \
 		source/opt/combine_access_chains.cpp \
-		source/opt/common_uniform_elim_pass.cpp \
 		source/opt/compact_ids_pass.cpp \
 		source/opt/composite.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/combine_access_chains.cpp",
     "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.h",
     "source/opt/composite.cpp",
@@ -670,6 +668,7 @@ static_library("spvtools_link") {
   ]
   deps = [
     ":spvtools",
+    ":spvtools_opt",
     ":spvtools_val",
   ]
   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_finder.cpp",
     "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h",
+    "source/spirv_reducer_options.cpp",
+    "source/spirv_reducer_options.h",
   ]
   deps = [
     ":spvtools",
@@ -865,6 +866,7 @@ source_set("spvtools_software_version") {
   ]
   deps = [
     ":spvtools_build_version",
+    ":spvtools_headers",
   ]
   configs += [ ":spvtools_internal_config" ]
 }
@@ -949,12 +951,11 @@ if (!is_ios) {
   # iOS does not allow std::system calls which spirv-reduce requires
   executable("spirv-reduce") {
     sources = [
-      "source/spirv_reducer_options.cpp",
-      "source/spirv_reducer_options.h",
       "tools/reduce/reduce.cpp",
     ]
     deps = [
       ":spvtools",
+      ":spvtools_opt",
       ":spvtools_reduce",
       ":spvtools_software_version",
       ":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 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
 static const int kInstStageOutCnt = kInstCommonOutCnt + 2;
 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.
 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
 // This pass eliminates unused code from the module. In addition,
 // 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
   code_sink.h
   combine_access_chains.h
-  common_uniform_elim_pass.h
   compact_ids_pass.h
   composite.h
   const_folding_rules.h
@@ -123,7 +122,6 @@ set(SPIRV_TOOLS_OPT_SOURCES
   cfg.cpp
   code_sink.cpp
   combine_access_chains.cpp
-  common_uniform_elim_pass.cpp
   compact_ids_pass.cpp
   composite.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;
 }
 
-bool InlinePass::IsInlinableFunctionCall(Instruction* inst) {
+bool InlinePass::IsInlinableFunctionCall(const Instruction* inst) {
   if (inst->opcode() != SpvOp::SpvOpFunctionCall) return false;
   const uint32_t calleeFnId =
       inst->GetSingleWordOperand(kSpvFunctionCallFunctionId);
   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(
@@ -738,9 +711,6 @@ bool InlinePass::IsInlinableFunction(Function* func) {
   // 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.
   // 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);
   if (no_return_in_loop_.find(func->result_id()) == no_return_in_loop_.cend()) {
     return false;
@@ -771,13 +741,6 @@ void InlinePass::InitializeInline() {
     }
     // Compute inlinability
     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);
 
   // 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
   // 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
   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
   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;
 }
 
+Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
+  i.get()->InsertBefore(this);
+  return i.release();
+}
+
 Instruction* Instruction::InsertBefore(
     std::vector<std::unique_ptr<Instruction>>&& list) {
   Instruction* first_node = list.front().get();
@@ -382,11 +387,6 @@ Instruction* Instruction::InsertBefore(
   return first_node;
 }
 
-Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
-  i.get()->InsertBefore(this);
-  return i.release();
-}
-
 bool Instruction::IsValidBasePointer() const {
   uint32_t tid = type_id();
   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;
 
-  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);
+  // 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;
 
   // 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(
             base_offset_id, uint_frag_coord_inst->result_id(), u, builder);
     } 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;
   }
 }
@@ -843,7 +865,12 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
       stage != SpvExecutionModelGeometry &&
       stage != SpvExecutionModelGLCompute &&
       stage != SpvExecutionModelTessellationControl &&
-      stage != SpvExecutionModelTessellationEvaluation)
+      stage != SpvExecutionModelTessellationEvaluation &&
+      stage != SpvExecutionModelRayGenerationNV &&
+      stage != SpvExecutionModelIntersectionNV &&
+      stage != SpvExecutionModelAnyHitNV &&
+      stage != SpvExecutionModelClosestHitNV &&
+      stage != SpvExecutionModelMissNV && stage != SpvExecutionModelCallableNV)
     return false;
   // Add together the roots of all entry points
   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);
         break;
       }
-      case SpvBuiltInGlobalInvocationId: {
+      case SpvBuiltInGlobalInvocationId:
+      case SpvBuiltInLaunchIdNV: {
         analysis::Integer uint_ty(32, false);
         analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
         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->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {target}}});
   context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst);
+  new_edges_[target_block].insert(block->id());
   cfg()->AddEdge(block->id(), target);
 }
 
@@ -236,28 +237,18 @@ void MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source,
     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,
                                             Instruction& inst) {
   DominatorAnalysis* dom_tree =
       context()->GetDominatorAnalysis(merge_block->GetParent());
-  BasicBlock* inst_bb = context()->get_instr_block(&inst);
 
   if (inst.result_id() != 0) {
+    BasicBlock* inst_bb = context()->get_instr_block(&inst);
     std::vector<Instruction*> users_to_update;
     context()->get_def_use_mgr()->ForEachUser(
         &inst,
@@ -295,12 +286,13 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
         IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
     uint32_t undef_id = Type2Undef(inst.type_id());
     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,
     // otherwise use |inst|'s id.
     std::vector<uint32_t> preds = cfg()->preds(merge_block->id());
     for (uint32_t pred_id : preds) {
-      if (return_blocks_.count(pred_id)) {
+      if (new_edges.count(pred_id)) {
         phi_operands.push_back(undef_id);
       } else {
         phi_operands.push_back(inst.result_id());
@@ -417,6 +409,8 @@ bool MergeReturnPass::BreakFromConstruct(
   auto old_body_id = TakeNextId();
   BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter);
   predicated->insert(old_body);
+  cfg()->AddEdges(old_body);
+
   // If a return block is being split, mark the new body block also as a return
   // block.
   if (return_blocks_.count(block->id())) {
@@ -456,14 +450,15 @@ bool MergeReturnPass::BreakFromConstruct(
   builder.AddConditionalBranch(load_id, merge_block->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
   // because |UpdatePhiNodes| assumes the edge from |block| has not been added
   // to the CFG yet.
@@ -659,26 +654,37 @@ void MergeReturnPass::MergeReturnBlocks(
 }
 
 void MergeReturnPass::AddNewPhiNodes() {
-  DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(function_);
   std::list<BasicBlock*> order;
   cfg()->ComputeStructuredOrder(function_, &*function_->begin(), &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_);
-  // 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) {
       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,

+ 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.
   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
   // added by calling |AddNewPhiNodes|.  It is assumed that |block| used to have
   // 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
   // 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
-  // phi nodes.  See |MarkForNewPhiNodes|.
+  // phi nodes.  See |RecordImmediateDominator|.
   //
   // The CFG must not include the edge from |new_source| to |target| yet.
   void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
@@ -301,6 +289,11 @@ class MergeReturnPass : public MemPass {
   // |merge_target| as the merge node.
   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
   // constructs.
   std::vector<StructuredControlState> state_;
@@ -324,12 +317,13 @@ class MergeReturnPass : public MemPass {
   // after processing the current function.
   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_;
-  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
   // 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(CreateBlockMergePass())
       .RegisterPass(CreateSimplificationPass());
-  // Currently exposing driver bugs resulting in crashes (#946)
-  // .RegisterPass(CreateCommonUniformElimPass())
 }
 
 Optimizer& Optimizer::RegisterSizePasses() {
@@ -215,8 +213,6 @@ Optimizer& Optimizer::RegisterSizePasses() {
       .RegisterPass(CreateDeadInsertElimPass())
       .RegisterPass(CreateRedundancyEliminationPass())
       .RegisterPass(CreateCFGCleanupPass())
-      // Currently exposing driver bugs resulting in crashes (#946)
-      // .RegisterPass(CreateCommonUniformElimPass())
       .RegisterPass(CreateAggressiveDCEPass());
 }
 
@@ -340,8 +336,6 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
     RegisterPass(CreateEliminateDeadFunctionsPass());
   } else if (pass_name == "eliminate-local-multi-store") {
     RegisterPass(CreateLocalMultiStoreElimPass());
-  } else if (pass_name == "eliminate-common-uniform") {
-    RegisterPass(CreateCommonUniformElimPass());
   } else if (pass_name == "eliminate-dead-const") {
     RegisterPass(CreateEliminateDeadConstantPass());
   } else if (pass_name == "eliminate-dead-inserts") {
@@ -713,11 +707,6 @@ Optimizer::PassToken CreateRedundantLineInfoElimPass() {
       MakeUnique<opt::ProcessLinesPass>(opt::kLinesEliminateDeadLines));
 }
 
-Optimizer::PassToken CreateCommonUniformElimPass() {
-  return MakeUnique<Optimizer::PassToken::Impl>(
-      MakeUnique<opt::CommonUniformElimPass>());
-}
-
 Optimizer::PassToken CreateCompactIdsPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
       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/code_sink.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/copy_prop_arrays.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;
 }
 
+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) {
   const uint32_t struct_id = inst->GetOperandAs<uint32_t>(0);
   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) {
     _.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;
 }
 

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

@@ -24,7 +24,6 @@ add_spvtools_unittest(TARGET opt
        cfg_test.cpp
        code_sink_test.cpp
        combine_access_chains_test.cpp
-       common_uniform_elim_test.cpp
        compact_ids_test.cpp
        constant_manager_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);
 }
 
-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:
 //
 //    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);
 }
 
+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:
 //
 //   Compute shader

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

@@ -163,7 +163,6 @@ TEST(Optimizer, CanRegisterPassesFromFlags) {
       "--eliminate-dead-branches",
       "--eliminate-dead-functions",
       "--eliminate-local-multi-store",
-      "--eliminate-common-uniform",
       "--eliminate-dead-const",
       "--eliminate-dead-inserts",
       "--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);
 }
 
+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 opt
 }  // namespace spvtools

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

@@ -79,6 +79,15 @@ class ReturnCodeIsZero(SpirvTest):
     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):
   """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 = [
       '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
       '--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-functions', '--eliminate-dead-inserts',
       '--eliminate-dead-variables', '--eliminate-insert-extract',
@@ -82,7 +82,6 @@ class TestValidPassFlags(expect.ValidObjectFile1_4,
       'compact-ids',
       'convert-local-access-chains',
       'copy-propagate-arrays',
-      'eliminate-common-uniform',
       'eliminate-dead-branches',
       'eliminate-dead-code-aggressive',
       'eliminate-dead-const',
@@ -332,3 +331,45 @@ class TestLoopPeelingThresholdArgsInvalidNumber(expect.ErrorMessageSubstr):
 
   spirv_args = ['--loop-peeling-threshold=a10f']
   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."));
 }
 
+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) {
   std::string spirv = kGLSL450MemoryModel + R"(
 %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
                variables.)");
   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
                Convert conditional branches with constant condition to the
                indicated unconditional brranch. Delete all resulting dead
@@ -728,16 +721,17 @@ OptStatus ParseFlags(int argc, const char** argv,
                                              max_id_bound);
       } else if (0 == strncmp(cur_arg,
                               "--target-env=", sizeof("--target-env=") - 1)) {
+        target_env_set = true;
         if (vulkan_to_webgpu_set) {
           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};
         }
         if (webgpu_to_vulkan_set) {
           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};
         }
         const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
@@ -750,32 +744,36 @@ OptStatus ParseFlags(int argc, const char** argv,
         }
         optimizer->SetTargetEnv(target_env);
       } else if (0 == strcmp(cur_arg, "--vulkan-to-webgpu")) {
+        vulkan_to_webgpu_set = true;
         if (target_env_set) {
           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};
         }
         if (webgpu_to_vulkan_set) {
           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};
         }
 
         optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
         optimizer->RegisterVulkanToWebGPUPasses();
       } else if (0 == strcmp(cur_arg, "--webgpu-to-vulkan")) {
+        webgpu_to_vulkan_set = true;
         if (target_env_set) {
           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};
         }
         if (vulkan_to_webgpu_set) {
           spvtools::Error(opt_diagnostic, nullptr, {},
                           "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};
         }