Instrument: Add support for Buffer Device Address extension (#2792)

diff --git a/Android.mk b/Android.mk
index a3146cf..d2fe6a6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -117,6 +117,7 @@
 		source/opt/inline_exhaustive_pass.cpp \
 		source/opt/inline_opaque_pass.cpp \
 		source/opt/inst_bindless_check_pass.cpp \
+		source/opt/inst_buff_addr_check_pass.cpp \
 		source/opt/instruction.cpp \
 		source/opt/instruction_list.cpp \
 		source/opt/instrument_pass.cpp \
diff --git a/BUILD.gn b/BUILD.gn
index 8e4dd91..6237af3 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -535,6 +535,8 @@
     "source/opt/inline_pass.h",
     "source/opt/inst_bindless_check_pass.cpp",
     "source/opt/inst_bindless_check_pass.h",
+    "source/opt/inst_buff_addr_check_pass.cpp",
+    "source/opt/inst_buff_addr_check_pass.h",
     "source/opt/instruction.cpp",
     "source/opt/instruction.h",
     "source/opt/instruction_list.cpp",
diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp
index dfd6e35..681d008 100644
--- a/include/spirv-tools/instrument.hpp
+++ b/include/spirv-tools/instrument.hpp
@@ -23,8 +23,9 @@
 // communicate with shaders instrumented by passes created by:
 //
 //   CreateInstBindlessCheckPass
+//   CreateInstBuffAddrCheckPass
 //
-// More detailed documentation of this routine can be found in optimizer.hpp
+// More detailed documentation of these routines can be found in optimizer.hpp
 
 namespace spvtools {
 
@@ -157,6 +158,12 @@
 static const int kInst2BindlessUninitOutUnused = kInst2StageOutCnt + 2;
 static const int kInst2BindlessUninitOutCnt = kInst2StageOutCnt + 3;
 
+// A buffer address unalloc error will output the 64-bit pointer in
+// two 32-bit pieces, lower bits first.
+static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1;
+static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2;
+static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3;
+
 // DEPRECATED
 static const int kInstBindlessOutDescIndex = kInstStageOutCnt + 1;
 static const int kInstBindlessOutDescBound = kInstStageOutCnt + 2;
@@ -171,6 +178,7 @@
 // These are the possible validation error codes.
 static const int kInstErrorBindlessBounds = 0;
 static const int kInstErrorBindlessUninit = 1;
+static const int kInstErrorBuffAddrUnallocRef = 2;
 
 // Direct Input Buffer Offsets
 //
@@ -187,14 +195,16 @@
 // These are the bindings for the different buffers which are
 // read or written by the instrumentation passes.
 //
-// This is the output buffer written by InstBindlessCheckPass
-// and possibly other future validations.
+// This is the output buffer written by InstBindlessCheckPass,
+// InstBuffAddrCheckPass, and possibly other future validations.
 static const int kDebugOutputBindingStream = 0;
 
-// The binding for the input buffer read by InstBindlessCheckPass and
-// possibly other future validations.
+// The binding for the input buffer read by InstBindlessCheckPass.
 static const int kDebugInputBindingBindless = 1;
 
+// The binding for the input buffer read by InstBuffAddrCheckPass.
+static const int kDebugInputBindingBuffAddr = 2;
+
 // Bindless Validation Input Buffer Format
 //
 // An input buffer for bindless validation consists of a single array of
@@ -216,6 +226,31 @@
 // Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ]
 static const int kDebugInputBindlessOffsetLengths = 1;
 
+// Buffer Device Address Input Buffer Format
+//
+// An input buffer for buffer device address validation consists of a single
+// array of unsigned 64-bit integers we will call Data[]. This array is
+// formatted as follows:
+//
+// At offset kDebugInputBuffAddrPtrOffset is a list of sorted valid buffer
+// addresses. The list is terminated with the address 0xffffffffffffffff.
+// If 0x0 is not a valid buffer address, this address is inserted at the
+// start of the list.
+//
+static const int kDebugInputBuffAddrPtrOffset = 1;
+//
+// At offset kDebugInputBuffAddrLengthOffset in Data[] is a single uint64 which
+// gives an offset to the start of the buffer length data. More
+// specifically, for a buffer whose pointer is located at input buffer offset
+// i, the length is located at:
+//
+// Data[ i - kDebugInputBuffAddrPtrOffset
+//         + Data[ kDebugInputBuffAddrLengthOffset ] ]
+//
+// The length associated with the 0xffffffffffffffff address is zero. If
+// not a valid buffer, the length associated with the 0x0 address is zero.
+static const int kDebugInputBuffAddrLengthOffset = 0;
+
 }  // namespace spvtools
 
 #endif  // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index 22f2115..cec1f16 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -729,6 +729,30 @@
     uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false,
     bool input_init_enable = false, uint32_t version = 1);
 
+// Create a pass to instrument physical buffer address checking
+// This pass instruments all physical buffer address references to check that
+// all referenced bytes fall in a valid buffer. If the reference is
+// invalid, a record is written to the debug output buffer (if space allows)
+// and a null value is returned. This pass is designed to support buffer
+// address validation in the Vulkan validation layers.
+//
+// Dead code elimination should be run after this pass as the original,
+// potentially invalid code is not removed and could cause undefined behavior,
+// including crashes. Instruction simplification would likely also be
+// beneficial. It is also generally recommended that this pass (and all
+// instrumentation passes) be run after any legalization and optimization
+// passes. This will give better analysis for the instrumentation and avoid
+// potentially de-optimizing the instrument code, for example, inlining
+// the debug record output function throughout the module.
+//
+// The instrumentation will read and write buffers in debug
+// descriptor set |desc_set|. It will write |shader_id| in each output record
+// to identify the shader module which generated the record.
+// |version| specifies the output buffer record format.
+Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
+                                                 uint32_t shader_id,
+                                                 uint32_t version = 2);
+
 // Create a pass to upgrade to the VulkanKHR memory model.
 // This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
 // Additionally, it modifies memory, image, atomic and barrier operations to
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 6a3d702..bf7fe13 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -55,6 +55,7 @@
   inline_opaque_pass.h
   inline_pass.h
   inst_bindless_check_pass.h
+  inst_buff_addr_check_pass.h
   instruction.h
   instruction_list.h
   instrument_pass.h
@@ -158,6 +159,7 @@
   inline_opaque_pass.cpp
   inline_pass.cpp
   inst_bindless_check_pass.cpp
+  inst_buff_addr_check_pass.cpp
   instruction.cpp
   instruction_list.cpp
   instrument_pass.cpp
