instrument: Combine descriptor length and init state checking (#5274)

Simplify what we add to user code by moving most of it into a function
that checks both that the descriptor index is in bounds and the
initialization state. Move error logging into this function as
well.

Remove many options to turn off parts of the instrumentation,
because there were far too many permutations to keep working and
test properly.

Combine Buffer and TexBuffer error checking. This requires that VVL
set the length of TexBuffers in the descriptor input state, rather
than relying on the instrumentation code to call OpImageQuerySize.
Since the error log includes the descriptor set and binding numbers
we can use a single OOB error code rather than having 4 per-type
error codes.

Since the error codes are getting renumbered, make them start at 1
rather than 0 so it is easier to determine if the error code was
actually set by the instrumentation.
This commit is contained in:
Jeremy Gebben 2023-06-22 09:39:49 -06:00 committed by GitHub
parent a68ef7b2c5
commit daee1e7d34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1442 additions and 2218 deletions

View File

@ -182,18 +182,11 @@ static const int kInstMaxOutCnt = kInstStageOutCnt + 6;
// Validation Error Codes
//
// These are the possible validation error codes.
static const int kInstErrorBindlessBounds = 0;
static const int kInstErrorBindlessUninit = 1;
static const int kInstErrorBuffAddrUnallocRef = 2;
// Deleted: static const int kInstErrorBindlessBuffOOB = 3;
// This comment will will remain for 2 releases to allow
// for the transition of all builds. Buffer OOB is
// generating the following four differentiated codes instead:
static const int kInstErrorBuffOOBUniform = 4;
static const int kInstErrorBuffOOBStorage = 5;
static const int kInstErrorBuffOOBUniformTexel = 6;
static const int kInstErrorBuffOOBStorageTexel = 7;
static const int kInstErrorMax = kInstErrorBuffOOBStorageTexel;
static const int kInstErrorBindlessBounds = 1;
static const int kInstErrorBindlessUninit = 2;
static const int kInstErrorBuffAddrUnallocRef = 3;
static const int kInstErrorOOB = 4;
static const int kInstErrorMax = kInstErrorOOB;
// Direct Input Buffer Offsets
//

View File

@ -769,16 +769,8 @@ Optimizer::PassToken CreateCombineAccessChainsPass();
// 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.
// |desc_length_enable| controls instrumentation of runtime descriptor array
// references, |desc_init_enable| controls instrumentation of descriptor
// initialization checking, and |buff_oob_enable| controls instrumentation
// of storage and uniform buffer bounds checking, all of which require input
// buffer support. |texbuff_oob_enable| controls instrumentation of texel
// buffers, which does not require input buffer support.
Optimizer::PassToken CreateInstBindlessCheckPass(
uint32_t desc_set, uint32_t shader_id, bool desc_length_enable = false,
bool desc_init_enable = false, bool buff_oob_enable = false,
bool texbuff_oob_enable = false);
Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
uint32_t shader_id);
// Create a pass to instrument physical buffer address checking
// This pass instruments all physical buffer address references to check that

View File

