diff options
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.cpp | 832 |
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() { |