|
|
@@ -0,0 +1,427 @@
|
|
|
+// Copyright (c) 2019 The Khronos Group Inc.
|
|
|
+// Copyright (c) 2019 Valve Corporation
|
|
|
+// Copyright (c) 2019 LunarG Inc.
|
|
|
+//
|
|
|
+// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+// you may not use this file except in compliance with the License.
|
|
|
+// You may obtain a copy of the License at
|
|
|
+//
|
|
|
+// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+//
|
|
|
+// Unless required by applicable law or agreed to in writing, software
|
|
|
+// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+// See the License for the specific language governing permissions and
|
|
|
+// limitations under the License.
|
|
|
+
|
|
|
+#include "inst_buff_addr_check_pass.h"
|
|
|
+
|
|
|
+namespace spvtools {
|
|
|
+namespace opt {
|
|
|
+
|
|
|
+uint32_t InstBuffAddrCheckPass::CloneOriginalReference(
|
|
|
+ Instruction* ref_inst, InstructionBuilder* builder) {
|
|
|
+ // Clone original ref with new result id (if load)
|
|
|
+ assert(
|
|
|
+ (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) &&
|
|
|
+ "unexpected ref");
|
|
|
+ std::unique_ptr<Instruction> new_ref_inst(ref_inst->Clone(context()));
|
|
|
+ uint32_t ref_result_id = ref_inst->result_id();
|
|
|
+ uint32_t new_ref_id = 0;
|
|
|
+ if (ref_result_id != 0) {
|
|
|
+ new_ref_id = TakeNextId();
|
|
|
+ new_ref_inst->SetResultId(new_ref_id);
|
|
|
+ }
|
|
|
+ // Register new reference and add to new block
|
|
|
+ Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst));
|
|
|
+ uid2offset_[added_inst->unique_id()] = uid2offset_[ref_inst->unique_id()];
|
|
|
+ if (new_ref_id != 0)
|
|
|
+ get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id);
|
|
|
+ return new_ref_id;
|
|
|
+}
|
|
|
+
|
|
|
+bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) {
|
|
|
+ if (ref_inst->opcode() != SpvOpLoad && ref_inst->opcode() != SpvOpStore)
|
|
|
+ return false;
|
|
|
+ uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0);
|
|
|
+ analysis::DefUseManager* du_mgr = get_def_use_mgr();
|
|
|
+ Instruction* ptr_inst = du_mgr->GetDef(ptr_id);
|
|
|
+ if (ptr_inst->opcode() != SpvOpAccessChain) return false;
|
|
|
+ uint32_t ptr_ty_id = ptr_inst->type_id();
|
|
|
+ Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id);
|
|
|
+ if (ptr_ty_inst->GetSingleWordInOperand(0) !=
|
|
|
+ SpvStorageClassPhysicalStorageBufferEXT)
|
|
|
+ return false;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+// TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ??
|
|
|
+void InstBuffAddrCheckPass::GenCheckCode(
|
|
|
+ uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id,
|
|
|
+ uint32_t stage_idx, Instruction* ref_inst,
|
|
|
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
|
+ BasicBlock* back_blk_ptr = &*new_blocks->back();
|
|
|
+ InstructionBuilder builder(
|
|
|
+ context(), back_blk_ptr,
|
|
|
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
|
|
+ // Gen conditional branch on check_id. Valid branch generates original
|
|
|
+ // reference. Invalid generates debug output and zero result (if needed).
|
|
|
+ uint32_t merge_blk_id = TakeNextId();
|
|
|
+ uint32_t valid_blk_id = TakeNextId();
|
|
|
+ uint32_t invalid_blk_id = TakeNextId();
|
|
|
+ std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
|
|
|
+ std::unique_ptr<Instruction> valid_label(NewLabel(valid_blk_id));
|
|
|
+ std::unique_ptr<Instruction> invalid_label(NewLabel(invalid_blk_id));
|
|
|
+ (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id,
|
|
|
+ merge_blk_id, SpvSelectionControlMaskNone);
|
|
|
+ // Gen valid branch
|
|
|
+ std::unique_ptr<BasicBlock> new_blk_ptr(
|
|
|
+ new BasicBlock(std::move(valid_label)));
|
|
|
+ builder.SetInsertPoint(&*new_blk_ptr);
|
|
|
+ uint32_t new_ref_id = CloneOriginalReference(ref_inst, &builder);
|
|
|
+ (void)builder.AddBranch(merge_blk_id);
|
|
|
+ new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
+ // Gen invalid block
|
|
|
+ new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
|
|
|
+ builder.SetInsertPoint(&*new_blk_ptr);
|
|
|
+ // Convert uptr from uint64 to 2 uint32
|
|
|
+ Instruction* lo_uptr_inst =
|
|
|
+ builder.AddUnaryOp(GetUintId(), SpvOpUConvert, ref_uptr_id);
|
|
|
+ Instruction* rshift_uptr_inst =
|
|
|
+ builder.AddBinaryOp(GetUint64Id(), SpvOpShiftRightLogical, ref_uptr_id,
|
|
|
+ builder.GetUintConstantId(32));
|
|
|
+ Instruction* hi_uptr_inst = builder.AddUnaryOp(GetUintId(), SpvOpUConvert,
|
|
|
+ rshift_uptr_inst->result_id());
|
|
|
+ GenDebugStreamWrite(
|
|
|
+ uid2offset_[ref_inst->unique_id()], stage_idx,
|
|
|
+ {error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
|
|
|
+ &builder);
|
|
|
+ // Gen zero for invalid reference
|
|
|
+ uint32_t ref_type_id = ref_inst->type_id();
|
|
|
+ (void)builder.AddBranch(merge_blk_id);
|
|
|
+ new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
+ // Gen merge block
|
|
|
+ new_blk_ptr.reset(new BasicBlock(std::move(merge_label)));
|
|
|
+ builder.SetInsertPoint(&*new_blk_ptr);
|
|
|
+ // Gen phi of new reference and zero, if necessary, and replace the
|
|
|
+ // result id of the original reference with that of the Phi. Kill original
|
|
|
+ // reference.
|
|
|
+ if (new_ref_id != 0) {
|
|
|
+ Instruction* phi_inst = builder.AddPhi(
|
|
|
+ ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id),
|
|
|
+ invalid_blk_id});
|
|
|
+ context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id());
|
|
|
+ }
|
|
|
+ new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
+ context()->KillInst(ref_inst);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) {
|
|
|
+ Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
|
|
+ switch (type_inst->opcode()) {
|
|
|
+ case SpvOpTypeFloat:
|
|
|
+ case SpvOpTypeInt:
|
|
|
+ return type_inst->GetSingleWordInOperand(0) / 8u;
|
|
|
+ case SpvOpTypeVector:
|
|
|
+ case SpvOpTypeMatrix:
|
|
|
+ return type_inst->GetSingleWordInOperand(1) *
|
|
|
+ GetTypeLength(type_inst->GetSingleWordInOperand(0));
|
|
|
+ case SpvOpTypePointer:
|
|
|
+ assert(type_inst->GetSingleWordInOperand(0) ==
|
|
|
+ SpvStorageClassPhysicalStorageBufferEXT &&
|
|
|
+ "unexpected pointer type");
|
|
|
+ return 8u;
|
|
|
+ default:
|
|
|
+ assert(false && "unexpected buffer reference type");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void InstBuffAddrCheckPass::AddParam(uint32_t type_id,
|
|
|
+ std::vector<uint32_t>* param_vec,
|
|
|
+ std::unique_ptr<Function>* input_func) {
|
|
|
+ uint32_t pid = TakeNextId();
|
|
|
+ param_vec->push_back(pid);
|
|
|
+ std::unique_ptr<Instruction> param_inst(new Instruction(
|
|
|
+ get_module()->context(), SpvOpFunctionParameter, type_id, pid, {}));
|
|
|
+ get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
|
|
|
+ (*input_func)->AddParameter(std::move(param_inst));
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() {
|
|
|
+ if (search_test_func_id_ == 0) {
|
|
|
+ // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)"
|
|
|
+ // which searches input buffer for buffer which most likely contains the
|
|
|
+ // pointer value |ref_ptr| and verifies that the entire reference of
|
|
|
+ // length |len| bytes is contained in the buffer.
|
|
|
+ search_test_func_id_ = TakeNextId();
|
|
|
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
|
|
+ std::vector<const analysis::Type*> param_types = {
|
|
|
+ type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())};
|
|
|
+ analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types);
|
|
|
+ analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
|
|
|
+ std::unique_ptr<Instruction> func_inst(
|
|
|
+ new Instruction(get_module()->context(), SpvOpFunction, GetBoolId(),
|
|
|
+ search_test_func_id_,
|
|
|
+ {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
|
|
+ {SpvFunctionControlMaskNone}},
|
|
|
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
|
|
+ {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
|
|
|
+ get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
|
|
|
+ std::unique_ptr<Function> input_func =
|
|
|
+ MakeUnique<Function>(std::move(func_inst));
|
|
|
+ std::vector<uint32_t> param_vec;
|
|
|
+ // Add ref_ptr and length parameters
|
|
|
+ AddParam(GetUint64Id(), ¶m_vec, &input_func);
|
|
|
+ AddParam(GetUintId(), ¶m_vec, &input_func);
|
|
|
+ // Empty first block.
|
|
|
+ uint32_t first_blk_id = TakeNextId();
|
|
|
+ std::unique_ptr<Instruction> first_blk_label(NewLabel(first_blk_id));
|
|
|
+ std::unique_ptr<BasicBlock> first_blk_ptr =
|
|
|
+ MakeUnique<BasicBlock>(std::move(first_blk_label));
|
|
|
+ InstructionBuilder builder(
|
|
|
+ context(), &*first_blk_ptr,
|
|
|
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
|
|
+ uint32_t hdr_blk_id = TakeNextId();
|
|
|
+ // Branch to search loop header
|
|
|
+ std::unique_ptr<Instruction> hdr_blk_label(NewLabel(hdr_blk_id));
|
|
|
+ (void)builder.AddInstruction(MakeUnique<Instruction>(
|
|
|
+ context(), SpvOpBranch, 0, 0,
|
|
|
+ std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {hdr_blk_id}}}));
|
|
|
+ first_blk_ptr->SetParent(&*input_func);
|
|
|
+ input_func->AddBasicBlock(std::move(first_blk_ptr));
|
|
|
+ // Linear search loop header block
|
|
|
+ // TODO(greg-lunarg): Implement binary search
|
|
|
+ std::unique_ptr<BasicBlock> hdr_blk_ptr =
|
|
|
+ MakeUnique<BasicBlock>(std::move(hdr_blk_label));
|
|
|
+ builder.SetInsertPoint(&*hdr_blk_ptr);
|
|
|
+ // Phi for search index. Starts with 1.
|
|
|
+ uint32_t cont_blk_id = TakeNextId();
|
|
|
+ std::unique_ptr<Instruction> cont_blk_label(NewLabel(cont_blk_id));
|
|
|
+ // Deal with def-use cycle caused by search loop index computation.
|
|
|
+ // Create Add and Phi instructions first, then do Def analysis on Add.
|
|
|
+ // Add Phi and Add instructions and do Use analysis later.
|
|
|
+ uint32_t idx_phi_id = TakeNextId();
|
|
|
+ uint32_t idx_inc_id = TakeNextId();
|
|
|
+ std::unique_ptr<Instruction> idx_inc_inst(new Instruction(
|
|
|
+ context(), SpvOpIAdd, GetUintId(), idx_inc_id,
|
|
|
+ {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}},
|
|
|
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
|
|
+ {builder.GetUintConstantId(1u)}}}));
|
|
|
+ std::unique_ptr<Instruction> idx_phi_inst(new Instruction(
|
|
|
+ context(), SpvOpPhi, GetUintId(), idx_phi_id,
|
|
|
+ {{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
|
|
+ {builder.GetUintConstantId(1u)}},
|
|
|
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}},
|
|
|
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}},
|
|
|
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
|
|
|
+ get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst);
|
|
|
+ // Add (previously created) search index phi
|
|
|
+ (void)builder.AddInstruction(std::move(idx_phi_inst));
|
|
|
+ // LoopMerge
|
|
|
+ uint32_t bound_test_blk_id = TakeNextId();
|
|
|
+ std::unique_ptr<Instruction> bound_test_blk_label(
|
|
|
+ NewLabel(bound_test_blk_id));
|
|
|
+ (void)builder.AddInstruction(MakeUnique<Instruction>(
|
|
|
+ context(), SpvOpLoopMerge, 0, 0,
|
|
|
+ std::initializer_list<Operand>{
|
|
|
+ {SPV_OPERAND_TYPE_ID, {bound_test_blk_id}},
|
|
|
+ {SPV_OPERAND_TYPE_ID, {cont_blk_id}},
|
|
|
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvLoopControlMaskNone}}}));
|
|
|
+ // Branch to continue/work block
|
|
|
+ (void)builder.AddInstruction(MakeUnique<Instruction>(
|
|
|
+ context(), SpvOpBranch, 0, 0,
|
|
|
+ std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
|
|
|
+ hdr_blk_ptr->SetParent(&*input_func);
|
|
|
+ input_func->AddBasicBlock(std::move(hdr_blk_ptr));
|
|
|
+ // Continue/Work Block. Read next buffer pointer and break if greater
|
|
|
+ // than ref_ptr arg.
|
|
|
+ std::unique_ptr<BasicBlock> cont_blk_ptr =
|
|
|
+ MakeUnique<BasicBlock>(std::move(cont_blk_label));
|
|
|
+ builder.SetInsertPoint(&*cont_blk_ptr);
|
|
|
+ // Add (previously created) search index increment now.
|
|
|
+ (void)builder.AddInstruction(std::move(idx_inc_inst));
|
|
|
+ // Load next buffer address from debug input buffer
|
|
|
+ uint32_t ibuf_id = GetInputBufferId();
|
|
|
+ uint32_t ibuf_ptr_id = GetInputBufferPtrId();
|
|
|
+ Instruction* uptr_ac_inst = builder.AddTernaryOp(
|
|
|
+ ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
|
|
|
+ builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id);
|
|
|
+ uint32_t ibuf_type_id = GetInputBufferTypeId();
|
|
|
+ Instruction* uptr_load_inst =
|
|
|
+ builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, uptr_ac_inst->result_id());
|
|
|
+ // If loaded address greater than ref_ptr arg, break, else branch back to
|
|
|
+ // loop header
|
|
|
+ Instruction* uptr_test_inst =
|
|
|
+ builder.AddBinaryOp(GetBoolId(), SpvOpUGreaterThan,
|
|
|
+ uptr_load_inst->result_id(), param_vec[0]);
|
|
|
+ (void)builder.AddConditionalBranch(uptr_test_inst->result_id(),
|
|
|
+ bound_test_blk_id, hdr_blk_id,
|
|
|
+ kInvalidId, SpvSelectionControlMaskNone);
|
|
|
+ cont_blk_ptr->SetParent(&*input_func);
|
|
|
+ input_func->AddBasicBlock(std::move(cont_blk_ptr));
|
|
|
+ // Bounds test block. Read length of selected buffer and test that
|
|
|
+ // all len arg bytes are in buffer.
|
|
|
+ std::unique_ptr<BasicBlock> bound_test_blk_ptr =
|
|
|
+ MakeUnique<BasicBlock>(std::move(bound_test_blk_label));
|
|
|
+ builder.SetInsertPoint(&*bound_test_blk_ptr);
|
|
|
+ // Decrement index to point to previous/candidate buffer address
|
|
|
+ Instruction* cand_idx_inst = builder.AddBinaryOp(
|
|
|
+ GetUintId(), SpvOpISub, idx_inc_id, builder.GetUintConstantId(1u));
|
|
|
+ // Load candidate buffer address
|
|
|
+ Instruction* cand_ac_inst =
|
|
|
+ builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
|
|
|
+ builder.GetUintConstantId(kDebugInputDataOffset),
|
|
|
+ cand_idx_inst->result_id());
|
|
|
+ Instruction* cand_load_inst =
|
|
|
+ builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, cand_ac_inst->result_id());
|
|
|
+ // Compute offset of ref_ptr from candidate buffer address
|
|
|
+ Instruction* offset_inst = builder.AddBinaryOp(
|
|
|
+ ibuf_type_id, SpvOpISub, param_vec[0], cand_load_inst->result_id());
|
|
|
+ // Convert ref length to uint64
|
|
|
+ Instruction* ref_len_64_inst =
|
|
|
+ builder.AddUnaryOp(ibuf_type_id, SpvOpUConvert, param_vec[1]);
|
|
|
+ // Add ref length to ref offset to compute end of reference
|
|
|
+ Instruction* ref_end_inst =
|
|
|
+ builder.AddBinaryOp(ibuf_type_id, SpvOpIAdd, offset_inst->result_id(),
|
|
|
+ ref_len_64_inst->result_id());
|
|
|
+ // Load starting index of lengths in input buffer and convert to uint32
|
|
|
+ Instruction* len_start_ac_inst =
|
|
|
+ builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
|
|
|
+ builder.GetUintConstantId(kDebugInputDataOffset),
|
|
|
+ builder.GetUintConstantId(0u));
|
|
|
+ Instruction* len_start_load_inst = builder.AddUnaryOp(
|
|
|
+ ibuf_type_id, SpvOpLoad, len_start_ac_inst->result_id());
|
|
|
+ Instruction* len_start_32_inst = builder.AddUnaryOp(
|
|
|
+ GetUintId(), SpvOpUConvert, len_start_load_inst->result_id());
|
|
|
+ // Decrement search index to get candidate buffer length index
|
|
|
+ Instruction* cand_len_idx_inst =
|
|
|
+ builder.AddBinaryOp(GetUintId(), SpvOpISub, cand_idx_inst->result_id(),
|
|
|
+ builder.GetUintConstantId(1u));
|
|
|
+ // Add candidate length index to start index
|
|
|
+ Instruction* len_idx_inst = builder.AddBinaryOp(
|
|
|
+ GetUintId(), SpvOpIAdd, cand_len_idx_inst->result_id(),
|
|
|
+ len_start_32_inst->result_id());
|
|
|
+ // Load candidate buffer length
|
|
|
+ Instruction* len_ac_inst =
|
|
|
+ builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id,
|
|
|
+ builder.GetUintConstantId(kDebugInputDataOffset),
|
|
|
+ len_idx_inst->result_id());
|
|
|
+ Instruction* len_load_inst =
|
|
|
+ builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, len_ac_inst->result_id());
|
|
|
+ // Test if reference end within candidate buffer length
|
|
|
+ Instruction* len_test_inst = builder.AddBinaryOp(
|
|
|
+ GetBoolId(), SpvOpULessThanEqual, ref_end_inst->result_id(),
|
|
|
+ len_load_inst->result_id());
|
|
|
+ // Return test result
|
|
|
+ (void)builder.AddInstruction(MakeUnique<Instruction>(
|
|
|
+ context(), SpvOpReturnValue, 0, 0,
|
|
|
+ std::initializer_list<Operand>{
|
|
|
+ {SPV_OPERAND_TYPE_ID, {len_test_inst->result_id()}}}));
|
|
|
+ // Close block
|
|
|
+ bound_test_blk_ptr->SetParent(&*input_func);
|
|
|
+ input_func->AddBasicBlock(std::move(bound_test_blk_ptr));
|
|
|
+ // Close function and add function to module
|
|
|
+ std::unique_ptr<Instruction> func_end_inst(
|
|
|
+ new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {}));
|
|
|
+ get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
|
|
|
+ input_func->SetFunctionEnd(std::move(func_end_inst));
|
|
|
+ context()->AddFunction(std::move(input_func));
|
|
|
+ }
|
|
|
+ return search_test_func_id_;
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst,
|
|
|
+ InstructionBuilder* builder,
|
|
|
+ uint32_t* ref_uptr_id) {
|
|
|
+ // Enable Int64 if necessary
|
|
|
+ if (!get_feature_mgr()->HasCapability(SpvCapabilityInt64)) {
|
|
|
+ std::unique_ptr<Instruction> cap_int64_inst(new Instruction(
|
|
|
+ context(), SpvOpCapability, 0, 0,
|
|
|
+ std::initializer_list<Operand>{
|
|
|
+ {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityInt64}}}));
|
|
|
+ get_def_use_mgr()->AnalyzeInstDefUse(&*cap_int64_inst);
|
|
|
+ get_module()->AddCapability(std::move(cap_int64_inst));
|
|
|
+ }
|
|
|
+ // Convert reference pointer to uint64
|
|
|
+ uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
|
|
|
+ Instruction* ref_uptr_inst =
|
|
|
+ builder->AddUnaryOp(GetUint64Id(), SpvOpConvertPtrToU, ref_ptr_id);
|
|
|
+ *ref_uptr_id = ref_uptr_inst->result_id();
|
|
|
+ // Compute reference length in bytes
|
|
|
+ analysis::DefUseManager* du_mgr = get_def_use_mgr();
|
|
|
+ Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id);
|
|
|
+ uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
|
|
|
+ Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id);
|
|
|
+ uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
|
|
|
+ uint32_t ref_len_id = builder->GetUintConstantId(ref_len);
|
|
|
+ // Gen call to search and test function
|
|
|
+ const std::vector<uint32_t> args = {GetSearchAndTestFuncId(), *ref_uptr_id,
|
|
|
+ ref_len_id};
|
|
|
+ Instruction* call_inst =
|
|
|
+ builder->AddNaryOp(GetBoolId(), SpvOpFunctionCall, args);
|
|
|
+ uint32_t retval = call_inst->result_id();
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+void InstBuffAddrCheckPass::GenBuffAddrCheckCode(
|
|
|
+ BasicBlock::iterator ref_inst_itr,
|
|
|
+ UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
|
|
|
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
|
+ // Look for reference through indexed descriptor. If found, analyze and
|
|
|
+ // save components. If not, return.
|
|
|
+ Instruction* ref_inst = &*ref_inst_itr;
|
|
|
+ if (!IsPhysicalBuffAddrReference(ref_inst)) return;
|
|
|
+ // Move original block's preceding instructions into first new block
|
|
|
+ std::unique_ptr<BasicBlock> new_blk_ptr;
|
|
|
+ MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
|
|
|
+ InstructionBuilder builder(
|
|
|
+ context(), &*new_blk_ptr,
|
|
|
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
|
|
+ new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
+ uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef);
|
|
|
+ // Generate code to do search and test if all bytes of reference
|
|
|
+ // are within a listed buffer. Return reference pointer converted to uint64.
|
|
|
+ uint32_t ref_uptr_id;
|
|
|
+ uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id);
|
|
|
+ // Generate test of search results with true branch
|
|
|
+ // being full reference and false branch being debug output and zero
|
|
|
+ // for the referenced value.
|
|
|
+ GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst,
|
|
|
+ new_blocks);
|
|
|
+ // Move original block's remaining code into remainder/merge block and add
|
|
|
+ // to new blocks
|
|
|
+ BasicBlock* back_blk_ptr = &*new_blocks->back();
|
|
|
+ MovePostludeCode(ref_block_itr, back_blk_ptr);
|
|
|
+}
|
|
|
+
|
|
|
+void InstBuffAddrCheckPass::InitInstBuffAddrCheck() {
|
|
|
+ // Initialize base class
|
|
|
+ InitializeInstrument();
|
|
|
+ // Initialize class
|
|
|
+ search_test_func_id_ = 0;
|
|
|
+}
|
|
|
+
|
|
|
+Pass::Status InstBuffAddrCheckPass::ProcessImpl() {
|
|
|
+ // Perform bindless bounds check on each entry point function in module
|
|
|
+ InstProcessFunction pfn =
|
|
|
+ [this](BasicBlock::iterator ref_inst_itr,
|
|
|
+ UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
|
|
|
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
|
+ return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
|
|
|
+ new_blocks);
|
|
|
+ };
|
|
|
+ bool modified = InstProcessEntryPointCallTree(pfn);
|
|
|
+ return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
|
|
+}
|
|
|
+
|
|
|
+Pass::Status InstBuffAddrCheckPass::Process() {
|
|
|
+ if (!get_feature_mgr()->HasCapability(
|
|
|
+ SpvCapabilityPhysicalStorageBufferAddressesEXT))
|
|
|
+ return Status::SuccessWithoutChange;
|
|
|
+ InitInstBuffAddrCheck();
|
|
|
+ return ProcessImpl();
|
|
|
+}
|
|
|
+
|
|
|
+} // namespace opt
|
|
|
+} // namespace spvtools
|