diff --git a/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp
new file mode 100644
index 0000000..6629379
--- /dev/null
+++ b/source/opt/inst_buff_addr_check_pass.cpp
@@ -0,0 +1,427 @@
+// Copyright (c) 2019 The Khronos Group Inc.
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "inst_buff_addr_check_pass.h"
+
+namespace spvtools {
+namespace opt {
+
+uint32_t InstBuffAddrCheckPass::CloneOriginalReference(
+    Instruction* ref_inst, InstructionBuilder* builder) {
+  // Clone original ref with new result id (if load)
+  assert(
+      (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) &&
+      "unexpected ref");
+  std::unique_ptr<Instruction> new_ref_inst(ref_inst->Clone(context()));
+  uint32_t ref_result_id = ref_inst->result_id();
+  uint32_t new_ref_id = 0;
+  if (ref_result_id != 0) {
+    new_ref_id = TakeNextId();
+    new_ref_inst->SetResultId(new_ref_id);
+  }
+  // Register new reference and add to new block
+  Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst));
+  uid2offset_[added_inst->unique_id()] = uid2offset_[ref_inst->unique_id()];
+  if (new_ref_id != 0)
+    get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id);
+  return new_ref_id;
+}
+
+bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) {
+  if (ref_inst->opcode() != SpvOpLoad && ref_inst->opcode() != SpvOpStore)
+    return false;
+  uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0);
+  analysis::DefUseManager* du_mgr = get_def_use_mgr();
+  Instruction* ptr_inst = du_mgr->GetDef(ptr_id);
+  if (ptr_inst->opcode() != SpvOpAccessChain) return false;
+  uint32_t ptr_ty_id = ptr_inst->type_id();
+  Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id);
+  if (ptr_ty_inst->GetSingleWordInOperand(0) !=
+      SpvStorageClassPhysicalStorageBufferEXT)
+    return false;
+  return true;
+}
+
+// TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ??
+void InstBuffAddrCheckPass::GenCheckCode(
+    uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id,
+    uint32_t stage_idx, Instruction* ref_inst,
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  BasicBlock* back_blk_ptr = &*new_blocks->back();
+  InstructionBuilder builder(
+      context(), back_blk_ptr,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  // Gen conditional branch on check_id. Valid branch generates original
+  // reference. Invalid generates debug output and zero result (if needed).
+  uint32_t merge_blk_id = TakeNextId();
+  uint32_t valid_blk_id = TakeNextId();
+  uint32_t invalid_blk_id = TakeNextId();
+  std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
+  std::unique_ptr<Instruction> valid_label(NewLabel(valid_blk_id));
+  std::unique_ptr<Instruction> invalid_label(NewLabel(invalid_blk_id));
+  (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id,
+                                     merge_blk_id, SpvSelectionControlMaskNone);
+  // Gen valid branch
+  std::unique_ptr<BasicBlock> new_blk_ptr(
+      new BasicBlock(std::move(valid_label)));
+  builder.SetInsertPoint(&*new_blk_ptr);
+  uint32_t new_ref_id = CloneOriginalReference(ref_inst, &builder);
+  (void)builder.AddBranch(merge_blk_id);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  // Gen invalid block
+  new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
+  builder.SetInsertPoint(&*new_blk_ptr);
+  // Convert uptr from uint64 to 2 uint32
+  Instruction* lo_uptr_inst =
+      builder.AddUnaryOp(GetUintId(), SpvOpUConvert, ref_uptr_id);
+  Instruction* rshift_uptr_inst =
+      builder.AddBinaryOp(GetUint64Id(), SpvOpShiftRightLogical, ref_uptr_id,
+                          builder.GetUintConstantId(32));
+  Instruction* hi_uptr_inst = builder.AddUnaryOp(GetUintId(), SpvOpUConvert,
+                                                 rshift_uptr_inst->result_id());
+  GenDebugStreamWrite(
+      uid2offset_[ref_inst->unique_id()], stage_idx,
+      {error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
+      &builder);
+  // Gen zero for invalid  reference
+  uint32_t ref_type_id = ref_inst->type_id();
+  (void)builder.AddBranch(merge_blk_id);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  // Gen merge block
+  new_blk_ptr.reset(new BasicBlock(std::move(merge_label)));
+  builder.SetInsertPoint(&*new_blk_ptr);
+  // Gen phi of new reference and zero, if necessary, and replace the
+  // result id of the original reference with that of the Phi. Kill original
+  // reference.
+  if (new_ref_id != 0) {
+    Instruction* phi_inst = builder.AddPhi(
+        ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id),
+                      invalid_blk_id});
+    context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id());
+  }
+  new_blocks->push_back(std::move(new_blk_ptr));
+  context()->KillInst(ref_inst);
+}
+
+uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) {
+  Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+  switch (type_inst->opcode()) {
+    case SpvOpTypeFloat:
+    case SpvOpTypeInt:
+      return type_inst->GetSingleWordInOperand(0) / 8u;
+    case SpvOpTypeVector:
+    case SpvOpTypeMatrix:
+      return type_inst->GetSingleWordInOperand(1) *
+             GetTypeLength(type_inst->GetSingleWordInOperand(0));
+    case SpvOpTypePointer:
+      assert(type_inst->GetSingleWordInOperand(0) ==
+                 SpvStorageClassPhysicalStorageBufferEXT &&
+             "unexpected pointer type");
+      return 8u;
+    default:
+      assert(false && "unexpected buffer reference type");
+      return 0;
+  }
+}
+
+void InstBuffAddrCheckPass::AddParam(uint32_t type_id,
+                                     std::vector<uint32_t>* param_vec,
+                                     std::unique_ptr<Function>* input_func) {
+  uint32_t pid = TakeNextId();
+  param_vec->push_back(pid);
+  std::unique_ptr<Instruction> param_inst(new Instruction(
+      get_module()->context(), SpvOpFunctionParameter, type_id, pid, {}));
+  get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
+  (*input_func)->AddParameter(std::move(param_inst));
+}
+
+uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() {
+  if (search_test_func_id_ == 0) {
+    // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)"
+    // which searches input buffer for buffer which most likely contains the
+    // pointer value |ref_ptr| and verifies that the entire reference of
+    // length |len| bytes is contained in the buffer.
+    search_test_func_id_ = TakeNextId();
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    std::vector<const analysis::Type*> param_types = {
+        type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())};
+    analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types);
+    analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
+    std::unique_ptr<Instruction> func_inst(
+        new Instruction(get_module()->context(), SpvOpFunction, GetBoolId(),
+                        search_test_func_id_,
+                        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+                          {SpvFunctionControlMaskNone}},
+                         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+                          {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
+    get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
+    std::unique_ptr<Function> input_func =
+        MakeUnique<Function>(std::move(func_inst));
+    std::vector<uint32_t> param_vec;
+    // Add ref_ptr and length parameters
+    AddParam(GetUint64Id(), &param_vec, &input_func);
+    AddParam(GetUintId(), &param_vec, &input_func);
+    // Empty first block.
+    uint32_t first_blk_id = TakeNextId();
+    std::unique_ptr<Instruction> first_blk_label(NewLabel(first_blk_id));
+    std::unique_ptr<BasicBlock> first_blk_ptr =
+        MakeUnique<BasicBlock>(std::move(first_blk_label));
+    InstructionBuilder builder(
+        context(), &*first_blk_ptr,
+        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+    uint32_t hdr_blk_id = TakeNextId();
+    // Branch to search loop header
+    std::unique_ptr<Instruction> hdr_blk_label(NewLabel(hdr_blk_id));
+    (void)builder.AddInstruction(MakeUnique<Instruction>(
+        context(), SpvOpBranch, 0, 0,
+        std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {hdr_blk_id}}}));
+    first_blk_ptr->SetParent(&*input_func);
+    input_func->AddBasicBlock(std::move(first_blk_ptr));
+    // Linear search loop header block
+    // TODO(greg-lunarg): Implement binary search
+    std::unique_ptr<BasicBlock> hdr_blk_ptr =
+        MakeUnique<BasicBlock>(std::move(hdr_blk_label));
+    builder.SetInsertPoint(&*hdr_blk_ptr);
+    // Phi for search index. Starts with 1.
+    uint32_t cont_blk_id = TakeNextId();
+    std::unique_ptr<Instruction> cont_blk_label(NewLabel(cont_blk_id));
+    // Deal with def-use cycle caused by search loop index computation.
+    // Create Add and Phi instructions first, then do Def analysis on Add.
+    // Add Phi and Add instructions and do Use analysis later.
+    uint32_t idx_phi_id = TakeNextId();
+    uint32_t idx_inc_id = TakeNextId();
+    std::unique_ptr<Instruction> idx_inc_inst(new Instruction(
+        context(), SpvOpIAdd, GetUintId(), idx_inc_id,
+        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+          {builder.GetUintConstantId(1u)}}}));
+    std::unique_ptr<Instruction> idx_phi_inst(new Instruction(
+        context(), SpvOpPhi, GetUintId(), idx_phi_id,
+        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+          {builder.GetUintConstantId(1u)}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
+    get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst);
+    // Add (previously created) search index phi
+    (void)builder.AddInstruction(std::move(idx_phi_inst));
+    // LoopMerge
+    uint32_t bound_test_blk_id = TakeNextId();
+    std::unique_ptr<Instruction> bound_test_blk_label(
+        NewLabel(bound_test_blk_id));
+    (void)builder.AddInstruction(MakeUnique<Instruction>(
+        context(), SpvOpLoopMerge, 0, 0,
+        std::initializer_list<Operand>{
+            {SPV_OPERAND_TYPE_ID, {bound_test_blk_id}},
+            {SPV_OPERAND_TYPE_ID, {cont_blk_id}},
+            {SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvLoopControlMaskNone}}}));
+    // Branch to continue/work block
+    (void)builder.AddInstruction(MakeUnique<Instruction>(
+        context(), SpvOpBranch, 0, 0,
+        std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
+    hdr_blk_ptr->SetParent(&*input_func);
+    input_func->AddBasicBlock(std::move(hdr_blk_ptr));
+    // Continue/Work Block. Read next buffer pointer and break if greater
+    // than ref_ptr arg.
+    std::unique_ptr<BasicBlock> cont_blk_ptr =
+        MakeUnique<BasicBlock>(std::move(cont_blk_label));
+    builder.SetInsertPoint(&*cont_blk_ptr);
+    // Add (previously created) search index increment now.
+    (void)builder.AddInstruction(std::move(idx_inc_inst));
+    // Load next buffer address from debug input buffer
+    uint32_t ibuf_id = GetInputBufferId();
+    uint32_t ibuf_ptr_id = GetInputBufferPtrId();
+    Instruction* uptr_ac_inst = builder.AddTernaryOp(
+        ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
+        builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id);
+    uint32_t ibuf_type_id = GetInputBufferTypeId();
+    Instruction* uptr_load_inst =
+        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, uptr_ac_inst->result_id());
+    // If loaded address greater than ref_ptr arg, break, else branch back to
+    // loop header
+    Instruction* uptr_test_inst =
+        builder.AddBinaryOp(GetBoolId(), SpvOpUGreaterThan,
+                            uptr_load_inst->result_id(), param_vec[0]);
+    (void)builder.AddConditionalBranch(uptr_test_inst->result_id(),
+                                       bound_test_blk_id, hdr_blk_id,
+                                       kInvalidId, SpvSelectionControlMaskNone);
+    cont_blk_ptr->SetParent(&*input_func);
+    input_func->AddBasicBlock(std::move(cont_blk_ptr));
+    // Bounds test block. Read length of selected buffer and test that
+    // all len arg bytes are in buffer.
+    std::unique_ptr<BasicBlock> bound_test_blk_ptr =
+        MakeUnique<BasicBlock>(std::move(bound_test_blk_label));
+    builder.SetInsertPoint(&*bound_test_blk_ptr);
+    // Decrement index to point to previous/candidate buffer address
+    Instruction* cand_idx_inst = builder.AddBinaryOp(
+        GetUintId(), SpvOpISub, idx_inc_id, builder.GetUintConstantId(1u));
+    // Load candidate buffer address
+    Instruction* cand_ac_inst =
+        builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
+                             builder.GetUintConstantId(kDebugInputDataOffset),
+                             cand_idx_inst->result_id());
+    Instruction* cand_load_inst =
+        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, cand_ac_inst->result_id());
+    // Compute offset of ref_ptr from candidate buffer address
+    Instruction* offset_inst = builder.AddBinaryOp(
+        ibuf_type_id, SpvOpISub, param_vec[0], cand_load_inst->result_id());
+    // Convert ref length to uint64
+    Instruction* ref_len_64_inst =
+        builder.AddUnaryOp(ibuf_type_id, SpvOpUConvert, param_vec[1]);
+    // Add ref length to ref offset to compute end of reference
+    Instruction* ref_end_inst =
+        builder.AddBinaryOp(ibuf_type_id, SpvOpIAdd, offset_inst->result_id(),
+                            ref_len_64_inst->result_id());
+    // Load starting index of lengths in input buffer and convert to uint32
+    Instruction* len_start_ac_inst =
+        builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
+                             builder.GetUintConstantId(kDebugInputDataOffset),
+                             builder.GetUintConstantId(0u));
+    Instruction* len_start_load_inst = builder.AddUnaryOp(
+        ibuf_type_id, SpvOpLoad, len_start_ac_inst->result_id());
+    Instruction* len_start_32_inst = builder.AddUnaryOp(
+        GetUintId(), SpvOpUConvert, len_start_load_inst->result_id());
+    // Decrement search index to get candidate buffer length index
+    Instruction* cand_len_idx_inst =
+        builder.AddBinaryOp(GetUintId(), SpvOpISub, cand_idx_inst->result_id(),
+                            builder.GetUintConstantId(1u));
+    // Add candidate length index to start index
+    Instruction* len_idx_inst = builder.AddBinaryOp(
+        GetUintId(), SpvOpIAdd, cand_len_idx_inst->result_id(),
+        len_start_32_inst->result_id());
+    // Load candidate buffer length
+    Instruction* len_ac_inst =
+        builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
+                             builder.GetUintConstantId(kDebugInputDataOffset),
+                             len_idx_inst->result_id());
+    Instruction* len_load_inst =
+        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, len_ac_inst->result_id());
+    // Test if reference end within candidate buffer length
+    Instruction* len_test_inst = builder.AddBinaryOp(
+        GetBoolId(), SpvOpULessThanEqual, ref_end_inst->result_id(),
+        len_load_inst->result_id());
+    // Return test result
+    (void)builder.AddInstruction(MakeUnique<Instruction>(
+        context(), SpvOpReturnValue, 0, 0,
+        std::initializer_list<Operand>{
+            {SPV_OPERAND_TYPE_ID, {len_test_inst->result_id()}}}));
+    // Close block
+    bound_test_blk_ptr->SetParent(&*input_func);
+    input_func->AddBasicBlock(std::move(bound_test_blk_ptr));
+    // Close function and add function to module
+    std::unique_ptr<Instruction> func_end_inst(
+        new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {}));
+    get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
+    input_func->SetFunctionEnd(std::move(func_end_inst));
+    context()->AddFunction(std::move(input_func));
+  }
+  return search_test_func_id_;
+}
+
+uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst,
+                                                 InstructionBuilder* builder,
+                                                 uint32_t* ref_uptr_id) {
+  // Enable Int64 if necessary
+  if (!get_feature_mgr()->HasCapability(SpvCapabilityInt64)) {
+    std::unique_ptr<Instruction> cap_int64_inst(new Instruction(
+        context(), SpvOpCapability, 0, 0,
+        std::initializer_list<Operand>{
+            {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityInt64}}}));
+    get_def_use_mgr()->AnalyzeInstDefUse(&*cap_int64_inst);
+    get_module()->AddCapability(std::move(cap_int64_inst));
+  }
+  // Convert reference pointer to uint64
+  uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
+  Instruction* ref_uptr_inst =
+      builder->AddUnaryOp(GetUint64Id(), SpvOpConvertPtrToU, ref_ptr_id);
+  *ref_uptr_id = ref_uptr_inst->result_id();
+  // Compute reference length in bytes
+  analysis::DefUseManager* du_mgr = get_def_use_mgr();
+  Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id);
+  uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
+  Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id);
+  uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
+  uint32_t ref_len_id = builder->GetUintConstantId(ref_len);
+  // Gen call to search and test function
+  const std::vector<uint32_t> args = {GetSearchAndTestFuncId(), *ref_uptr_id,
+                                      ref_len_id};
+  Instruction* call_inst =
+      builder->AddNaryOp(GetBoolId(), SpvOpFunctionCall, args);
+  uint32_t retval = call_inst->result_id();
+  return retval;
+}
+
+void InstBuffAddrCheckPass::GenBuffAddrCheckCode(
+    BasicBlock::iterator ref_inst_itr,
+    UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  // Look for reference through indexed descriptor. If found, analyze and
+  // save components. If not, return.
+  Instruction* ref_inst = &*ref_inst_itr;
+  if (!IsPhysicalBuffAddrReference(ref_inst)) return;
+  // Move original block's preceding instructions into first new block
+  std::unique_ptr<BasicBlock> new_blk_ptr;
+  MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
+  InstructionBuilder builder(
+      context(), &*new_blk_ptr,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef);
+  // Generate code to do search and test if all bytes of reference
+  // are within a listed buffer. Return reference pointer converted to uint64.
+  uint32_t ref_uptr_id;
+  uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id);
+  // Generate test of search results with true branch
+  // being full reference and false branch being debug output and zero
+  // for the referenced value.
+  GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst,
+               new_blocks);
+  // Move original block's remaining code into remainder/merge block and add
+  // to new blocks
+  BasicBlock* back_blk_ptr = &*new_blocks->back();
+  MovePostludeCode(ref_block_itr, back_blk_ptr);
+}
+
+void InstBuffAddrCheckPass::InitInstBuffAddrCheck() {
+  // Initialize base class
+  InitializeInstrument();
+  // Initialize class
+  search_test_func_id_ = 0;
+}
+
+Pass::Status InstBuffAddrCheckPass::ProcessImpl() {
+  // Perform bindless bounds check on each entry point function in module
+  InstProcessFunction pfn =
+      [this](BasicBlock::iterator ref_inst_itr,
+             UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+             std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+        return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
+                                    new_blocks);
+      };
+  bool modified = InstProcessEntryPointCallTree(pfn);
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+Pass::Status InstBuffAddrCheckPass::Process() {
+  if (!get_feature_mgr()->HasCapability(
+          SpvCapabilityPhysicalStorageBufferAddressesEXT))
+    return Status::SuccessWithoutChange;
+  InitInstBuffAddrCheck();
+  return ProcessImpl();
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h
new file mode 100644
index 0000000..9ad3528
--- /dev/null
+++ b/source/opt/inst_buff_addr_check_pass.h
@@ -0,0 +1,133 @@
+// Copyright (c) 2019 The Khronos Group Inc.
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
+#define LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
+
+#include "instrument_pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// This class/pass is designed to support the GPU-assisted validation layer of
+// the Buffer Device Address (BDA) extension in
+// https://github.com/KhronosGroup/Vulkan-ValidationLayers. The internal and
+// external design of this class may change as the layer evolves.
+class InstBuffAddrCheckPass : public InstrumentPass {
+ public:
+  // For test harness only
+  InstBuffAddrCheckPass()
+      : InstrumentPass(7, 23, kInstValidationIdBuffAddr, 1) {}
+  // For all other interfaces
+  InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version)
+      : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr,
+                       version) {}
+
+  ~InstBuffAddrCheckPass() override = default;
+
+  // See optimizer.hpp for pass user documentation.
+  Status Process() override;
+
+  const char* name() const override { return "inst-bindless-check-pass"; }
+
+ private:
+  // Return byte length of type |type_id|. Must be int, float, vector, matrix
+  // or physical pointer.
+  uint32_t GetTypeLength(uint32_t type_id);
+
+  // Add |type_id| param to |input_func| and add id to |param_vec|.
+  void AddParam(uint32_t type_id, std::vector<uint32_t>* param_vec,
+                std::unique_ptr<Function>* input_func);
+
+  // Return id for search and test function. Generate it if not already gen'd.
+  uint32_t GetSearchAndTestFuncId();
+
+  // Generate code into |builder| to do search of the BDA debug input buffer
+  // for the buffer used by |ref_inst| and test that all bytes of reference
+  // are within the buffer. Returns id of boolean value which is true if
+  // search and test is successful, false otherwise.
+  uint32_t GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder,
+                            uint32_t* ref_uptr_id);
+
+  // This function does checking instrumentation on a single
+  // instruction which references through a physical storage buffer address.
+  // GenBuffAddrCheckCode generates code that checks that all bytes that
+  // are referenced fall within a buffer that was queried via
+  // the Vulkan API call vkGetBufferDeviceAddressEXT().
+  //
+  // The function is designed to be passed to
+  // InstrumentPass::InstProcessEntryPointCallTree(), which applies the
+  // function to each instruction in a module and replaces the instruction
+  // with instrumented code if warranted.
+  //
+  // If |ref_inst_itr| is a physical storage buffer reference, return in
+  // |new_blocks| the result of instrumenting it with validation code within
+  // its block at |ref_block_itr|.  The validation code first executes a check
+  // for the specific condition called for. If the check passes, it executes
+  // the remainder of the reference, otherwise writes a record to the debug
+  // output buffer stream including |function_idx, instruction_idx, stage_idx|
+  // and replaces the reference with the null value of the original type. The
+  // block at |ref_block_itr| can just be replaced with the blocks in
+  // |new_blocks|, which will contain at least two blocks. The last block will
+  // comprise all instructions following |ref_inst_itr|,
+  // preceded by a phi instruction if needed.
+  //
+  // This instrumentation function utilizes GenDebugStreamWrite() to write its
+  // error records. The validation-specific part of the error record will
+  // have the format:
+  //
+  //    Validation Error Code (=kInstErrorBuffAddr)
+  //    Buffer Address (lowest 32 bits)
+  //    Buffer Address (highest 32 bits)
+  //
+  void GenBuffAddrCheckCode(
+      BasicBlock::iterator ref_inst_itr,
+      UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+      std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Return true if |ref_inst| is a physical buffer address reference, false
+  // otherwise.
+  bool IsPhysicalBuffAddrReference(Instruction* ref_inst);
+
+  // Clone original reference |ref_inst| into |builder| and return id of result
+  uint32_t CloneOriginalReference(Instruction* ref_inst,
+                                  InstructionBuilder* builder);
+
+  // Generate instrumentation code for boolean test result |check_id|,
+  // adding new blocks to |new_blocks|. Generate conditional branch to valid
+  // or invalid reference blocks. Generate valid reference block which does
+  // original reference |ref_inst|. Then generate invalid reference block which
+  // writes debug error output utilizing |ref_inst|, |error_id| and
+  // |stage_idx|. Generate merge block for valid and invalid reference blocks.
+  // Kill original reference.
+  void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t length_id,
+                    uint32_t stage_idx, Instruction* ref_inst,
+                    std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Initialize state for instrumenting physical buffer address checking
+  void InitInstBuffAddrCheck();
+
+  // Apply GenBuffAddrCheckCode to every instruction in module.
+  Pass::Status ProcessImpl();
+
+  // Id of search and test function, if already gen'd, else zero.
+  uint32_t search_test_func_id_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp
index 00bb918..418759b 100644
--- a/source/opt/instrument_pass.cpp
+++ b/source/opt/instrument_pass.cpp
@@ -107,7 +107,7 @@
       builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id,
                            builder->GetUintConstantId(field_offset));
   uint32_t buf_id = GetOutputBufferId();