@ -31,15 +31,12 @@ constexpr int kSpvLoadPtrIdInIdx = 0;
constexpr int kSpvAccessChainBaseIdInIdx = 0;
constexpr int kSpvAccessChainIndex0IdInIdx = 1;
constexpr int kSpvTypeArrayTypeIdInIdx = 0;
constexpr int kSpvTypeArrayLengthIdInIdx = 1;
constexpr int kSpvConstantValueInIdx = 0;
constexpr int kSpvVariableStorageClassInIdx = 0;
constexpr int kSpvTypePtrTypeIdInIdx = 1;
constexpr int kSpvTypeImageDim = 1;
constexpr int kSpvTypeImageDepth = 2;
constexpr int kSpvTypeImageArrayed = 3;
constexpr int kSpvTypeImageMS = 4;
constexpr int kSpvTypeImageSampled = 5;
} // namespace
void InstBindlessCheckPass::SetupInputBufferIds() {
@ -135,228 +132,76 @@ void InstBindlessCheckPass::SetupInputBufferIds() {
// clang-format off
// GLSL:
// uint inst_bindless_read_binding_length(uint desc_set_idx, uint binding_idx)
// {
// if (desc_set_idx >= inst_bindless_input_buffer.desc_sets.length()) {
// return 0;
// }
//
// DescriptorSetData set_data = inst_bindless_input_buffer.desc_sets[desc_set_idx];
// uvec2 ptr_as_vec = uvec2(set_data);
// if ((ptr_as_vec.x == 0u) && (_ptr_as_vec.y == 0u))
// {
// return 0u;
// }
// uint num_bindings = set_data.num_bindings;
// if (binding_idx >= num_bindings) {
// return 0;
// }
// return set_data.data[binding_idx];
// }
//bool inst_bindless_check_desc(uint shader_id, uint line, uvec4 stage_info, uint desc_set_idx, uint binding_idx, uint desc_idx,
// uint offset)
//{
// if (desc_set_idx >= inst_bindless_input_buffer.desc_sets.length()) {
// // kInstErrorBindlessBounds
// inst_bindless_stream_write_6(shader_id, line, stage_info, 1, desc_set_idx, binding_idx, desc_idx, 0, 0);
// return false;
// }
// DescriptorSetData set_data = inst_bindless_input_buffer.desc_sets[desc_set_idx];
// uvec2 ptr_vec = uvec2(set_data);
// if (ptr_vec.x == 0 && ptr_vec.y == 0) {
// // kInstErrorBindlessBounds
// inst_bindless_stream_write_6(shader_id, line, stage_info, 1, desc_set_idx, binding_idx, desc_idx, 0, 0);
// return false;
// }
// uint num_bindings = set_data.num_bindings;
// if (binding_idx >= num_bindings) {
// // kInstErrorBindlessBounds
// inst_bindless_stream_write_6(shader_id, line, stage_info, 1, desc_set_idx, binding_idx, desc_idx, 0, 0);
// return false;
// }
// uint binding_length = set_data.data[binding_idx];
// if (desc_idx >= binding_length) {
// // kInstErrorBindlessBounds
// inst_bindless_stream_write_6(shader_id, line, stage_info, 1, desc_set_idx, binding_idx, desc_idx, binding_length, 0);
// return false;
// }
// uint desc_records_start = set_data.data[num_bindings + binding_idx];
// uint init_or_len = set_data.data[desc_records_start + desc_idx];
// if (init_or_len == 0) {
// // kInstErrorBindlessUninit
// inst_bindless_stream_write_6(shader_id, line, stage_info, 2, desc_set_idx, binding_idx, desc_idx, 0, 0);
// return false;
// }
// if (offset >= init_or_len) {
// // kInstErrorOOB
// inst_bindless_stream_write_6(shader_id, line, stage_info, 4, desc_set_idx, binding_idx, desc_idx, offset,
// init_or_len);
// return false;
// }
// return true;
//}
// clang-format on
uint32_t InstBindlessCheckPass::GenDebugReadLengthFunctionId() {
if (read_length_func_id_ != 0) {
return read_length_func_id_;
}
SetupInputBufferIds();
const analysis::Integer* uint_type = GetInteger(32, false);
const std::vector<const analysis::Type*> param_types(2, uint_type);
const uint32_t func_id = TakeNextId();
std::unique_ptr<Function> func =
StartFunction(func_id, uint_type, param_types);
const std::vector<uint32_t> param_ids = AddParameters(*func, param_types);
// Create block
auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId()));
InstructionBuilder builder(
context(), new_blk_ptr.get(),
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
Instruction* inst;
inst = builder.AddBinaryOp(
GetBoolId(), spv::Op::OpUGreaterThanEqual, param_ids[0],
builder.GetUintConstantId(kDebugInputBindlessMaxDescSets));
const uint32_t desc_cmp_id = inst->result_id();
uint32_t error_blk_id = TakeNextId();
uint32_t merge_blk_id = TakeNextId();
std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
std::unique_ptr<Instruction> error_label(NewLabel(error_blk_id));
(void)builder.AddConditionalBranch(desc_cmp_id, error_blk_id, merge_blk_id,
merge_blk_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// error return
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
builder.SetInsertPoint(&*new_blk_ptr);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue,
builder.GetUintConstantId(0));
func->AddBasicBlock(std::move(new_blk_ptr));
// check descriptor set table entry is non-null
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
builder.SetInsertPoint(&*new_blk_ptr);
analysis::TypeManager* type_mgr = context()->get_type_mgr();
const uint32_t desc_set_ptr_ptr = type_mgr->FindPointerToType(
desc_set_ptr_id_, spv::StorageClass::StorageBuffer);
inst = builder.AddAccessChain(desc_set_ptr_ptr, input_buffer_id_,
{builder.GetUintConstantId(0), param_ids[0]});
const uint32_t set_access_chain_id = inst->result_id();
inst = builder.AddLoad(desc_set_ptr_id_, set_access_chain_id);
const uint32_t desc_set_ptr_id = inst->result_id();
inst =
builder.AddUnaryOp(GetVecUintId(2), spv::Op::OpBitcast, desc_set_ptr_id);
const uint32_t ptr_as_uvec_id = inst->result_id();
inst = builder.AddCompositeExtract(GetUintId(), ptr_as_uvec_id, {0});
const uint32_t uvec_x = inst->result_id();
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, uvec_x,
builder.GetUintConstantId(0));
const uint32_t x_is_zero_id = inst->result_id();
inst = builder.AddCompositeExtract(GetUintId(), ptr_as_uvec_id, {1});
const uint32_t uvec_y = inst->result_id();
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, uvec_y,
builder.GetUintConstantId(0));
const uint32_t y_is_zero_id = inst->result_id();
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpLogicalAnd, x_is_zero_id,
y_is_zero_id);
const uint32_t is_null_id = inst->result_id();
error_blk_id = TakeNextId();
merge_blk_id = TakeNextId();
merge_label = NewLabel(merge_blk_id);
error_label = NewLabel(error_blk_id);
(void)builder.AddConditionalBranch(is_null_id, error_blk_id, merge_blk_id,
merge_blk_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// error return
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
builder.SetInsertPoint(&*new_blk_ptr);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue,
builder.GetUintConstantId(0));
func->AddBasicBlock(std::move(new_blk_ptr));
// check binding is in range
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
builder.SetInsertPoint(&*new_blk_ptr);
const uint32_t uint_ptr = type_mgr->FindPointerToType(
GetUintId(), spv::StorageClass::PhysicalStorageBuffer);
inst = builder.AddAccessChain(uint_ptr, desc_set_ptr_id,
{builder.GetUintConstantId(0)});
const uint32_t binding_access_chain_id = inst->result_id();
inst = builder.AddLoad(GetUintId(), binding_access_chain_id, 8);
const uint32_t num_bindings_id = inst->result_id();
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual,
param_ids[1], num_bindings_id);
const uint32_t bindings_cmp_id = inst->result_id();
error_blk_id = TakeNextId();
merge_blk_id = TakeNextId();
merge_label = NewLabel(merge_blk_id);
error_label = NewLabel(error_blk_id);
(void)builder.AddConditionalBranch(bindings_cmp_id, error_blk_id,
merge_blk_id, merge_blk_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// error return
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
builder.SetInsertPoint(&*new_blk_ptr);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue,
builder.GetUintConstantId(0));
func->AddBasicBlock(std::move(new_blk_ptr));
// read binding length
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
builder.SetInsertPoint(&*new_blk_ptr);
inst = builder.AddAccessChain(uint_ptr, desc_set_ptr_id,
{{builder.GetUintConstantId(1), param_ids[1]}});
const uint32_t length_ac_id = inst->result_id();
inst = builder.AddLoad(GetUintId(), length_ac_id, sizeof(uint32_t));
const uint32_t length_id = inst->result_id();
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, length_id);
func->AddBasicBlock(std::move(new_blk_ptr));
func->SetFunctionEnd(EndFunction());
context()->AddFunction(std::move(func));
context()->AddDebug2Inst(NewGlobalName(func_id, "read_binding_length"));
read_length_func_id_ = func_id;
// Make sure this function doesn't get processed by
// InstrumentPass::InstProcessCallTreeFromRoots()
param2output_func_id_[2] = func_id;
return read_length_func_id_;
}
// clang-format off
// GLSL:
// result = inst_bindless_read_binding_length(desc_set_id, binding_id);
// clang-format on
uint32_t InstBindlessCheckPass::GenDebugReadLength(
uint32_t var_id, InstructionBuilder* builder) {
const uint32_t func_id = GenDebugReadLengthFunctionId();
const std::vector<uint32_t> args = {
builder->GetUintConstantId(var2desc_set_[var_id]),
builder->GetUintConstantId(var2binding_[var_id]),
uint32_t InstBindlessCheckPass::GenDescCheckFunctionId() {
enum {
kShaderId = 0,
kInstructionIndex = 1,
kStageInfo = 2,
kDescSet = 3,
kDescBinding = 4,
kDescIndex = 5,
kByteOffset = 6,
kNumArgs
};
return GenReadFunctionCall(func_id, args, builder);
}
// clang-format off
// GLSL:
// uint inst_bindless_read_desc_init(uint desc_set_idx, uint binding_idx, uint desc_idx)
// {
// if (desc_set_idx >= uint(inst_bindless_input_buffer.desc_sets.length()))
// {
// return 0u;
// }
// DescriptorSetData set_data = inst_bindless_input_buffer.desc_sets[desc_set_idx];
// uvec2 ptr_as_vec = uvec2(set_data)
// if ((ptr_as_vec .x == 0u) && (ptr_as_vec.y == 0u))
// {
// return 0u;
// }
// if (binding_idx >= set_data.num_bindings)
// {
// return 0u;
// }
// if (desc_idx >= set_data.data[binding_idx])
// {
// return 0u;
// }
// uint desc_records_start = set_data.data[set_data.num_bindings + binding_idx];
// return set_data.data[desc_records_start + desc_idx];
// }
// clang-format on
uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
if (read_init_func_id_ != 0) {
return read_init_func_id_;
if (desc_check_func_id_ != 0) {
return desc_check_func_id_;
}
SetupInputBufferIds();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
const analysis::Integer* uint_type = GetInteger(32, false);
const std::vector<const analysis::Type*> param_types(3, uint_type);
const analysis::Vector v4uint(uint_type, 4);
const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint);
std::vector<const analysis::Type*> param_types(kNumArgs, uint_type);
param_types[2] = v4uint_type;
const uint32_t func_id = TakeNextId();
std::unique_ptr<Function> func =
StartFunction(func_id, uint_type, param_types);
StartFunction(func_id, type_mgr->GetBoolType(), param_types);
const std::vector<uint32_t> param_ids = AddParameters(*func, param_types);
@ -365,10 +210,12 @@ uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
InstructionBuilder builder(
context(), new_blk_ptr.get(),
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
const uint32_t false_id = builder.GetBoolConstantId(false);
const uint32_t true_id = builder.GetBoolConstantId(true);
Instruction* inst;
inst = builder.AddBinaryOp(
GetBoolId(), spv::Op::OpUGreaterThanEqual, param_ids[0],
GetBoolId(), spv::Op::OpUGreaterThanEqual, param_ids[kDescSet],
builder.GetUintConstantId(kDebugInputBindlessMaxDescSets));
const uint32_t desc_cmp_id = inst->result_id();
@ -383,20 +230,19 @@ uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
// error return
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
builder.SetInsertPoint(&*new_blk_ptr);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue,
builder.GetUintConstantId(0));
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, false_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// check descriptor set table entry is non-null
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
builder.SetInsertPoint(&*new_blk_ptr);
analysis::TypeManager* type_mgr = context()->get_type_mgr();
const uint32_t desc_set_ptr_ptr = type_mgr->FindPointerToType(
desc_set_ptr_id_, spv::StorageClass::StorageBuffer);
inst = builder.AddAccessChain(desc_set_ptr_ptr, input_buffer_id_,
{builder.GetUintConstantId(0), param_ids[0]});
inst = builder.AddAccessChain(
desc_set_ptr_ptr, input_buffer_id_,
{builder.GetUintConstantId(0), param_ids[kDescSet]});
const uint32_t set_access_chain_id = inst->result_id();
inst = builder.AddLoad(desc_set_ptr_id_, set_access_chain_id);
@ -434,8 +280,13 @@ uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
// error return
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
builder.SetInsertPoint(&*new_blk_ptr);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue,
builder.GetUintConstantId(0));
GenDebugStreamWrite(
param_ids[kShaderId], param_ids[kInstructionIndex], param_ids[kStageInfo],
{builder.GetUintConstantId(kInstErrorBindlessBounds), param_ids[kDescSet],
param_ids[kDescBinding], param_ids[kDescIndex],
builder.GetUintConstantId(0), builder.GetUintConstantId(0)},
&builder);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, false_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// check binding is in range
@ -453,7 +304,7 @@ uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
const uint32_t num_bindings_id = inst->result_id();
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual,
param_ids[1], num_bindings_id);
param_ids[kDescBinding], num_bindings_id);
const uint32_t bindings_cmp_id = inst->result_id();
error_blk_id = TakeNextId();
@ -466,16 +317,22 @@ uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
// error return
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
builder.SetInsertPoint(&*new_blk_ptr);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue,
builder.GetUintConstantId(0));
GenDebugStreamWrite(
param_ids[kShaderId], param_ids[kInstructionIndex], param_ids[kStageInfo],
{builder.GetUintConstantId(kInstErrorBindlessBounds), param_ids[kDescSet],
param_ids[kDescBinding], param_ids[kDescIndex],
builder.GetUintConstantId(0), builder.GetUintConstantId(0)},
&builder);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, false_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// read binding length
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
builder.SetInsertPoint(&*new_blk_ptr);
inst = builder.AddAccessChain(uint_ptr, desc_set_ptr_id,
{{builder.GetUintConstantId(1), param_ids[1]}});
inst = builder.AddAccessChain(
uint_ptr, desc_set_ptr_id,
{{builder.GetUintConstantId(1), param_ids[kDescBinding]}});
const uint32_t length_ac_id = inst->result_id();
inst = builder.AddLoad(GetUintId(), length_ac_id, sizeof(uint32_t));
@ -483,7 +340,7 @@ uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
// Check descriptor index in bounds
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual,
param_ids[2], length_id);
param_ids[kDescIndex], length_id);
const uint32_t desc_idx_range_id = inst->result_id();
error_blk_id = TakeNextId();
@ -496,15 +353,20 @@ uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
// Error return
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
builder.SetInsertPoint(&*new_blk_ptr);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue,
builder.GetUintConstantId(0));
GenDebugStreamWrite(
param_ids[kShaderId], param_ids[kInstructionIndex], param_ids[kStageInfo],
{builder.GetUintConstantId(kInstErrorBindlessBounds), param_ids[kDescSet],
param_ids[kDescBinding], param_ids[kDescIndex], length_id,
builder.GetUintConstantId(0)},
&builder);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, false_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// Read descriptor init status
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
builder.SetInsertPoint(&*new_blk_ptr);
inst = builder.AddIAdd(GetUintId(), num_bindings_id, param_ids[1]);
inst = builder.AddIAdd(GetUintId(), num_bindings_id, param_ids[kDescBinding]);
const uint32_t state_offset_id = inst->result_id();
inst =
@ -515,7 +377,7 @@ uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
inst = builder.AddLoad(GetUintId(), state_start_ac_id, sizeof(uint32_t));
const uint32_t state_start_id = inst->result_id();
inst = builder.AddIAdd(GetUintId(), state_start_id, param_ids[2]);
inst = builder.AddIAdd(GetUintId(), state_start_id, param_ids[kDescIndex]);
const uint32_t state_entry_id = inst->result_id();
// Note: length starts from the beginning of the buffer, not the beginning of
@ -528,35 +390,90 @@ uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() {
inst = builder.AddLoad(GetUintId(), init_ac_id, sizeof(uint32_t));
const uint32_t init_status_id = inst->result_id();
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, init_status_id);
// Check for uninitialized descriptor
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, init_status_id,
builder.GetUintConstantId(0));
const uint32_t uninit_check_id = inst->result_id();
error_blk_id = TakeNextId();
merge_blk_id = TakeNextId();
merge_label = NewLabel(merge_blk_id);
error_label = NewLabel(error_blk_id);
(void)builder.AddConditionalBranch(uninit_check_id, error_blk_id,
merge_blk_id, merge_blk_id);
func->AddBasicBlock(std::move(new_blk_ptr));
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
builder.SetInsertPoint(&*new_blk_ptr);
GenDebugStreamWrite(
param_ids[kShaderId], param_ids[kInstructionIndex], param_ids[kStageInfo],
{builder.GetUintConstantId(kInstErrorBindlessUninit), param_ids[kDescSet],
param_ids[kDescBinding], param_ids[kDescIndex],
builder.GetUintConstantId(0), builder.GetUintConstantId(0)},
&builder);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, false_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// Check for OOB.
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
builder.SetInsertPoint(&*new_blk_ptr);
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual,
param_ids[kByteOffset], init_status_id);
const uint32_t buf_offset_range_id = inst->result_id();
error_blk_id = TakeNextId();
merge_blk_id = TakeNextId();
merge_label = NewLabel(merge_blk_id);
error_label = NewLabel(error_blk_id);
(void)builder.AddConditionalBranch(buf_offset_range_id, error_blk_id,
merge_blk_id, merge_blk_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// Error return
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
builder.SetInsertPoint(&*new_blk_ptr);
GenDebugStreamWrite(
param_ids[kShaderId], param_ids[kInstructionIndex], param_ids[kStageInfo],
{builder.GetUintConstantId(kInstErrorOOB), param_ids[kDescSet],
param_ids[kDescBinding], param_ids[kDescIndex], param_ids[kByteOffset],
init_status_id},
&builder);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, false_id);
func->AddBasicBlock(std::move(new_blk_ptr));
// Success return
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
builder.SetInsertPoint(&*new_blk_ptr);
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, true_id);
func->AddBasicBlock(std::move(new_blk_ptr));
func->SetFunctionEnd(EndFunction());
context()->AddFunction(std::move(func));
context()->AddDebug2Inst(NewGlobalName(func_id, "read_desc_init"));
context()->AddDebug2Inst(NewGlobalName(func_id, "desc_check"));
read_init_func_id_ = func_id;
desc_check_func_id_ = func_id;
// Make sure function doesn't get processed by
// InstrumentPass::InstProcessCallTreeFromRoots()
param2output_func_id_[3] = func_id;
return read_init_func_id_;
return desc_check_func_id_;
}
// clang-format off
// GLSL:
// result = inst_bindless_read_desc_init(desc_set_id, binding_id, desc_idx_id);
// result = inst_bindless_desc_check(shader_id, inst_idx, stage_info, desc_set, binding, desc_idx, offset);
//
// clang-format on
uint32_t InstBindlessCheckPass::GenDebugReadInit(uint32_t var_id,
uint32_t desc_idx_id,
InstructionBuilder* builder) {
const uint32_t func_id = GenDebugReadInitFunctionId();
uint32_t InstBindlessCheckPass::GenDescCheckCall(
uint32_t inst_idx, uint32_t stage_idx, uint32_t var_id,
uint32_t desc_idx_id, uint32_t offset_id, InstructionBuilder* builder) {
const uint32_t func_id = GenDescCheckFunctionId();
const std::vector<uint32_t> args = {
builder->GetUintConstantId(shader_id_),
builder->GetUintConstantId(inst_idx),
GenStageInfo(stage_idx, builder),
builder->GetUintConstantId(var2desc_set_[var_id]),
builder->GetUintConstantId(var2binding_[var_id]),
GenUintCastCode(desc_idx_id, builder)};
return GenReadFunctionCall(func_id, args, builder);
GenUintCastCode(desc_idx_id, builder),
offset_id};
return GenReadFunctionCall(GetBoolId(), func_id, args, builder);
}
uint32_t InstBindlessCheckPass::CloneOriginalImage(
@ -1047,29 +964,30 @@ void InstBindlessCheckPass::GenCheckCode(
// Gen invalid block
new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
builder.SetInsertPoint(&*new_blk_ptr);
const uint32_t u_set_id = builder.GetUintConstantId(ref->set);
const uint32_t u_binding_id = builder.GetUintConstantId(ref->binding);
const uint32_t u_index_id = GenUintCastCode(ref->desc_idx_id, &builder);
const uint32_t u_length_id = GenUintCastCode(length_id, &builder);
if (offset_id != 0) {
const uint32_t u_offset_id = GenUintCastCode(offset_id, &builder);
// Buffer OOB
GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx,
{error_id, u_set_id, u_binding_id, u_index_id,
u_offset_id, u_length_id},
&builder);
} else if (buffer_bounds_enabled_ || texel_buffer_enabled_) {
// Uninitialized Descriptor - Return additional unused zero so all error
// modes will use same debug stream write function
GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx,
{error_id, u_set_id, u_binding_id, u_index_id,
u_length_id, builder.GetUintConstantId(0)},
&builder);
} else {
// Uninitialized Descriptor - Normal error return
GenDebugStreamWrite(
uid2offset_[ref->ref_inst->unique_id()], stage_idx,
{error_id, u_set_id, u_binding_id, u_index_id, u_length_id}, &builder);
if (error_id != 0) {
const uint32_t u_shader_id = builder.GetUintConstantId(shader_id_);
const uint32_t u_inst_id =
builder.GetUintConstantId(ref->ref_inst->unique_id());
const uint32_t shader_info_id = GenStageInfo(stage_idx, &builder);
const uint32_t u_set_id = builder.GetUintConstantId(ref->set);
const uint32_t u_binding_id = builder.GetUintConstantId(ref->binding);
const uint32_t u_index_id = GenUintCastCode(ref->desc_idx_id, &builder);
const uint32_t u_length_id = GenUintCastCode(length_id, &builder);
if (offset_id != 0) {
const uint32_t u_offset_id = GenUintCastCode(offset_id, &builder);
// Buffer OOB
GenDebugStreamWrite(u_shader_id, u_inst_id, shader_info_id,
{error_id, u_set_id, u_binding_id, u_index_id,
u_offset_id, u_length_id},
&builder);
} else {
// Uninitialized Descriptor - Return additional unused zero so all error
// modes will use same debug stream write function
GenDebugStreamWrite(u_shader_id, u_inst_id, shader_info_id,
{error_id, u_set_id, u_binding_id, u_index_id,
u_length_id, builder.GetUintConstantId(0)},
&builder);
}
}
// Generate a ConstantNull, converting to uint64 if the type cannot be a null.
if (new_ref_id != 0) {
@ -1106,77 +1024,42 @@ void InstBindlessCheckPass::GenCheckCode(
context()->KillInst(ref->ref_inst);
}
void InstBindlessCheckPass::GenDescIdxCheckCode(
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.
RefAnalysis ref;
if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) return;
Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref.ptr_id);
if (ptr_inst->opcode() != spv::Op::OpAccessChain) return;
// If index and bound both compile-time constants and index < bound,
// return without changing
Instruction* var_inst = get_def_use_mgr()->GetDef(ref.var_id);
Instruction* desc_type_inst = GetPointeeTypeInst(var_inst);
uint32_t length_id = 0;
if (desc_type_inst->opcode() == spv::Op::OpTypeArray) {
length_id =
desc_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx);
Instruction* index_inst = get_def_use_mgr()->GetDef(ref.desc_idx_id);
Instruction* length_inst = get_def_use_mgr()->GetDef(length_id);
if (index_inst->opcode() == spv::Op::OpConstant &&
length_inst->opcode() == spv::Op::OpConstant &&
index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) <
length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx))
return;
} else if (!desc_idx_enabled_ ||
desc_type_inst->opcode() != spv::Op::OpTypeRuntimeArray) {
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(kInstErrorBindlessBounds);
// If length id not yet set, descriptor array is runtime size so
// generate load of length from stage's debug input buffer.
if (length_id == 0) {
assert(desc_type_inst->opcode() == spv::Op::OpTypeRuntimeArray &&
"unexpected bindless type");
length_id = GenDebugReadLength(ref.var_id, &builder);
}
// Generate full runtime bounds test code with true branch
// being full reference and false branch being debug output and zero
// for the referenced value.
uint32_t desc_idx_32b_id = Gen32BitCvtCode(ref.desc_idx_id, &builder);
uint32_t length_32b_id = Gen32BitCvtCode(length_id, &builder);
Instruction* ult_inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpULessThan,
desc_idx_32b_id, length_32b_id);
ref.desc_idx_id = desc_idx_32b_id;
GenCheckCode(ult_inst->result_id(), error_id, 0u, length_id, stage_idx, &ref,
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 InstBindlessCheckPass::GenDescInitCheckCode(
void InstBindlessCheckPass::GenDescCheckCode(
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 descriptor. If not, return.
RefAnalysis ref;
if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) return;
std::unique_ptr<BasicBlock> new_blk_ptr;
// Move original block's preceding instructions into first new block
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));
// Determine if we can only do initialization check
bool init_check = false;
if (ref.desc_load_id != 0 || !buffer_bounds_enabled_) {
init_check = true;
uint32_t ref_id = builder.GetUintConstantId(0u);
spv::Op op = ref.ref_inst->opcode();
if (ref.desc_load_id != 0) {
uint32_t num_in_oprnds = ref.ref_inst->NumInOperands();
if ((op == spv::Op::OpImageRead && num_in_oprnds == 2) ||
(op == spv::Op::OpImageFetch && num_in_oprnds == 2) ||
(op == spv::Op::OpImageWrite && num_in_oprnds == 3)) {
Instruction* image_inst = get_def_use_mgr()->GetDef(ref.image_id);
uint32_t image_ty_id = image_inst->type_id();
Instruction* image_ty_inst = get_def_use_mgr()->GetDef(image_ty_id);
if (spv::Dim(image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDim)) ==
spv::Dim::Buffer) {
if ((image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDepth) == 0) &&
(image_ty_inst->GetSingleWordInOperand(kSpvTypeImageArrayed) ==
0) &&
(image_ty_inst->GetSingleWordInOperand(kSpvTypeImageMS) == 0)) {
ref_id = GenUintCastCode(ref.ref_inst->GetSingleWordInOperand(1),
&builder);
}
}
}
} else {
// For now, only do bounds check for non-aggregate types. Otherwise
// just do descriptor initialization check.
@ -1184,106 +1067,24 @@ void InstBindlessCheckPass::GenDescInitCheckCode(
Instruction* ref_ptr_inst = get_def_use_mgr()->GetDef(ref.ptr_id);
Instruction* pte_type_inst = GetPointeeTypeInst(ref_ptr_inst);
spv::Op pte_type_op = pte_type_inst->opcode();
if (pte_type_op == spv::Op::OpTypeArray ||
pte_type_op == spv::Op::OpTypeRuntimeArray ||
pte_type_op == spv::Op::OpTypeStruct)
init_check = true;
if (pte_type_op != spv::Op::OpTypeArray &&
pte_type_op != spv::Op::OpTypeRuntimeArray &&
pte_type_op != spv::Op::OpTypeStruct) {
ref_id = GenLastByteIdx(&ref, &builder);
}
}
// If initialization check and not enabled, return
if (init_check && !desc_init_enabled_) 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));
// If initialization check, use reference value of zero.
// Else use the index of the last byte referenced.
uint32_t ref_id = init_check ? builder.GetUintConstantId(0u)
: GenLastByteIdx(&ref, &builder);
// Read initialization/bounds from debug input buffer. If index id not yet
// set, binding is single descriptor, so set index to constant 0.
if (ref.desc_idx_id == 0) ref.desc_idx_id = builder.GetUintConstantId(0u);
uint32_t init_id = GenDebugReadInit(ref.var_id, ref.desc_idx_id, &builder);
// Generate runtime initialization/bounds test code with true branch
// being full reference and false branch being debug output and zero
// for the referenced value.
Instruction* ult_inst =
builder.AddBinaryOp(GetBoolId(), spv::Op::OpULessThan, ref_id, init_id);
uint32_t error =
init_check
? kInstErrorBindlessUninit
: (spv::StorageClass(ref.strg_class) == spv::StorageClass::Uniform
? kInstErrorBuffOOBUniform
: kInstErrorBuffOOBStorage);
uint32_t error_id = builder.GetUintConstantId(error);
GenCheckCode(ult_inst->result_id(), error_id, init_check ? 0 : ref_id,
init_check ? builder.GetUintConstantId(0u) : init_id, stage_idx,
&ref, 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);
}
uint32_t check_id =
GenDescCheckCall(ref.ref_inst->unique_id(), stage_idx, ref.var_id,
ref.desc_idx_id, ref_id, &builder);
void InstBindlessCheckPass::GenTexBuffCheckCode(
BasicBlock::iterator ref_inst_itr,
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
// Only process OpImageRead and OpImageWrite with no optional operands
Instruction* ref_inst = &*ref_inst_itr;
spv::Op op = ref_inst->opcode();
uint32_t num_in_oprnds = ref_inst->NumInOperands();
if (!((op == spv::Op::OpImageRead && num_in_oprnds == 2) ||
(op == spv::Op::OpImageFetch && num_in_oprnds == 2) ||
(op == spv::Op::OpImageWrite && num_in_oprnds == 3)))
return;
// Pull components from descriptor reference
RefAnalysis ref;
if (!AnalyzeDescriptorReference(ref_inst, &ref)) return;
// Only process if image is texel buffer
Instruction* image_inst = get_def_use_mgr()->GetDef(ref.image_id);
uint32_t image_ty_id = image_inst->type_id();
Instruction* image_ty_inst = get_def_use_mgr()->GetDef(image_ty_id);
if (spv::Dim(image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDim)) !=
spv::Dim::Buffer) {
return;
}
if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDepth) != 0) return;
if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageArrayed) != 0) return;
if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageMS) != 0) return;
// Enable ImageQuery Capability if not yet enabled
context()->AddCapability(spv::Capability::ImageQuery);
// 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));
// Get texel coordinate
uint32_t coord_id =
GenUintCastCode(ref_inst->GetSingleWordInOperand(1), &builder);
// If index id not yet set, binding is single descriptor, so set index to
// constant 0.
if (ref.desc_idx_id == 0) ref.desc_idx_id = builder.GetUintConstantId(0u);
// Get texel buffer size.
Instruction* size_inst =
builder.AddUnaryOp(GetUintId(), spv::Op::OpImageQuerySize, ref.image_id);
uint32_t size_id = size_inst->result_id();
// Generate runtime initialization/bounds test code with true branch
// being full reference and false branch being debug output and zero
// being full reference and false branch being zero
// for the referenced value.
Instruction* ult_inst =
builder.AddBinaryOp(GetBoolId(), spv::Op::OpULessThan, coord_id, size_id);
uint32_t error =
(image_ty_inst->GetSingleWordInOperand(kSpvTypeImageSampled) == 2)
? kInstErrorBuffOOBStorageTexel
: kInstErrorBuffOOBUniformTexel;
uint32_t error_id = builder.GetUintConstantId(error);
GenCheckCode(ult_inst->result_id(), error_id, coord_id, size_id, stage_idx,
&ref, new_blocks);
GenCheckCode(check_id, 0, 0, 0, stage_idx, &ref, new_blocks);
// Move original block's remaining code into remainder/merge block and add
// to new blocks
BasicBlock* back_blk_ptr = &*new_blocks->back();
@ -1293,58 +1094,32 @@ void InstBindlessCheckPass::GenTexBuffCheckCode(
void InstBindlessCheckPass::InitializeInstBindlessCheck() {
// Initialize base class
InitializeInstrument();
// If runtime array length support or buffer bounds checking are enabled,
// create variable mappings. Length support is always enabled if descriptor
// init check is enabled.
if (desc_idx_enabled_ || buffer_bounds_enabled_ || texel_buffer_enabled_)
for (auto& anno : get_module()->annotations())
if (anno.opcode() == spv::Op::OpDecorate) {
if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
spv::Decoration::DescriptorSet) {
var2desc_set_[anno.GetSingleWordInOperand(0u)] =
anno.GetSingleWordInOperand(2u);
} else if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
spv::Decoration::Binding) {
var2binding_[anno.GetSingleWordInOperand(0u)] =
anno.GetSingleWordInOperand(2u);
}
for (auto& anno : get_module()->annotations()) {
if (anno.opcode() == spv::Op::OpDecorate) {
if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
spv::Decoration::DescriptorSet) {
var2desc_set_[anno.GetSingleWordInOperand(0u)] =
anno.GetSingleWordInOperand(2u);
} else if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
spv::Decoration::Binding) {
var2binding_[anno.GetSingleWordInOperand(0u)] =
anno.GetSingleWordInOperand(2u);
}
}
}
}
Pass::Status InstBindlessCheckPass::ProcessImpl() {
// Perform bindless bounds check on each entry point function in module
bool modified = false;
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 GenDescIdxCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
new_blocks);
return GenDescCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
new_blocks);
};
bool modified = InstProcessEntryPointCallTree(pfn);
if (desc_init_enabled_ || buffer_bounds_enabled_) {
// Perform descriptor initialization and/or buffer bounds check on each
// entry point function in module
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 GenDescInitCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
new_blocks);
};
modified |= InstProcessEntryPointCallTree(pfn);
}
if (texel_buffer_enabled_) {
// Perform texel buffer bounds check on each entry point function in
// module. Generate after descriptor bounds and initialization checks.
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 GenTexBuffCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
new_blocks);
};
modified |= InstProcessEntryPointCallTree(pfn);
}
modified = InstProcessEntryPointCallTree(pfn);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}

