aboutsummaryrefslogtreecommitdiff
path: root/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.cpp')
-rw-r--r--third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.cpp832
1 files changed, 118 insertions, 714 deletions
diff --git a/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.cpp b/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.cpp
index e8c412ffe..8e7d4f83e 100644
--- a/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.cpp
@@ -31,532 +31,88 @@ 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() {
- if (input_buffer_id_ != 0) {
- return;
- }
- AddStorageBufferExt();
- if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) {
- context()->AddExtension("SPV_KHR_physical_storage_buffer");
- }
- context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
- Instruction* memory_model = get_module()->GetMemoryModel();
- // TODO should this be just Physical64?
- memory_model->SetInOperand(
- 0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)});
-
- analysis::DecorationManager* deco_mgr = get_decoration_mgr();
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- constexpr uint32_t width = 32u;
-
- // declare the DescriptorSetData struct
- analysis::Struct* desc_set_struct =
- GetStruct({type_mgr->GetUIntType(), GetUintRuntimeArrayType(width)});
- desc_set_type_id_ = type_mgr->GetTypeInstruction(desc_set_struct);
- // By the Vulkan spec, a pre-existing struct containing a RuntimeArray
- // must be a block, and will therefore be decorated with Block. Therefore
- // the undecorated type returned here will not be pre-existing and can
- // safely be decorated. Since this type is now decorated, it is out of
- // sync with the TypeManager and therefore the TypeManager must be
- // invalidated after this pass.
- assert(context()->get_def_use_mgr()->NumUses(desc_set_type_id_) == 0 &&
- "used struct type returned");
- deco_mgr->AddDecoration(desc_set_type_id_, uint32_t(spv::Decoration::Block));
- deco_mgr->AddMemberDecoration(desc_set_type_id_, 0,
- uint32_t(spv::Decoration::Offset), 0);
- deco_mgr->AddMemberDecoration(desc_set_type_id_, 1,
- uint32_t(spv::Decoration::Offset), 4);
- context()->AddDebug2Inst(
- NewGlobalName(desc_set_type_id_, "DescriptorSetData"));
- context()->AddDebug2Inst(NewMemberName(desc_set_type_id_, 0, "num_bindings"));
- context()->AddDebug2Inst(NewMemberName(desc_set_type_id_, 1, "data"));
-
- // declare buffer address reference to DescriptorSetData
- desc_set_ptr_id_ = type_mgr->FindPointerToType(
- desc_set_type_id_, spv::StorageClass::PhysicalStorageBuffer);
- // runtime array of buffer addresses
- analysis::Type* rarr_ty = GetArray(type_mgr->GetType(desc_set_ptr_id_),
- kDebugInputBindlessMaxDescSets);
- deco_mgr->AddDecorationVal(type_mgr->GetId(rarr_ty),
- uint32_t(spv::Decoration::ArrayStride), 8u);
-
- // declare the InputBuffer type, a struct wrapper around the runtime array
- analysis::Struct* input_buffer_struct = GetStruct({rarr_ty});
- input_buffer_struct_id_ = type_mgr->GetTypeInstruction(input_buffer_struct);
- deco_mgr->AddDecoration(input_buffer_struct_id_,
- uint32_t(spv::Decoration::Block));
- deco_mgr->AddMemberDecoration(input_buffer_struct_id_, 0,
- uint32_t(spv::Decoration::Offset), 0);
- context()->AddDebug2Inst(
- NewGlobalName(input_buffer_struct_id_, "InputBuffer"));
- context()->AddDebug2Inst(
- NewMemberName(input_buffer_struct_id_, 0, "desc_sets"));
-
- input_buffer_ptr_id_ = type_mgr->FindPointerToType(
- input_buffer_struct_id_, spv::StorageClass::StorageBuffer);
-
- // declare the input_buffer global variable
- input_buffer_id_ = TakeNextId();
-
- const std::vector<Operand> var_operands = {
- {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
- {uint32_t(spv::StorageClass::StorageBuffer)}},
- };
- auto new_var_op = spvtools::MakeUnique<Instruction>(
- context(), spv::Op::OpVariable, input_buffer_ptr_id_, input_buffer_id_,
- var_operands);
-
- context()->AddGlobalValue(std::move(new_var_op));
- context()->AddDebug2Inst(NewGlobalName(input_buffer_id_, "input_buffer"));
- deco_mgr->AddDecorationVal(
- input_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_);
- deco_mgr->AddDecorationVal(input_buffer_id_,
- uint32_t(spv::Decoration::Binding),
- GetInputBufferBinding());
- if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
- // Add the new buffer to all entry points.
- for (auto& entry : get_module()->entry_points()) {
- entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}});
- context()->AnalyzeUses(&entry);
- }
- }
-}
-
+// This is a stub function for use with Import linkage
// 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(const uint shader_id, const uint inst_num, const uvec4 stage_info, const uint desc_set,
+// const uint binding, const uint desc_index, const uint byte_offset) {
+//}
// clang-format on
-uint32_t InstBindlessCheckPass::GenDebugReadLengthFunctionId() {
- if (read_length_func_id_ != 0) {
- return read_length_func_id_;
+uint32_t InstBindlessCheckPass::GenDescCheckFunctionId() {
+ enum {
+ kShaderId = 0,
+ kInstructionIndex = 1,
+ kStageInfo = 2,
+ kDescSet = 3,
+ kDescBinding = 4,
+ kDescIndex = 5,
+ kByteOffset = 6,
+ kNumArgs
+ };
+ if (check_desc_func_id_ != 0) {
+ return check_desc_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]),
- };
- 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_;
- }
- SetupInputBufferIds();
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);
-
- 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();
+ StartFunction(func_id, type_mgr->GetBoolType(), param_types);
- 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();
-
- // Check descriptor index in bounds
- inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual,
- param_ids[2], length_id);
- const uint32_t desc_idx_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(desc_idx_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);
- (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue,
- builder.GetUintConstantId(0));
- 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]);
- const uint32_t state_offset_id = inst->result_id();
-
- inst =
- builder.AddAccessChain(uint_ptr, desc_set_ptr_id,
- {{builder.GetUintConstantId(1), state_offset_id}});
- const uint32_t state_start_ac_id = inst->result_id();
-
- 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]);
- const uint32_t state_entry_id = inst->result_id();
-
- // Note: length starts from the beginning of the buffer, not the beginning of
- // the data array
- inst =
- builder.AddAccessChain(uint_ptr, desc_set_ptr_id,
- {{builder.GetUintConstantId(1), state_entry_id}});
- const uint32_t init_ac_id = inst->result_id();
-
- 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);
-
- func->AddBasicBlock(std::move(new_blk_ptr));
func->SetFunctionEnd(EndFunction());
- context()->AddFunction(std::move(func));
- context()->AddDebug2Inst(NewGlobalName(func_id, "read_desc_init"));
+ static const std::string func_name{"inst_bindless_check_desc"};
+ context()->AddFunctionDeclaration(std::move(func));
+ context()->AddDebug2Inst(NewName(func_id, func_name));
+ std::vector<Operand> operands{
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {func_id}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+ {uint32_t(spv::Decoration::LinkageAttributes)}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_STRING,
+ utils::MakeVector(func_name.c_str())},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_LINKAGE_TYPE,
+ {uint32_t(spv::LinkageType::Import)}},
+ };
+ get_decoration_mgr()->AddDecoration(spv::Op::OpDecorate, operands);
- read_init_func_id_ = func_id;
+ check_desc_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 check_desc_func_id_;
}
// clang-format off
// GLSL:
-// result = inst_bindless_read_desc_init(desc_set_id, binding_id, desc_idx_id);
+// result = inst_bindless_check_desc(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(
@@ -1017,8 +573,7 @@ uint32_t InstBindlessCheckPass::GenLastByteIdx(RefAnalysis* ref,
}
void InstBindlessCheckPass::GenCheckCode(
- uint32_t check_id, uint32_t error_id, uint32_t offset_id,
- uint32_t length_id, uint32_t stage_idx, RefAnalysis* ref,
+ uint32_t check_id, RefAnalysis* ref,
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
BasicBlock* back_blk_ptr = &*new_blocks->back();
InstructionBuilder builder(
@@ -1047,30 +602,7 @@ 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);
- }
+
// Generate a ConstantNull, converting to uint64 if the type cannot be a null.
if (new_ref_id != 0) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -1106,77 +638,42 @@ void InstBindlessCheckPass::GenCheckCode(
context()->KillInst(ref->ref_inst);
}
-void InstBindlessCheckPass::GenDescIdxCheckCode(
+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 indexed descriptor. If found, analyze and
- // save components. If not, return.
+ // Look for reference through descriptor. 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;
+ // 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));
- 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(
- 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;
// 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 +681,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, &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,59 +708,48 @@ 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
+ // The memory model and linkage must always be updated for spirv-link to work
+ // correctly.
+ AddStorageBufferExt();
+ if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) {
+ context()->AddExtension("SPV_KHR_physical_storage_buffer");
+ }
+
+ context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
+ Instruction* memory_model = get_module()->GetMemoryModel();
+ memory_model->SetInOperand(
+ 0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)});
+
+ context()->AddCapability(spv::Capability::Linkage);
+
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);
- }
- return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+
+ InstProcessEntryPointCallTree(pfn);
+ // This pass always changes the memory model, so that linking will work
+ // properly.
+ return Status::SuccessWithChange;
}
Pass::Status InstBindlessCheckPass::Process() {