-  uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
+  uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
   Instruction* achain_inst =
       builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
                             builder->GetUintConstantId(kDebugOutputDataOffset),
@@ -373,19 +373,33 @@
       });
 }
 
-// Return id for output buffer uint ptr type
-uint32_t InstrumentPass::GetBufferUintPtrId() {
-  if (buffer_uint_ptr_id_ == 0) {
-    buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
+uint32_t InstrumentPass::GetOutputBufferPtrId() {
+  if (output_buffer_ptr_id_ == 0) {
+    output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
         GetUintId(), SpvStorageClassStorageBuffer);
   }
-  return buffer_uint_ptr_id_;
+  return output_buffer_ptr_id_;
+}
+
+uint32_t InstrumentPass::GetInputBufferTypeId() {
+  return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id()
+                                                       : GetUintId();
+}
+
+uint32_t InstrumentPass::GetInputBufferPtrId() {
+  if (input_buffer_ptr_id_ == 0) {
+    input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
+        GetInputBufferTypeId(), SpvStorageClassStorageBuffer);
+  }
+  return input_buffer_ptr_id_;
 }
 
 uint32_t InstrumentPass::GetOutputBufferBinding() {
   switch (validation_id_) {
     case kInstValidationIdBindless:
       return kDebugOutputBindingStream;
+    case kInstValidationIdBuffAddr:
+      return kDebugOutputBindingStream;
     default:
       assert(false && "unexpected validation id");
   }
@@ -396,20 +410,24 @@
   switch (validation_id_) {
     case kInstValidationIdBindless:
       return kDebugInputBindingBindless;
+    case kInstValidationIdBuffAddr:
+      return kDebugInputBindingBuffAddr;
     default:
       assert(false && "unexpected validation id");
   }
   return 0;
 }
 