View File

@ -28,16 +28,8 @@ namespace opt {
// external design may change as the layer evolves.
class InstBindlessCheckPass : public InstrumentPass {
public:
InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
bool desc_idx_enable, bool desc_init_enable,
bool buffer_bounds_enable, bool texel_buffer_enable,
bool opt_direct_reads)
: InstrumentPass(desc_set, shader_id, kInstValidationIdBindless,
opt_direct_reads),
desc_idx_enabled_(desc_idx_enable),
desc_init_enabled_(desc_init_enable),
buffer_bounds_enabled_(buffer_bounds_enable),
texel_buffer_enabled_(texel_buffer_enable) {}
InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id)
: InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, true) {}
~InstBindlessCheckPass() override = default;
@ -47,82 +39,18 @@ class InstBindlessCheckPass : public InstrumentPass {
const char* name() const override { return "inst-bindless-check-pass"; }
private:
// These functions do bindless checking instrumentation on a single
// instruction which references through a descriptor (ie references into an
// image or buffer). Refer to Vulkan API for further information on
// descriptors. GenDescIdxCheckCode checks that an index into a descriptor
// array (array of images or buffers) is in-bounds. GenDescInitCheckCode
// checks that the referenced descriptor has been initialized, if the
// SPV_EXT_descriptor_indexing extension is enabled, and initialized large
// enough to handle the reference, if RobustBufferAccess is disabled.
// GenDescInitCheckCode checks for uniform and storage buffer overrun.
// GenTexBuffCheckCode checks for texel buffer overrun and should be
// run after GenDescInitCheckCode to first make sure that the descriptor
// is initialized because it uses OpImageQuerySize on the descriptor.
//
// The functions are designed to be passed to
// InstrumentPass::InstProcessEntryPointCallTree(), which applies the
// function to each instruction in a module and replaces the instruction
// if warranted.
//
// If |ref_inst_itr| is a bindless 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.
//
// These instrumentation functions utilize GenDebugDirectRead() to read data
// from the debug input buffer, specifically the lengths of variable length
// descriptor arrays, and the initialization status of each descriptor.
// The format of the debug input buffer is documented in instrument.hpp.
//
// These instrumentation functions utilize GenDebugStreamWrite() to write its
// error records. The validation-specific part of the error record will
// have the format:
//
// Validation Error Code (=kInstErrorBindlessBounds)
// Descriptor Index
// Descriptor Array Size
//
// The Descriptor Index is the index which has been determined to be
// out-of-bounds.
//
// The Descriptor Array Size is the size of the descriptor array which was
// indexed.
void GenDescIdxCheckCode(
BasicBlock::iterator ref_inst_itr,
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
void GenDescInitCheckCode(
BasicBlock::iterator ref_inst_itr,
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
void GenTexBuffCheckCode(
BasicBlock::iterator ref_inst_itr,
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
void GenDescCheckCode(BasicBlock::iterator ref_inst_itr,
UptrVectorIterator<BasicBlock> ref_block_itr,
uint32_t stage_idx,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
void SetupInputBufferIds();
uint32_t GenDebugReadLengthFunctionId();
// Generate instructions into |builder| to read length of runtime descriptor
// array |var_id| from debug input buffer and return id of value.
uint32_t GenDebugReadLength(uint32_t var_id, InstructionBuilder* builder);
uint32_t GenDescCheckFunctionId();
uint32_t GenDebugReadInitFunctionId();
// Generate instructions into |builder| to read initialization status of
// descriptor array |image_id| at |index_id| from debug input buffer and
// return id of value.
uint32_t GenDebugReadInit(uint32_t image_id, uint32_t index_id,
InstructionBuilder* builder);
uint32_t GenDescCheckCall(uint32_t inst_idx, uint32_t stage_idx,
uint32_t var_id, uint32_t index_id,
uint32_t byte_offset, InstructionBuilder* builder);
// Analysis data for descriptor reference components, generated by
// AnalyzeDescriptorReference. It is necessary and sufficient for further
@ -190,26 +118,13 @@ class InstBindlessCheckPass : public InstrumentPass {
// GenDescInitCheckCode to every instruction in module.
Pass::Status ProcessImpl();
// Enable instrumentation of runtime array length checking
bool desc_idx_enabled_;
// Enable instrumentation of descriptor initialization checking
bool desc_init_enabled_;
// Enable instrumentation of uniform and storage buffer overrun checking
bool buffer_bounds_enabled_;
// Enable instrumentation of texel buffer overrun checking
bool texel_buffer_enabled_;
// Mapping from variable to descriptor set
std::unordered_map<uint32_t, uint32_t> var2desc_set_;
// Mapping from variable to binding
std::unordered_map<uint32_t, uint32_t> var2binding_;
uint32_t read_length_func_id_{0};
uint32_t read_init_func_id_{0};
uint32_t desc_check_func_id_{0};
uint32_t desc_set_type_id_{0};
uint32_t desc_set_ptr_id_{0};
uint32_t input_buffer_struct_id_{0};

View File

@ -113,7 +113,9 @@ void InstBuffAddrCheckPass::GenCheckCode(
Instruction* hi_uptr_inst = builder.AddUnaryOp(
GetUintId(), spv::Op::OpUConvert, rshift_uptr_inst->result_id());
GenDebugStreamWrite(
uid2offset_[ref_inst->unique_id()], stage_idx,
builder.GetUintConstantId(shader_id_),
builder.GetUintConstantId(uid2offset_[ref_inst->unique_id()]),
GenStageInfo(stage_idx, &builder),
{error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
&builder);
// Gen zero for invalid load. If pointer type, need to convert uint64

View File

@ -165,8 +165,10 @@ void InstDebugPrintfPass::GenOutputCode(
GenOutputValues(opnd_inst, &val_ids, &builder);
}
});
GenDebugStreamWrite(uid2offset_[printf_inst->unique_id()], stage_idx, val_ids,
&builder);
GenDebugStreamWrite(
builder.GetUintConstantId(shader_id_),
builder.GetUintConstantId(uid2offset_[printf_inst->unique_id()]),
GenStageInfo(stage_idx, &builder), val_ids, &builder);
context()->KillInst(printf_inst);
}

View File

@ -22,9 +22,6 @@
namespace spvtools {
namespace opt {
namespace {
// Common Parameter Positions
constexpr int kInstCommonParamInstIdx = 0;
constexpr int kInstCommonParamCnt = 1;
// Indices of operands in SPIR-V instructions
constexpr int kEntryPointFunctionIdInIdx = 1;
} // namespace
@ -216,34 +213,6 @@ void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
(void)builder->AddStore(achain_inst->result_id(), val_id);
}
void InstrumentPass::GenCommonStreamWriteCode(uint32_t record_sz,
uint32_t inst_id,
uint32_t stage_idx,
uint32_t base_offset_id,
InstructionBuilder* builder) {
// Store record size
GenDebugOutputFieldCode(base_offset_id, kInstCommonOutSize,
builder->GetUintConstantId(record_sz), builder);
// Store Shader Id
GenDebugOutputFieldCode(base_offset_id, kInstCommonOutShaderId,
builder->GetUintConstantId(shader_id_), builder);
// Store Instruction Idx
GenDebugOutputFieldCode(base_offset_id, kInstCommonOutInstructionIdx, inst_id,
builder);
// Store Stage Idx
GenDebugOutputFieldCode(base_offset_id, kInstCommonOutStageIdx,
builder->GetUintConstantId(stage_idx), builder);
}
void InstrumentPass::GenFragCoordEltDebugOutputCode(
uint32_t base_offset_id, uint32_t uint_frag_coord_id, uint32_t element,
InstructionBuilder* builder) {
Instruction* element_val_inst =
builder->AddCompositeExtract(GetUintId(), uint_frag_coord_id, {element});
GenDebugOutputFieldCode(base_offset_id, kInstFragOutFragCoordX + element,
element_val_inst->result_id(), builder);
}
uint32_t InstrumentPass::GenVarLoad(uint32_t var_id,
InstructionBuilder* builder) {
Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
@ -252,28 +221,23 @@ uint32_t InstrumentPass::GenVarLoad(uint32_t var_id,
return load_inst->result_id();
}
void InstrumentPass::GenBuiltinOutputCode(uint32_t builtin_id,
uint32_t builtin_off,
uint32_t base_offset_id,
InstructionBuilder* builder) {
// Load and store builtin
uint32_t load_id = GenVarLoad(builtin_id, builder);
GenDebugOutputFieldCode(base_offset_id, builtin_off, load_id, builder);
}
void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
uint32_t base_offset_id,
InstructionBuilder* builder) {
uint32_t InstrumentPass::GenStageInfo(uint32_t stage_idx,
InstructionBuilder* builder) {
std::vector<uint32_t> ids(4, builder->GetUintConstantId(0));
ids[0] = builder->GetUintConstantId(stage_idx);
// %289 = OpCompositeConstruct %v4uint %uint_0 %285 %288 %uint_0
// TODO(greg-lunarg): Add support for all stages
switch (spv::ExecutionModel(stage_idx)) {
case spv::ExecutionModel::Vertex: {
// Load and store VertexId and InstanceId
GenBuiltinOutputCode(
uint32_t load_id = GenVarLoad(
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::VertexIndex)),
kInstVertOutVertexIndex, base_offset_id, builder);
GenBuiltinOutputCode(context()->GetBuiltinInputVarId(
builder);
ids[1] = load_id;
load_id = GenVarLoad(context()->GetBuiltinInputVarId(
uint32_t(spv::BuiltIn::InstanceIndex)),
kInstVertOutInstanceIndex, base_offset_id, builder);
builder);
ids[2] = load_id;
} break;
case spv::ExecutionModel::GLCompute:
case spv::ExecutionModel::TaskNV:
@ -284,56 +248,50 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
uint32_t load_id = GenVarLoad(context()->GetBuiltinInputVarId(uint32_t(
spv::BuiltIn::GlobalInvocationId)),
builder);
Instruction* x_inst =
builder->AddCompositeExtract(GetUintId(), load_id, {0});
Instruction* y_inst =
builder->AddCompositeExtract(GetUintId(), load_id, {1});
Instruction* z_inst =
builder->AddCompositeExtract(GetUintId(), load_id, {2});
GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX,
x_inst->result_id(), builder);
GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY,
y_inst->result_id(), builder);
GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ,
z_inst->result_id(), builder);
for (uint32_t u = 0; u < 3u; ++u) {
ids[u + 1] = builder->AddCompositeExtract(GetUintId(), load_id, {u})
->result_id();
}
} break;
case spv::ExecutionModel::Geometry: {
// Load and store PrimitiveId and InvocationId.
GenBuiltinOutputCode(
uint32_t load_id = GenVarLoad(
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)),
kInstGeomOutPrimitiveId, base_offset_id, builder);
GenBuiltinOutputCode(
builder);
ids[1] = load_id;
load_id = GenVarLoad(
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::InvocationId)),
kInstGeomOutInvocationId, base_offset_id, builder);
builder);
ids[2] = load_id;
} break;
case spv::ExecutionModel::TessellationControl: {
// Load and store InvocationId and PrimitiveId
GenBuiltinOutputCode(
uint32_t load_id = GenVarLoad(
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::InvocationId)),
kInstTessCtlOutInvocationId, base_offset_id, builder);
GenBuiltinOutputCode(
builder);
ids[1] = load_id;
load_id = GenVarLoad(
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)),
kInstTessCtlOutPrimitiveId, base_offset_id, builder);
builder);
ids[2] = load_id;
} break;
case spv::ExecutionModel::TessellationEvaluation: {
// Load and store PrimitiveId and TessCoord.uv
GenBuiltinOutputCode(
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)),
kInstTessEvalOutPrimitiveId, base_offset_id, builder);
uint32_t load_id = GenVarLoad(
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)),
builder);
ids[1] = load_id;
load_id = GenVarLoad(
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::TessCoord)),
builder);
Instruction* uvec3_cast_inst =
builder->AddUnaryOp(GetVec3UintId(), spv::Op::OpBitcast, load_id);
uint32_t uvec3_cast_id = uvec3_cast_inst->result_id();
Instruction* u_inst =
builder->AddCompositeExtract(GetUintId(), uvec3_cast_id, {0});
Instruction* v_inst =
builder->AddCompositeExtract(GetUintId(), uvec3_cast_id, {1});
GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU,
u_inst->result_id(), builder);
GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV,
v_inst->result_id(), builder);
for (uint32_t u = 0; u < 2u; ++u) {
ids[u + 2] =
builder->AddCompositeExtract(GetUintId(), uvec3_cast_id, {u})
->result_id();
}
} break;
case spv::ExecutionModel::Fragment: {
// Load FragCoord and convert to Uint
@ -342,9 +300,13 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::FragCoord)));
Instruction* uint_frag_coord_inst = builder->AddUnaryOp(
GetVec4UintId(), spv::Op::OpBitcast, frag_coord_inst->result_id());
for (uint32_t u = 0; u < 2u; ++u)
GenFragCoordEltDebugOutputCode(
base_offset_id, uint_frag_coord_inst->result_id(), u, builder);
for (uint32_t u = 0; u < 2u; ++u) {
ids[u + 1] =
builder
->AddCompositeExtract(GetUintId(),
uint_frag_coord_inst->result_id(), {u})
->result_id();
}
} break;
case spv::ExecutionModel::RayGenerationNV:
case spv::ExecutionModel::IntersectionNV:
@ -356,33 +318,26 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
uint32_t launch_id = GenVarLoad(
context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::LaunchIdNV)),
builder);
Instruction* x_launch_inst =
builder->AddCompositeExtract(GetUintId(), launch_id, {0});
Instruction* y_launch_inst =
builder->AddCompositeExtract(GetUintId(), launch_id, {1});
Instruction* z_launch_inst =
builder->AddCompositeExtract(GetUintId(), 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);
for (uint32_t u = 0; u < 3u; ++u) {
ids[u + 1] = builder->AddCompositeExtract(GetUintId(), launch_id, {u})
->result_id();
}
} break;
default: { assert(false && "unsupported stage"); } break;
}
return builder->AddCompositeConstruct(GetVec4UintId(), ids)->result_id();
}
void InstrumentPass::GenDebugStreamWrite(
uint32_t instruction_idx, uint32_t stage_idx,
uint32_t shader_id, uint32_t instruction_idx_id, uint32_t stage_info_id,
const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) {
// Call debug output function. Pass func_idx, instruction_idx and
// validation ids as args.
uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size());
std::vector<uint32_t> args = {builder->GetUintConstantId(instruction_idx)};
std::vector<uint32_t> args = {shader_id, instruction_idx_id, stage_info_id};
(void)args.insert(args.end(), validation_ids.begin(), validation_ids.end());
(void)builder->AddFunctionCall(
GetVoidId(), GetStreamWriteFunctionId(stage_idx, val_id_cnt), args);
(void)builder->AddFunctionCall(GetVoidId(),
GetStreamWriteFunctionId(val_id_cnt), args);
}
bool InstrumentPass::AllConstant(const std::vector<uint32_t>& ids) {
@ -398,11 +353,12 @@ uint32_t InstrumentPass::GenDebugDirectRead(
// Call debug input function. Pass func_idx and offset ids as args.
const uint32_t off_id_cnt = static_cast<uint32_t>(offset_ids.size());
const uint32_t input_func_id = GetDirectReadFunctionId(off_id_cnt);
return GenReadFunctionCall(input_func_id, offset_ids, builder);
return GenReadFunctionCall(GetUintId(), input_func_id, offset_ids, builder);
}
uint32_t InstrumentPass::GenReadFunctionCall(
uint32_t func_id, const std::vector<uint32_t>& func_call_args,
uint32_t return_id, uint32_t func_id,
const std::vector<uint32_t>& func_call_args,
InstructionBuilder* ref_builder) {
// If optimizing direct reads and the call has already been generated,
// use its result
@ -423,8 +379,7 @@ uint32_t InstrumentPass::GenReadFunctionCall(
builder.SetInsertPoint(insert_before);
}
uint32_t res_id =
builder.AddFunctionCall(GetUintId(), func_id, func_call_args)
->result_id();
builder.AddFunctionCall(return_id, func_id, func_call_args)->result_id();
if (insert_in_first_block) call2id_[func_call_args] = res_id;
return res_id;
}
@ -817,18 +772,27 @@ uint32_t InstrumentPass::GetVoidId() {
return void_id_;
}
uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
uint32_t val_spec_param_cnt) {
uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t param_cnt) {
enum {
kShaderId = 0,
kInstructionIndex = 1,
kStageInfo = 2,
kFirstParam = 3,
};
// Total param count is common params plus validation-specific
// params
uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt;
if (param2output_func_id_[param_cnt] == 0) {
// Create function
param2output_func_id_[param_cnt] = TakeNextId();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
const std::vector<const analysis::Type*> param_types(param_cnt,
GetInteger(32, false));
const analysis::Type* uint_type = GetInteger(32, false);
const analysis::Vector v4uint(uint_type, 4);
const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint);
std::vector<const analysis::Type*> param_types(kFirstParam + param_cnt,
uint_type);
param_types[kStageInfo] = v4uint_type;
std::unique_ptr<Function> output_func = StartFunction(
param2output_func_id_[param_cnt], type_mgr->GetVoidType(), param_types);
@ -841,10 +805,10 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
context(), &*new_blk_ptr,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
// Gen test if debug output buffer size will not be exceeded.
uint32_t val_spec_offset = kInstStageOutCnt;
uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt;
uint32_t buf_id = GetOutputBufferId();
uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
const uint32_t val_spec_offset = kInstStageOutCnt;
const uint32_t obuf_record_sz = val_spec_offset + param_cnt;
const uint32_t buf_id = GetOutputBufferId();
const uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
Instruction* obuf_curr_sz_ac_inst = builder.AddAccessChain(
buf_uint_ptr_id, buf_id,
{builder.GetUintConstantId(kDebugOutputSizeOffset)});
@ -884,13 +848,26 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label));
builder.SetInsertPoint(&*new_blk_ptr);
// Generate common and stage-specific debug record members
GenCommonStreamWriteCode(obuf_record_sz, param_ids[kInstCommonParamInstIdx],
stage_idx, obuf_curr_sz_id, &builder);
GenStageStreamWriteCode(stage_idx, obuf_curr_sz_id, &builder);
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutSize,
builder.GetUintConstantId(obuf_record_sz),
&builder);
// Store Shader Id
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutShaderId,
param_ids[kShaderId], &builder);
// Store Instruction Idx
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutInstructionIdx,
param_ids[kInstructionIndex], &builder);
// Store stage info. Stage Idx + 3 words of stage-specific data.
for (uint32_t i = 0; i < 4; ++i) {
Instruction* field =
builder.AddCompositeExtract(GetUintId(), param_ids[kStageInfo], {i});
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutStageIdx + i,
field->result_id(), &builder);
}
// Gen writes of validation specific data
for (uint32_t i = 0; i < val_spec_param_cnt; ++i) {
for (uint32_t i = 0; i < param_cnt; ++i) {
GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i,
param_ids[kInstCommonParamCnt + i], &builder);
param_ids[kFirstParam + i], &builder);
}
// Close write block and gen merge block
(void)builder.AddBranch(merge_blk_id);

View File

@ -196,7 +196,8 @@ class InstrumentPass : public Pass {
// Because the code that is generated checks against the size of the buffer
// before writing, the size of the debug out buffer can be used by the
// validation layer to control the number of error records that are written.
void GenDebugStreamWrite(uint32_t instruction_idx, uint32_t stage_idx,
void GenDebugStreamWrite(uint32_t shader_id, uint32_t instruction_idx_id,
uint32_t stage_info_id,
const std::vector<uint32_t>& validation_ids,
InstructionBuilder* builder);
@ -214,7 +215,7 @@ class InstrumentPass : public Pass {
uint32_t GenDebugDirectRead(const std::vector<uint32_t>& offset_ids,
InstructionBuilder* builder);
uint32_t GenReadFunctionCall(uint32_t func_id,
uint32_t GenReadFunctionCall(uint32_t return_id, uint32_t func_id,
const std::vector<uint32_t>& args,
InstructionBuilder* builder);
@ -323,8 +324,7 @@ class InstrumentPass : public Pass {
// Return id for output function. Define if it doesn't exist with
// |val_spec_param_cnt| validation-specific uint32 parameters.
uint32_t GetStreamWriteFunctionId(uint32_t stage_idx,
uint32_t val_spec_param_cnt);
uint32_t GetStreamWriteFunctionId(uint32_t val_spec_param_cnt);
// Return id for input function taking |param_cnt| uint32 parameters. Define
// if it doesn't exist.
@ -355,34 +355,11 @@ class InstrumentPass : public Pass {
uint32_t field_value_id,
InstructionBuilder* builder);
// Generate instructions into |builder| which will write the members
// of the debug output record common for all stages and validations at
// |base_off|.
void GenCommonStreamWriteCode(uint32_t record_sz, uint32_t instruction_idx,
uint32_t stage_idx, uint32_t base_off,
InstructionBuilder* builder);
// Generate instructions into |builder| which will write
// |uint_frag_coord_id| at |component| of the record at |base_offset_id| of
// the debug output buffer .
void GenFragCoordEltDebugOutputCode(uint32_t base_offset_id,
uint32_t uint_frag_coord_id,
uint32_t component,
InstructionBuilder* builder);
// Generate instructions into |builder| which will load |var_id| and return
// its result id.
uint32_t GenVarLoad(uint32_t var_id, InstructionBuilder* builder);
// Generate instructions into |builder| which will load the uint |builtin_id|
// and write it into the debug output buffer at |base_off| + |builtin_off|.
void GenBuiltinOutputCode(uint32_t builtin_id, uint32_t builtin_off,
uint32_t base_off, InstructionBuilder* builder);
// Generate instructions into |builder| which will write the |stage_idx|-
// specific members of the debug output stream at |base_off|.
void GenStageStreamWriteCode(uint32_t stage_idx, uint32_t base_off,
InstructionBuilder* builder);
uint32_t GenStageInfo(uint32_t stage_idx, InstructionBuilder* builder);
// Return true if instruction must be in the same block that its result
// is used.

View File

@ -440,6 +440,22 @@ class InstructionBuilder {
return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant);
}
Instruction* GetBoolConstant(bool value) {
analysis::Bool type;
uint32_t type_id = GetContext()->get_type_mgr()->GetTypeInstruction(&type);
analysis::Type* rebuilt_type =
GetContext()->get_type_mgr()->GetType(type_id);
uint32_t word = value;
const analysis::Constant* constant =
GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word});
return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant);
}
uint32_t GetBoolConstantId(bool value) {
Instruction* inst = GetBoolConstant(value);
return (inst != nullptr ? inst->result_id() : 0);
}
Instruction* AddCompositeExtract(uint32_t type, uint32_t id_of_composite,
const std::vector<uint32_t>& index_list) {
std::vector<Operand> operands;

View File

@ -429,20 +429,11 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateWorkaround1209Pass());
} else if (pass_name == "replace-invalid-opcode") {
RegisterPass(CreateReplaceInvalidOpcodePass());
} else if (pass_name == "inst-bindless-check") {
RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
RegisterPass(CreateAggressiveDCEPass(true));
} else if (pass_name == "inst-desc-idx-check") {
RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
RegisterPass(CreateAggressiveDCEPass(true));
} else if (pass_name == "inst-buff-oob-check") {
RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, true, true));
} else if (pass_name == "inst-bindless-check" ||
pass_name == "inst-desc-idx-check" ||
pass_name == "inst-buff-oob-check") {
// preserve legacy names
RegisterPass(CreateInstBindlessCheckPass(7, 23));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
@ -955,14 +946,10 @@ Optimizer::PassToken CreateUpgradeMemoryModelPass() {
MakeUnique<opt::UpgradeMemoryModel>());
}
Optimizer::PassToken CreateInstBindlessCheckPass(
uint32_t desc_set, uint32_t shader_id, bool desc_length_enable,
bool desc_init_enable, bool buff_oob_enable, bool texbuff_oob_enable) {
Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
uint32_t shader_id) {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::InstBindlessCheckPass>(
desc_set, shader_id, desc_length_enable, desc_init_enable,
buff_oob_enable, texbuff_oob_enable,
desc_length_enable || desc_init_enable || buff_oob_enable));
MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id));
}
Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,