-analysis::Type* InstrumentPass::GetUintRuntimeArrayType(
-    analysis::DecorationManager* deco_mgr, analysis::TypeManager* type_mgr) {
-  if (uint_rarr_ty_ == nullptr) {
-    analysis::Integer uint_ty(32, false);
+analysis::Type* InstrumentPass::GetUintXRuntimeArrayType(
+    uint32_t width, analysis::Type** rarr_ty) {
+  if (*rarr_ty == nullptr) {
+    analysis::DecorationManager* deco_mgr = get_decoration_mgr();
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::Integer uint_ty(width, false);
     analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
     analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty);
-    uint_rarr_ty_ = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
-    uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(uint_rarr_ty_);
+    *rarr_ty = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
+    uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(*rarr_ty);
     // By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of
     // a block, and will therefore be decorated with an ArrayStride. Therefore
     // the undecorated type returned here will not be pre-existing and can
@@ -418,9 +436,16 @@
     // invalidated after this pass.
     assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 &&
            "used RuntimeArray type returned");
-    deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u);
+    deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride,
+                               width / 8u);
   }
-  return uint_rarr_ty_;
+  return *rarr_ty;
+}
+
+analysis::Type* InstrumentPass::GetUintRuntimeArrayType(uint32_t width) {
+  analysis::Type** rarr_ty =
+      (width == 64) ? &uint64_rarr_ty_ : &uint32_rarr_ty_;
+  return GetUintXRuntimeArrayType(width, rarr_ty);
 }
 
 void InstrumentPass::AddStorageBufferExt() {
@@ -445,8 +470,7 @@
     // If not created yet, create one
     analysis::DecorationManager* deco_mgr = get_decoration_mgr();
     analysis::TypeManager* type_mgr = context()->get_type_mgr();
-    analysis::Type* reg_uint_rarr_ty =
-        GetUintRuntimeArrayType(deco_mgr, type_mgr);
+    analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(32);
     analysis::Integer uint_ty(32, false);
     analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
     analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty});