File diff suppressed because it is too large Load Diff

View File

@ -40,12 +40,14 @@ static const std::string kOutputGlobals = R"(
; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer
)";
static const std::string kStreamWrite4Begin = R"(
; CHECK: {{%\w+}} = OpFunction %void None {{%\w+}}
; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
static const std::string kStreamWrite3 = R"(
; CHECK: %inst_buff_addr_stream_write_3 = OpFunction %void None {{%\w+}}
; CHECK: [[sw_shader_id:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_inst_idx:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_stage_info:%\w+]] = OpFunctionParameter %v4uint
; CHECK: [[sw_param_1:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_param_2:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_param_3:%\w+]] = OpFunctionParameter %uint
; CHECK: {{%\w+}} = OpLabel
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1
; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10
@ -60,81 +62,41 @@ static const std::string kStreamWrite4Begin = R"(
; CHECK: OpStore {{%\w+}} %uint_10
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} %uint_23
; CHECK: OpStore {{%\w+}} [[sw_shader_id]]
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_1]]
)";
static const std::string kStreamWrite4End = R"(
; CHECK: OpStore {{%\w+}} [[sw_inst_idx]]
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 0
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 1
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 2
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 3
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_2]]
; CHECK: OpStore {{%\w+}} [[sw_param_1]]
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_3]]
; CHECK: OpStore {{%\w+}} [[sw_param_2]]
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_4]]
; CHECK: OpStore {{%\w+}} [[sw_param_3]]
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
)";
// clang-format off
static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"(
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} %uint_4
; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
)" + kStreamWrite4End;
static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"(
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} %uint_5
; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
)" + kStreamWrite4End;
// clang-format on
// clang-format off
static const std::string kStreamWrite4Vert = kStreamWrite4Begin + R"(
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} %uint_0
; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
)" + kStreamWrite4End;
// clang-format on
static const std::string kInputDecorations = R"(
; CHECK: OpDecorate [[input_buffer_type:%inst_buff_addr_InputBuffer]] Block
; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0
@ -242,9 +204,9 @@ OpDecorate %u_info DescriptorSet 0
OpDecorate %u_info Binding 0
; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
)" + kInputDecorations + R"(
; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
)" + kOutputDecorations + R"(
; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
)";
const std::string globals = R"(
@ -267,17 +229,15 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
; CHECK: %ulong = OpTypeInt 64 0
; CHECK: %bool = OpTypeBool
; CHECK: %28 = OpTypeFunction %bool %ulong %uint
; CHECK: %_runtimearr_ulong = OpTypeRuntimeArray %ulong
)" + kInputGlobals + R"(
; CHECK: %_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
; CHECK: {{%\w+}} = OpTypeFunction %void %uint %uint %uint %uint
; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
)" + kOutputGlobals + R"(
; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
; CHECK: %v3uint = OpTypeVector %uint 3
; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
)" + kOutputGlobals + R"(
; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
)";
// clang-format off
@ -293,26 +253,31 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
; CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
; CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20
; CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1
; CHECK: %24 = OpConvertPtrToU %ulong %22
; CHECK: %61 = OpFunctionCall %bool %inst_buff_addr_search_and_test %24 %uint_4
; CHECK: OpSelectionMerge %62 None
; CHECK: OpBranchConditional %61 %63 %64
; CHECK: %63 = OpLabel
; CHECK: {{%\w+}} = OpConvertPtrToU %ulong %22
; CHECK: {{%\w+}} = OpFunctionCall %bool %inst_buff_addr_search_and_test {{%\w+}} %uint_4
; CHECK: OpSelectionMerge {{%\w+}} None
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
OpStore %22 %int_3239 Aligned 16
; CHECK: OpStore %22 %int_3239 Aligned 16
; CHECK: OpBranch %62
; CHECK: %64 = OpLabel
; CHECK: %65 = OpUConvert %uint %24
; CHECK: %67 = OpShiftRightLogical %ulong %24 %uint_32
; CHECK: %68 = OpUConvert %uint %67
; CHECK: %124 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_48 %uint_2 %65 %68
; CHECK: OpBranch %62
; CHECK: %62 = OpLabel
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_48 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute;
const std::string output_funcs = kSearchAndTest + kStreamWrite3;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<InstBuffAddrCheckPass>(
@ -376,10 +341,9 @@ OpDecorate %r DescriptorSet 0
OpDecorate %r Binding 0
; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
)" + kInputDecorations + R"(
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
)" + kOutputDecorations + R"(
; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
)";
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
)" + kOutputDecorations;
// clang-format on
const std::string globals = R"(
@ -422,31 +386,41 @@ OpStore %26 %int_531 Aligned 16
; CHECK: %72 = OpUConvert %uint %30
; CHECK: %74 = OpShiftRightLogical %ulong %30 %uint_32
; CHECK: %75 = OpUConvert %uint %74
; CHECK: %131 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_44 %uint_2 %72 %75
; CHECK: %133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132
; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_44 {{%\w+}} %uint_3 %72 %75
; CHECK: {{%\w+}} = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType {{%\w+}}
; CHECK: OpBranch %68
; CHECK: %68 = OpLabel
; CHECK: %134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70
; CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0
; CHECK: %135 = OpConvertPtrToU %ulong %26
; CHECK: %136 = OpFunctionCall %bool %inst_buff_addr_search_and_test %135 %uint_4
; CHECK: OpSelectionMerge %137 None
; CHECK: OpBranchConditional %136 %138 %139
; CHECK: %138 = OpLabel
; CHECK: {{%\w+}} = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 {{%\w+}} %70
; CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int {{%\w+}} %int_0
; CHECK: {{%\w+}} = OpConvertPtrToU %ulong %26
; CHECK: {{%\w+}} = OpFunctionCall %bool %inst_buff_addr_search_and_test {{%\w+}} %uint_4
; CHECK: OpSelectionMerge {{%\w+}} None
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: OpStore %26 %int_531 Aligned 16
; CHECK: OpBranch %137
; CHECK: %139 = OpLabel
; CHECK: %140 = OpUConvert %uint %135
; CHECK: %141 = OpShiftRightLogical %ulong %135 %uint_32
; CHECK: %142 = OpUConvert %uint %141
; CHECK: %144 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_46 %uint_2 %140 %142
; CHECK: OpBranch %137
; CHECK: %137 = OpLabel
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_46 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute;
const std::string output_funcs = kSearchAndTest + kStreamWrite3;
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<InstBuffAddrCheckPass>(
@ -481,7 +455,7 @@ OpCapability PhysicalStorageBufferAddresses
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
; CHECK: OpEntryPoint Fragment %main "main" %inst_buff_addr_input_buffer %inst_buff_addr_output_buffer %gl_FragCoord
; CHECK: OpEntryPoint Fragment %main "main" %inst_buff_addr_input_buffer %gl_FragCoord %inst_buff_addr_output_buffer
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_ARB_gpu_shader_int64"
@ -502,10 +476,9 @@ OpMemberDecorate %TestBuffer 0 Offset 0
OpDecorate %TestBuffer Block
; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
)" + kInputDecorations + R"(
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
)" + kOutputDecorations + R"(
; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
)";
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
)" + kOutputDecorations;
const std::string globals = R"(
%void = OpTypeVoid
@ -522,9 +495,7 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffe
%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0
%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704
; CHECK: %47 = OpTypeFunction %bool %ulong %uint
)" + kInputGlobals + R"(
; CHECK: {{%\w+}} = OpTypeFunction %void %uint %uint %uint %uint
)" + kOutputGlobals + R"(
)" + kInputGlobals + kOutputGlobals + R"(
; CHECK: {{%\w+}} = OpConstantNull %Test_0
)";
// clang-format on
@ -537,29 +508,34 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffe
%38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0
%39 = OpLoad %Test_0 %38 Aligned 16
; CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16
; CHECK: %43 = OpConvertPtrToU %ulong %38
; CHECK: %80 = OpFunctionCall %bool %inst_buff_addr_search_and_test %43 %uint_4
; CHECK: OpSelectionMerge %81 None
; CHECK: OpBranchConditional %80 %82 %83
; CHECK: %82 = OpLabel
; CHECK: %84 = OpLoad %Test_0 %38 Aligned 16
; CHECK: OpBranch %81
; CHECK: %83 = OpLabel
; CHECK: %85 = OpUConvert %uint %43
; CHECK: %87 = OpShiftRightLogical %ulong %43 %uint_32
; CHECK: %88 = OpUConvert %uint %87
; CHECK: %142 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_37 %uint_2 %85 %88
; CHECK: OpBranch %81
; CHECK: %81 = OpLabel
; CHECK: %144 = OpPhi %Test_0 %84 %82 %143 %83
; CHECK: {{%\w+}} = OpConvertPtrToU %ulong %38
; CHECK: {{%\w+}} = OpFunctionCall %bool %inst_buff_addr_search_and_test {{%\w+}} %uint_4
; CHECK: OpSelectionMerge {{%\w+}} None
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: {{%\w+}} = OpLoad %Test_0 %38 Aligned 16
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_37 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
%40 = OpCopyLogical %Test %39
; CHECK-NOT: %40 = OpCopyLogical %Test %39
; CHECK: %40 = OpCopyLogical %Test %144
; CHECK: %40 = OpCopyLogical %Test [[phi_result]]
OpReturn
OpFunctionEnd
)";
const std::string output_funcs = kSearchAndTest + kStreamWrite4Frag;
const std::string output_funcs = kSearchAndTest + kStreamWrite3;
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@ -625,10 +601,10 @@ OpMemberDecorate %Test_0 2 Offset 24
OpMemberDecorate %TestBuffer 0 Offset 0
; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
)" + kInputDecorations + R"(
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
)" + kOutputDecorations + R"(
; CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
; CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
)" + kOutputDecorations + R"(
)";
const std::string globals = R"(
@ -688,7 +664,10 @@ OpFunctionEnd
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_62 {{%\w+}} {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_62 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
@ -699,7 +678,7 @@ OpReturnValue %30
OpFunctionEnd
)";
const std::string output_funcs = kSearchAndTest + kStreamWrite4Vert;
const std::string output_funcs = kSearchAndTest + kStreamWrite3;
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@ -731,7 +710,7 @@ OpCapability PhysicalStorageBufferAddresses
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Vertex %main "main" %u_info
;CHECK: OpEntryPoint Vertex %main "main" %u_info %inst_buff_addr_input_buffer %inst_buff_addr_output_buffer %gl_VertexIndex %gl_InstanceIndex
;CHECK: OpEntryPoint Vertex %main "main" %u_info %inst_buff_addr_input_buffer %gl_VertexIndex %gl_InstanceIndex %inst_buff_addr_output_buffer
OpSource GLSL 450
OpSourceExtension "GL_EXT_buffer_reference"
OpName %main "main"
@ -798,13 +777,16 @@ OpBranchConditional %29 %11 %12
;CHECK: %78 = OpLabel
OpStore %36 %int_n559035791 Aligned 16
;CHECK: OpBranch %77
;CHECK: 79 = OpLabel
;CHECK: 80 = OpUConvert %uint %41
;CHECK: 82 = OpShiftRightLogical %ulong %41 %uint_32
;CHECK: 83 = OpUConvert %uint %82
;CHECK: 134 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_62 %uint_2 %80 %83
;CHECK: OpBranch %77
;CHECK: 77 = OpLabel
;CHECK: %79 = OpLabel
;CHECK: %80 = OpUConvert %uint %41
;CHECK: %82 = OpShiftRightLogical %ulong %41 %uint_32
;CHECK: %83 = OpUConvert %uint %82
;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
;CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_62 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
;CHECK: OpBranch {{%\w+}}
;CHECK: {{%\w+}} = OpLabel
OpBranch %13
%13 = OpLabel
%37 = OpLoad %int %i
@ -813,7 +795,7 @@ OpStore %i %38
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd)" + kSearchAndTest + kStreamWrite4Vert;
OpFunctionEnd)" + kSearchAndTest + kStreamWrite3;
// clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
@ -924,7 +906,10 @@ OpStore %readvec %38
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_66 %uint_2 {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_66 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: [[phi_result:%\w+]] = OpPhi %v3uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
@ -938,7 +923,7 @@ OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)" + kSearchAndTest + kStreamWrite4Vert;
)" + kSearchAndTest + kStreamWrite3;
// clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);

View File

@ -87,10 +87,9 @@ OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
OpDecorate %3 Location 0
OpDecorate %4 Location 0
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
)" + kOutputDecorations + R"(
; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
)";
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
)" + kOutputDecorations;
const std::string globals =
R"(%void = OpTypeVoid
@ -110,14 +109,14 @@ OpDecorate %4 Location 0
%_ptr_Output_v4float = OpTypePointer Output %v4float
%4 = OpVariable %_ptr_Output_v4float Output
; CHECK: %uint = OpTypeInt 32 0
; CHECK: [[func_type:%\w+]] = OpTypeFunction %void %uint %uint %uint %uint %uint %uint
; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
; CHECK: %v4uint = OpTypeVector %uint 4
; CHECK: [[func_type:%\w+]] = OpTypeFunction %void %uint %uint %v4uint %uint %uint %uint %uint %uint
; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
)" + kOutputGlobals + R"(
; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
; CHECK: %bool = OpTypeBool
; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
; CHECK: %v4uint = OpTypeVector %uint 4
)";
// clang-format on
@ -131,76 +130,86 @@ OpDecorate %4 Location 0
%25 = OpImageSampleImplicitLod %v4float %24 %21
%26 = OpExtInst %void %1 1 %5 %25
; CHECK-NOT: %26 = OpExtInst %void %1 1 %5 %25
; CHECK: %29 = OpCompositeExtract %float %25 0
; CHECK: %30 = OpBitcast %uint %29
; CHECK: %31 = OpCompositeExtract %float %25 1
; CHECK: %32 = OpBitcast %uint %31
; CHECK: %33 = OpCompositeExtract %float %25 2
; CHECK: %34 = OpBitcast %uint %33
; CHECK: %35 = OpCompositeExtract %float %25 3
; CHECK: %36 = OpBitcast %uint %35
; CHECK: %101 = OpFunctionCall %void %inst_printf_stream_write_6 %uint_36 %uint_5 %30 %32 %34 %36
; CHECK: OpBranch %102
; CHECK: %102 = OpLabel
; CHECK: {{%\w+}} = OpCompositeExtract %float %25 0
; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %float %25 1
; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %float %25 2
; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %float %25 3
; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}}
; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_printf_stream_write_5 %uint_23 %uint_36 {{%\w+}} %uint_5 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
OpStore %4 %25
OpReturn
OpFunctionEnd
)";
const std::string output_func = R"(
; CHECK: %inst_printf_stream_write_6 = OpFunction %void None [[func_type]]
; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
; CHECK: [[param_5:%\w+]] = OpFunctionParameter %uint
; CHECK: [[param_6:%\w+]] = OpFunctionParameter %uint
; CHECK: %inst_printf_stream_write_5 = OpFunction %void None {{%\w+}}
; CHECK: [[sw_shader_id:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_inst_idx:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_stage_info:%\w+]] = OpFunctionParameter %v4uint
; CHECK: [[sw_param_1:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_param_2:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_param_3:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_param_4:%\w+]] = OpFunctionParameter %uint
; CHECK: [[sw_param_5:%\w+]] = OpFunctionParameter %uint
; CHECK: {{%\w+}} = OpLabel
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1
; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_12
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_12
; CHECK: {{%\w+}} = OpArrayLength %uint %inst_printf_output_buffer 2
; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 2
; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
; CHECK: OpSelectionMerge {{%\w+}} None
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} %uint_12
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} %uint_23
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[sw_shader_id]]
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_1]]
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[sw_inst_idx]]
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 0
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} %uint_4
; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 1
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 2
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 3
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} {{%\w+}}
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_2]]
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[sw_param_1]]
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_3]]
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[sw_param_2]]
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_4]]
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[sw_param_3]]
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_5]]
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[sw_param_4]]
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_11
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[param_6]]
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
; CHECK: OpStore {{%\w+}} [[sw_param_5]]
; CHECK: OpBranch {{%\w+}}
; CHECK: {{%\w+}} = OpLabel
; CHECK: OpReturn