@@ -494,8 +518,8 @@
     // If not created yet, create one
     analysis::DecorationManager* deco_mgr = get_decoration_mgr();
     analysis::TypeManager* type_mgr = context()->get_type_mgr();
-    analysis::Type* reg_uint_rarr_ty =
-        GetUintRuntimeArrayType(deco_mgr, type_mgr);
+    uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u;
+    analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width);
     analysis::Struct buf_ty({reg_uint_rarr_ty});
     analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty);
     uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
@@ -555,6 +579,16 @@
   return uint_id_;
 }
 
+uint32_t InstrumentPass::GetUint64Id() {
+  if (uint64_id_ == 0) {
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::Integer uint64_ty(64, false);
+    analysis::Type* reg_uint64_ty = type_mgr->GetRegisteredType(&uint64_ty);
+    uint64_id_ = type_mgr->GetTypeInstruction(reg_uint64_ty);
+  }
+  return uint64_id_;
+}
+
 uint32_t InstrumentPass::GetVecUintId(uint32_t len) {
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::Integer uint_ty(32, false);
@@ -642,7 +676,7 @@
         (version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt;
     uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt;
     uint32_t buf_id = GetOutputBufferId();
-    uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
+    uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
     Instruction* obuf_curr_sz_ac_inst =
         builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
                             builder.GetUintConstantId(kDebugOutputSizeOffset));
@@ -713,16 +747,17 @@
 uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
   uint32_t func_id = param2input_func_id_[param_cnt];
   if (func_id != 0) return func_id;
-  // Create input function for param_cnt
+  // Create input function for param_cnt.
   func_id = TakeNextId();
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   std::vector<const analysis::Type*> param_types;
   for (uint32_t c = 0; c < param_cnt; ++c)
     param_types.push_back(type_mgr->GetType(GetUintId()));
-  analysis::Function func_ty(type_mgr->GetType(GetUintId()), param_types);
+  uint32_t ibuf_type_id = GetInputBufferTypeId();
+  analysis::Function func_ty(type_mgr->GetType(ibuf_type_id), param_types);
   analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
   std::unique_ptr<Instruction> func_inst(new Instruction(
-      get_module()->context(), SpvOpFunction, GetUintId(), func_id,
+      get_module()->context(), SpvOpFunction, ibuf_type_id, func_id,
       {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
         {SpvFunctionControlMaskNone}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
@@ -752,22 +787,27 @@
   // loaded value if it exists, and load value from input buffer at new offset.
   // Return last loaded value.
   uint32_t buf_id = GetInputBufferId();
-  uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
+  uint32_t buf_ptr_id = GetInputBufferPtrId();
   uint32_t last_value_id = 0;
   for (uint32_t p = 0; p < param_cnt; ++p) {
     uint32_t offset_id;
     if (p == 0) {
       offset_id = param_vec[0];
     } else {
+      if (ibuf_type_id != GetUintId()) {
+        Instruction* ucvt_inst =
+            builder.AddUnaryOp(GetUintId(), SpvOpUConvert, last_value_id);
+        last_value_id = ucvt_inst->result_id();
+      }
       Instruction* offset_inst = builder.AddBinaryOp(
           GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]);
       offset_id = offset_inst->result_id();
     }
     Instruction* ac_inst = builder.AddTernaryOp(
-        buf_uint_ptr_id, SpvOpAccessChain, buf_id,
+        buf_ptr_id, SpvOpAccessChain, buf_id,
         builder.GetUintConstantId(kDebugInputDataOffset), offset_id);
     Instruction* load_inst =
-        builder.AddUnaryOp(GetUintId(), SpvOpLoad, ac_inst->result_id());
+        builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, ac_inst->result_id());
     last_value_id = load_inst->result_id();
   }
   (void)builder.AddInstruction(MakeUnique<Instruction>(
@@ -894,18 +934,21 @@
 
 void InstrumentPass::InitializeInstrument() {
   output_buffer_id_ = 0;
-  buffer_uint_ptr_id_ = 0;
+  output_buffer_ptr_id_ = 0;
+  input_buffer_ptr_id_ = 0;
   output_func_id_ = 0;
   output_func_param_cnt_ = 0;
   input_buffer_id_ = 0;
   v4float_id_ = 0;
   uint_id_ = 0;
+  uint64_id_ = 0;
   v4uint_id_ = 0;
   v3uint_id_ = 0;
   bool_id_ = 0;
   void_id_ = 0;
   storage_buffer_ext_defined_ = false;
-  uint_rarr_ty_ = nullptr;
+  uint32_rarr_ty_ = nullptr;
+  uint64_rarr_ty_ = nullptr;
 
   // clear collections
   id2function_.clear();
diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h
index 1b3f83e..ead3b73 100644
--- a/source/opt/instrument_pass.h
+++ b/source/opt/instrument_pass.h
@@ -60,6 +60,7 @@
 // These are used to identify the general validation being done and map to
 // its output buffers.
 static const uint32_t kInstValidationIdBindless = 0;
+static const uint32_t kInstValidationIdBuffAddr = 1;
 
 class InstrumentPass : public Pass {
   using cbb_ptr = const BasicBlock*;
@@ -218,17 +219,29 @@
   uint32_t GetUintId();
 
   // Return id for 32-bit unsigned type
+  uint32_t GetUint64Id();
+
+  // Return id for 32-bit unsigned type
   uint32_t GetBoolId();
 
   // Return id for void type
   uint32_t GetVoidId();
 
   // Return pointer to type for runtime array of uint
-  analysis::Type* GetUintRuntimeArrayType(analysis::DecorationManager* deco_mgr,
-                                          analysis::TypeManager* type_mgr);
+  analysis::Type* GetUintXRuntimeArrayType(uint32_t width,
+                                           analysis::Type** rarr_ty);
+
+  // Return pointer to type for runtime array of uint
+  analysis::Type* GetUintRuntimeArrayType(uint32_t width);
 
   // Return id for buffer uint type
-  uint32_t GetBufferUintPtrId();
+  uint32_t GetOutputBufferPtrId();
+
+  // Return id for buffer uint type
+  uint32_t GetInputBufferTypeId();
+
+  // Return id for buffer uint type
+  uint32_t GetInputBufferPtrId();
 
   // Return binding for output buffer for current validation.
   uint32_t GetOutputBufferBinding();
@@ -354,8 +367,11 @@
   // id for output buffer variable
   uint32_t output_buffer_id_;
 
-  // type id for output buffer element
-  uint32_t buffer_uint_ptr_id_;
+  // ptr type id for output buffer element
+  uint32_t output_buffer_ptr_id_;
+
+  // ptr type id for input buffer element
+  uint32_t input_buffer_ptr_id_;
 
   // id for debug output function
   uint32_t output_func_id_;
@@ -381,6 +397,9 @@
   // id for 32-bit unsigned type
   uint32_t uint_id_;
 
+  // id for 32-bit unsigned type
+  uint32_t uint64_id_;
+
   // id for bool type
   uint32_t bool_id_;
 
@@ -394,7 +413,10 @@
   bool storage_buffer_ext_defined_;
 
   // runtime array of uint type
-  analysis::Type* uint_rarr_ty_;
+  analysis::Type* uint64_rarr_ty_;
+
+  // runtime array of uint type
+  analysis::Type* uint32_rarr_ty_;
 
   // Pre-instrumentation same-block insts
   std::unordered_map<uint32_t, Instruction*> same_block_pre_;
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index 0881afc..cbdda2d 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -400,11 +400,20 @@
   } else if (pass_name == "replace-invalid-opcode") {
     RegisterPass(CreateReplaceInvalidOpcodePass());
   } else if (pass_name == "inst-bindless-check") {
+    RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2));
+    RegisterPass(CreateSimplificationPass());
+    RegisterPass(CreateDeadBranchElimPass());
+    RegisterPass(CreateBlockMergePass());
+    RegisterPass(CreateAggressiveDCEPass());
+  } else if (pass_name == "inst-desc-idx-check") {
     RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2));
     RegisterPass(CreateSimplificationPass());
     RegisterPass(CreateDeadBranchElimPass());
     RegisterPass(CreateBlockMergePass());
     RegisterPass(CreateAggressiveDCEPass());
+  } else if (pass_name == "inst-buff-addr-check") {
+    RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2));
+    RegisterPass(CreateAggressiveDCEPass());
   } else if (pass_name == "simplify-instructions") {
     RegisterPass(CreateSimplificationPass());
   } else if (pass_name == "ssa-rewrite") {
@@ -859,6 +868,13 @@
                                              input_init_enable, version));
 }
 
+Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
+                                                 uint32_t shader_id,
+                                                 uint32_t version) {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
+}
+
 Optimizer::PassToken CreateCodeSinkingPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
       MakeUnique<opt::CodeSinkingPass>());
diff --git a/source/opt/passes.h b/source/opt/passes.h
index 5476e88..1dede0e 100644
--- a/source/opt/passes.h
+++ b/source/opt/passes.h
@@ -43,6 +43,7 @@
 #include "source/opt/inline_exhaustive_pass.h"
 #include "source/opt/inline_opaque_pass.h"
 #include "source/opt/inst_bindless_check_pass.h"
+#include "source/opt/inst_buff_addr_check_pass.h"
 #include "source/opt/legalize_vector_shuffle_pass.h"
 #include "source/opt/licm_pass.h"
 #include "source/opt/local_access_chain_convert_pass.h"
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index d723dc3..7c92f8e 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -52,6 +52,7 @@
        inline_test.cpp
        insert_extract_elim_test.cpp
        inst_bindless_check_test.cpp
+       inst_buff_addr_check_test.cpp
        instruction_list_test.cpp
        instruction_test.cpp
        ir_builder.cpp
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp
new file mode 100644
index 0000000..f859ee5
--- /dev/null
+++ b/test/opt/inst_buff_addr_check_test.cpp
@@ -0,0 +1,620 @@
+// Copyright (c) 2019 Valve Corporation
+// Copyright (c) 2019 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Bindless Check Instrumentation Tests.
+// Tests ending with V2 use version 2 record format.
+
+#include <string>
+#include <vector>
+
+#include "test/opt/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using InstBuffAddrTest = PassTest<::testing::Test>;
+
+TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) {
+  // #version 450
+  // #extension GL_EXT_buffer_reference : enable
+  //
+  // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
+  //
+  // layout(set = 0, binding = 0) uniform ufoo {
+  //     bufStruct data;
+  //     uint offset;
+  // } u_info;
+  //
+  // layout(buffer_reference, std140) buffer bufStruct {
+  //     layout(offset = 0) int a[2];
+  //     layout(offset = 32) int b;
+  // };
+  //
+  // void main() {
+  //     u_info.data.b = 0xca7;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability PhysicalStorageBufferAddressesEXT
+OpExtension "SPV_EXT_physical_storage_buffer"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %ufoo "ufoo"
+OpMemberName %ufoo 0 "data"
+OpMemberName %ufoo 1 "offset"
+OpName %bufStruct "bufStruct"
+OpMemberName %bufStruct 0 "a"
+OpMemberName %bufStruct 1 "b"
+OpName %u_info "u_info"
+OpMemberDecorate %ufoo 0 Offset 0
+OpMemberDecorate %ufoo 1 Offset 8
+OpDecorate %ufoo Block
+OpDecorate %_arr_int_uint_2 ArrayStride 16
+OpMemberDecorate %bufStruct 0 Offset 0
+OpMemberDecorate %bufStruct 1 Offset 32
+OpDecorate %bufStruct Block
+OpDecorate %u_info DescriptorSet 0
+OpDecorate %u_info Binding 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
+%uint = OpTypeInt 32 0
+%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
+%int = OpTypeInt 32 1
+%uint_2 = OpConstant %uint 2
+%_arr_int_uint_2 = OpTypeArray %int %uint_2
+%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
+%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
+%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
+%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
+%int_1 = OpConstant %int 1
+%int_3239 = OpConstant %int 3239
+%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability Int64
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %ufoo "ufoo"
+OpMemberName %ufoo 0 "data"
+OpMemberName %ufoo 1 "offset"
+OpName %bufStruct "bufStruct"
+OpMemberName %bufStruct 0 "a"
+OpMemberName %bufStruct 1 "b"
+OpName %u_info "u_info"
+OpMemberDecorate %ufoo 0 Offset 0
+OpMemberDecorate %ufoo 1 Offset 8
+OpDecorate %ufoo Block
+OpDecorate %_arr_int_uint_2 ArrayStride 16
+OpMemberDecorate %bufStruct 0 Offset 0
+OpMemberDecorate %bufStruct 1 Offset 32
+OpDecorate %bufStruct Block
+OpDecorate %u_info DescriptorSet 0
+OpDecorate %u_info Binding 0
+OpDecorate %_runtimearr_ulong ArrayStride 8
+OpDecorate %_struct_39 Block
+OpMemberDecorate %_struct_39 0 Offset 0
+OpDecorate %41 DescriptorSet 7
+OpDecorate %41 Binding 2
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_77 Block
+OpMemberDecorate %_struct_77 0 Offset 0
+OpMemberDecorate %_struct_77 1 Offset 4
+OpDecorate %79 DescriptorSet 7
+OpDecorate %79 Binding 0
+OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT
+%uint = OpTypeInt 32 0
+%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint
+%int = OpTypeInt 32 1
+%uint_2 = OpConstant %uint 2
+%_arr_int_uint_2 = OpTypeArray %int %uint_2
+%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
+%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct
+%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
+%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct
+%int_1 = OpConstant %int 1
+%int_3239 = OpConstant %int 3239
+%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%ulong = OpTypeInt 64 0
+%uint_4 = OpConstant %uint 4
+%bool = OpTypeBool
+%28 = OpTypeFunction %bool %ulong %uint
+%uint_1 = OpConstant %uint 1
+%_runtimearr_ulong = OpTypeRuntimeArray %ulong
+%_struct_39 = OpTypeStruct %_runtimearr_ulong
+%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
+%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
+%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
+%uint_0 = OpConstant %uint 0
+%uint_32 = OpConstant %uint 32
+%70 = OpTypeFunction %void %uint %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_77 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77
+%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%uint_10 = OpConstant %uint 10
+%uint_23 = OpConstant %uint 23
+%uint_5 = OpConstant %uint 5
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_8 = OpConstant %uint 8
+%uint_9 = OpConstant %uint 9
+%uint_48 = OpConstant %uint 48
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
+%18 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %17
+%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %18 %int_1
+OpStore %22 %int_3239 Aligned 16
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %8
+%19 = OpLabel
+%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0
+%21 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %20
+%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %21 %int_1
+%24 = OpConvertPtrToU %ulong %22
+%61 = OpFunctionCall %bool %26 %24 %uint_4
+OpSelectionMerge %62 None
+OpBranchConditional %61 %63 %64
+%63 = OpLabel
+OpStore %22 %int_3239 Aligned 16
+OpBranch %62
+%64 = OpLabel
+%65 = OpUConvert %uint %24
+%67 = OpShiftRightLogical %ulong %24 %uint_32
+%68 = OpUConvert %uint %67
+%124 = OpFunctionCall %void %69 %uint_48 %uint_2 %65 %68
+OpBranch %62
+%62 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%26 = OpFunction %bool None %28
+%29 = OpFunctionParameter %ulong
+%30 = OpFunctionParameter %uint
+%31 = OpLabel
+OpBranch %32
+%32 = OpLabel
+%34 = OpPhi %uint %uint_1 %31 %35 %33
+OpLoopMerge %37 %33 None
+OpBranch %33
+%33 = OpLabel
+%35 = OpIAdd %uint %34 %uint_1
+%44 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %35
+%45 = OpLoad %ulong %44
+%46 = OpUGreaterThan %bool %45 %29
+OpBranchConditional %46 %37 %32
+%37 = OpLabel
+%47 = OpISub %uint %35 %uint_1
+%48 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %47
+%49 = OpLoad %ulong %48
+%50 = OpISub %ulong %29 %49
+%51 = OpUConvert %ulong %30
+%52 = OpIAdd %ulong %50 %51
+%53 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %uint_0
+%54 = OpLoad %ulong %53
+%55 = OpUConvert %uint %54
+%56 = OpISub %uint %47 %uint_1
+%57 = OpIAdd %uint %56 %55
+%58 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %57
+%59 = OpLoad %ulong %58
+%60 = OpULessThanEqual %bool %52 %59
+OpReturnValue %60
+OpFunctionEnd
+%69 = OpFunction %void None %70
+%71 = OpFunctionParameter %uint
+%72 = OpFunctionParameter %uint
+%73 = OpFunctionParameter %uint
+%74 = OpFunctionParameter %uint
+%75 = OpLabel
+%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0
+%83 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10
+%84 = OpIAdd %uint %83 %uint_10
+%85 = OpArrayLength %uint %79 1
+%86 = OpULessThanEqual %bool %84 %85
+OpSelectionMerge %87 None
+OpBranchConditional %86 %88 %87
+%88 = OpLabel
+%89 = OpIAdd %uint %83 %uint_0
+%90 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %89
+OpStore %90 %uint_10
+%92 = OpIAdd %uint %83 %uint_1
+%93 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %92
+OpStore %93 %uint_23
+%94 = OpIAdd %uint %83 %uint_2
+%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94
+OpStore %95 %71
+%98 = OpIAdd %uint %83 %uint_3
+%99 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %98
+OpStore %99 %uint_5
+%103 = OpLoad %v3uint %gl_GlobalInvocationID
+%104 = OpCompositeExtract %uint %103 0
+%105 = OpCompositeExtract %uint %103 1
+%106 = OpCompositeExtract %uint %103 2
+%107 = OpIAdd %uint %83 %uint_4
+%108 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %107
+OpStore %108 %104
+%109 = OpIAdd %uint %83 %uint_5
+%110 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %109
+OpStore %110 %105
+%112 = OpIAdd %uint %83 %uint_6
+%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112
+OpStore %113 %106
+%115 = OpIAdd %uint %83 %uint_7
+%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115
+OpStore %116 %72
+%118 = OpIAdd %uint %83 %uint_8
+%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118
+OpStore %119 %73
+%121 = OpIAdd %uint %83 %uint_9
+%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121
+OpStore %122 %74
+OpBranch %87
+%87 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBuffAddrCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, 2u);
+}
+
+TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
+  // #version 450
+  // #extension GL_EXT_buffer_reference : enable
+
+  // // forward reference
+  // layout(buffer_reference) buffer blockType;
+
+  // layout(buffer_reference, std430, buffer_reference_align = 16) buffer
+  // blockType {
+  //   int x;
+  //   blockType next;
+  // };
+
+  // layout(std430) buffer rootBlock {
+  //   blockType root;
+  // } r;
+
+  // void main()
+  // {
+  //   blockType b = r.root;
+  //   b = b.next;
+  //   b.x = 531;
+  // }
+
+  const std::string defs_before =
+      R"(OpCapability Shader
+OpCapability PhysicalStorageBufferAddressesEXT
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %blockType "blockType"
+OpMemberName %blockType 0 "x"
+OpMemberName %blockType 1 "next"
+OpName %rootBlock "rootBlock"
+OpMemberName %rootBlock 0 "root"
+OpName %r "r"
+OpMemberDecorate %blockType 0 Offset 0
+OpMemberDecorate %blockType 1 Offset 8
+OpDecorate %blockType Block
+OpMemberDecorate %rootBlock 0 Offset 0
+OpDecorate %rootBlock Block
+OpDecorate %r DescriptorSet 0
+OpDecorate %r Binding 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
+%int = OpTypeInt 32 1
+%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
+%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
+%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
+%int_0 = OpConstant %int 0
+%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
+%int_1 = OpConstant %int 1
+%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
+%int_531 = OpConstant %int 531
+%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+)";
+
+  const std::string defs_after =
+      R"(OpCapability Shader
+OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability Int64
+OpCapability Int64
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %blockType "blockType"
+OpMemberName %blockType 0 "x"
+OpMemberName %blockType 1 "next"
+OpName %rootBlock "rootBlock"
+OpMemberName %rootBlock 0 "root"
+OpName %r "r"
+OpMemberDecorate %blockType 0 Offset 0
+OpMemberDecorate %blockType 1 Offset 8
+OpDecorate %blockType Block
+OpMemberDecorate %rootBlock 0 Offset 0
+OpDecorate %rootBlock Block
+OpDecorate %r DescriptorSet 0
+OpDecorate %r Binding 0
+OpDecorate %_runtimearr_ulong ArrayStride 8
+OpDecorate %_struct_45 Block
+OpMemberDecorate %_struct_45 0 Offset 0
+OpDecorate %47 DescriptorSet 7
+OpDecorate %47 Binding 2
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpDecorate %_struct_84 Block
+OpMemberDecorate %_struct_84 0 Offset 0
+OpMemberDecorate %_struct_84 1 Offset 4
+OpDecorate %86 DescriptorSet 7
+OpDecorate %86 Binding 0
+OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT
+%int = OpTypeInt 32 1
+%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType
+%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType
+%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
+%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
+%int_0 = OpConstant %int 0
+%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType
+%int_1 = OpConstant %int 1
+%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType
+%int_531 = OpConstant %int 531
+%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int
+%uint = OpTypeInt 32 0
+%uint_2 = OpConstant %uint 2
+%ulong = OpTypeInt 64 0
+%uint_8 = OpConstant %uint 8
+%bool = OpTypeBool
+%34 = OpTypeFunction %bool %ulong %uint
+%uint_1 = OpConstant %uint 1
+%_runtimearr_ulong = OpTypeRuntimeArray %ulong
+%_struct_45 = OpTypeStruct %_runtimearr_ulong
+%_ptr_StorageBuffer__struct_45 = OpTypePointer StorageBuffer %_struct_45
+%47 = OpVariable %_ptr_StorageBuffer__struct_45 StorageBuffer
+%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
+%uint_0 = OpConstant %uint 0
+%uint_32 = OpConstant %uint 32
+%77 = OpTypeFunction %void %uint %uint %uint %uint
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_84 = OpTypeStruct %uint %_runtimearr_uint
+%_ptr_StorageBuffer__struct_84 = OpTypePointer StorageBuffer %_struct_84
+%86 = OpVariable %_ptr_StorageBuffer__struct_84 StorageBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%uint_10 = OpConstant %uint 10
+%uint_4 = OpConstant %uint 4
+%uint_23 = OpConstant %uint 23
+%uint_5 = OpConstant %uint 5
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_9 = OpConstant %uint 9
+%uint_44 = OpConstant %uint 44
+%132 = OpConstantNull %_ptr_PhysicalStorageBufferEXT_blockType
+%uint_46 = OpConstant %uint 46
+)";
+
+  const std::string func_before =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
+%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
+%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
+%22 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
+%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %22 %int_0
+OpStore %26 %int_531 Aligned 16
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string func_after =
+      R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0
+%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16
+%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1
+%30 = OpConvertPtrToU %ulong %21
+%67 = OpFunctionCall %bool %32 %30 %uint_8
+OpSelectionMerge %68 None
+OpBranchConditional %67 %69 %70
+%69 = OpLabel
+%71 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8
+OpBranch %68
+%70 = OpLabel
+%72 = OpUConvert %uint %30
+%74 = OpShiftRightLogical %ulong %30 %uint_32
+%75 = OpUConvert %uint %74
+%131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75
+OpBranch %68
+%68 = OpLabel
+%133 = OpPhi %_ptr_PhysicalStorageBufferEXT_blockType %71 %69 %132 %70
+%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %133 %int_0
+%134 = OpConvertPtrToU %ulong %26
+%135 = OpFunctionCall %bool %32 %134 %uint_4
+OpSelectionMerge %136 None
+OpBranchConditional %135 %137 %138
+%137 = OpLabel
+OpStore %26 %int_531 Aligned 16
+OpBranch %136
+%138 = OpLabel
+%139 = OpUConvert %uint %134
+%140 = OpShiftRightLogical %ulong %134 %uint_32
+%141 = OpUConvert %uint %140
+%143 = OpFunctionCall %void %76 %uint_46 %uint_2 %139 %141
+OpBranch %136
+%136 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string new_funcs =
+      R"(%32 = OpFunction %bool None %34
+%35 = OpFunctionParameter %ulong
+%36 = OpFunctionParameter %uint
+%37 = OpLabel
+OpBranch %38
+%38 = OpLabel
+%40 = OpPhi %uint %uint_1 %37 %41 %39
+OpLoopMerge %43 %39 None
+OpBranch %39
+%39 = OpLabel
+%41 = OpIAdd %uint %40 %uint_1
+%50 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %41
+%51 = OpLoad %ulong %50
+%52 = OpUGreaterThan %bool %51 %35
+OpBranchConditional %52 %43 %38
+%43 = OpLabel
+%53 = OpISub %uint %41 %uint_1
+%54 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %53
+%55 = OpLoad %ulong %54
+%56 = OpISub %ulong %35 %55
+%57 = OpUConvert %ulong %36
+%58 = OpIAdd %ulong %56 %57
+%59 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %uint_0
+%60 = OpLoad %ulong %59
+%61 = OpUConvert %uint %60
+%62 = OpISub %uint %53 %uint_1
+%63 = OpIAdd %uint %62 %61
+%64 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %63
+%65 = OpLoad %ulong %64
+%66 = OpULessThanEqual %bool %58 %65
+OpReturnValue %66
+OpFunctionEnd
+%76 = OpFunction %void None %77
+%78 = OpFunctionParameter %uint
+%79 = OpFunctionParameter %uint
+%80 = OpFunctionParameter %uint
+%81 = OpFunctionParameter %uint
+%82 = OpLabel
+%88 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_0
+%91 = OpAtomicIAdd %uint %88 %uint_4 %uint_0 %uint_10
+%92 = OpIAdd %uint %91 %uint_10
+%93 = OpArrayLength %uint %86 1
+%94 = OpULessThanEqual %bool %92 %93
+OpSelectionMerge %95 None
+OpBranchConditional %94 %96 %95
+%96 = OpLabel
+%97 = OpIAdd %uint %91 %uint_0
+%98 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %97
+OpStore %98 %uint_10
+%100 = OpIAdd %uint %91 %uint_1
+%101 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %100
+OpStore %101 %uint_23
+%102 = OpIAdd %uint %91 %uint_2
+%103 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %102
+OpStore %103 %78
+%106 = OpIAdd %uint %91 %uint_3
+%107 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %106
+OpStore %107 %uint_5
+%111 = OpLoad %v3uint %gl_GlobalInvocationID
+%112 = OpCompositeExtract %uint %111 0
+%113 = OpCompositeExtract %uint %111 1
+%114 = OpCompositeExtract %uint %111 2
+%115 = OpIAdd %uint %91 %uint_4
+%116 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %115
+OpStore %116 %112
+%117 = OpIAdd %uint %91 %uint_5
+%118 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %117
+OpStore %118 %113
+%120 = OpIAdd %uint %91 %uint_6
+%121 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %120
+OpStore %121 %114
+%123 = OpIAdd %uint %91 %uint_7
+%124 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %123
+OpStore %124 %79
+%125 = OpIAdd %uint %91 %uint_8
+%126 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %125
+OpStore %126 %80
+%128 = OpIAdd %uint %91 %uint_9
+%129 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %128
+OpStore %129 %81
+OpBranch %95
+%95 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndCheck<InstBuffAddrCheckPass>(
+      defs_before + func_before, defs_after + func_after + new_funcs, true,
+      true, 7u, 23u, 2u);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools