| 1 | // Copyright (c) 2019 The Khronos Group Inc. | 
|---|
| 2 | // Copyright (c) 2019 Valve Corporation | 
|---|
| 3 | // Copyright (c) 2019 LunarG Inc. | 
|---|
| 4 | // | 
|---|
| 5 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|---|
| 6 | // you may not use this file except in compliance with the License. | 
|---|
| 7 | // You may obtain a copy of the License at | 
|---|
| 8 | // | 
|---|
| 9 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|---|
| 10 | // | 
|---|
| 11 | // Unless required by applicable law or agreed to in writing, software | 
|---|
| 12 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|---|
| 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|---|
| 14 | // See the License for the specific language governing permissions and | 
|---|
| 15 | // limitations under the License. | 
|---|
| 16 |  | 
|---|
| 17 | #include "inst_buff_addr_check_pass.h" | 
|---|
| 18 |  | 
|---|
| 19 | namespace spvtools { | 
|---|
| 20 | namespace opt { | 
|---|
| 21 |  | 
|---|
| 22 | uint32_t InstBuffAddrCheckPass::CloneOriginalReference( | 
|---|
| 23 | Instruction* ref_inst, InstructionBuilder* builder) { | 
|---|
| 24 | // Clone original ref with new result id (if load) | 
|---|
| 25 | assert( | 
|---|
| 26 | (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) && | 
|---|
| 27 | "unexpected ref"); | 
|---|
| 28 | std::unique_ptr<Instruction> new_ref_inst(ref_inst->Clone(context())); | 
|---|
| 29 | uint32_t ref_result_id = ref_inst->result_id(); | 
|---|
| 30 | uint32_t new_ref_id = 0; | 
|---|
| 31 | if (ref_result_id != 0) { | 
|---|
| 32 | new_ref_id = TakeNextId(); | 
|---|
| 33 | new_ref_inst->SetResultId(new_ref_id); | 
|---|
| 34 | } | 
|---|
| 35 | // Register new reference and add to new block | 
|---|
| 36 | Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst)); | 
|---|
| 37 | uid2offset_[added_inst->unique_id()] = uid2offset_[ref_inst->unique_id()]; | 
|---|
| 38 | if (new_ref_id != 0) | 
|---|
| 39 | get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id); | 
|---|
| 40 | return new_ref_id; | 
|---|
| 41 | } | 
|---|
| 42 |  | 
|---|
| 43 | bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) { | 
|---|
| 44 | if (ref_inst->opcode() != SpvOpLoad && ref_inst->opcode() != SpvOpStore) | 
|---|
| 45 | return false; | 
|---|
| 46 | uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0); | 
|---|
| 47 | analysis::DefUseManager* du_mgr = get_def_use_mgr(); | 
|---|
| 48 | Instruction* ptr_inst = du_mgr->GetDef(ptr_id); | 
|---|
| 49 | if (ptr_inst->opcode() != SpvOpAccessChain) return false; | 
|---|
| 50 | uint32_t ptr_ty_id = ptr_inst->type_id(); | 
|---|
| 51 | Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id); | 
|---|
| 52 | if (ptr_ty_inst->GetSingleWordInOperand(0) != | 
|---|
| 53 | SpvStorageClassPhysicalStorageBufferEXT) | 
|---|
| 54 | return false; | 
|---|
| 55 | return true; | 
|---|
| 56 | } | 
|---|
| 57 |  | 
|---|
| 58 | // TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ?? | 
|---|
| 59 | void InstBuffAddrCheckPass::GenCheckCode( | 
|---|
| 60 | uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id, | 
|---|
| 61 | uint32_t stage_idx, Instruction* ref_inst, | 
|---|
| 62 | std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { | 
|---|
| 63 | BasicBlock* back_blk_ptr = &*new_blocks->back(); | 
|---|
| 64 | InstructionBuilder builder( | 
|---|
| 65 | context(), back_blk_ptr, | 
|---|
| 66 | IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); | 
|---|
| 67 | // Gen conditional branch on check_id. Valid branch generates original | 
|---|
| 68 | // reference. Invalid generates debug output and zero result (if needed). | 
|---|
| 69 | uint32_t merge_blk_id = TakeNextId(); | 
|---|
| 70 | uint32_t valid_blk_id = TakeNextId(); | 
|---|
| 71 | uint32_t invalid_blk_id = TakeNextId(); | 
|---|
| 72 | std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id)); | 
|---|
| 73 | std::unique_ptr<Instruction> valid_label(NewLabel(valid_blk_id)); | 
|---|
| 74 | std::unique_ptr<Instruction> invalid_label(NewLabel(invalid_blk_id)); | 
|---|
| 75 | (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id, | 
|---|
| 76 | merge_blk_id, SpvSelectionControlMaskNone); | 
|---|
| 77 | // Gen valid branch | 
|---|
| 78 | std::unique_ptr<BasicBlock> new_blk_ptr( | 
|---|
| 79 | new BasicBlock(std::move(valid_label))); | 
|---|
| 80 | builder.SetInsertPoint(&*new_blk_ptr); | 
|---|
| 81 | uint32_t new_ref_id = CloneOriginalReference(ref_inst, &builder); | 
|---|
| 82 | (void)builder.AddBranch(merge_blk_id); | 
|---|
| 83 | new_blocks->push_back(std::move(new_blk_ptr)); | 
|---|
| 84 | // Gen invalid block | 
|---|
| 85 | new_blk_ptr.reset(new BasicBlock(std::move(invalid_label))); | 
|---|
| 86 | builder.SetInsertPoint(&*new_blk_ptr); | 
|---|
| 87 | // Convert uptr from uint64 to 2 uint32 | 
|---|
| 88 | Instruction* lo_uptr_inst = | 
|---|
| 89 | builder.AddUnaryOp(GetUintId(), SpvOpUConvert, ref_uptr_id); | 
|---|
| 90 | Instruction* rshift_uptr_inst = | 
|---|
| 91 | builder.AddBinaryOp(GetUint64Id(), SpvOpShiftRightLogical, ref_uptr_id, | 
|---|
| 92 | builder.GetUintConstantId(32)); | 
|---|
| 93 | Instruction* hi_uptr_inst = builder.AddUnaryOp(GetUintId(), SpvOpUConvert, | 
|---|
| 94 | rshift_uptr_inst->result_id()); | 
|---|
| 95 | GenDebugStreamWrite( | 
|---|
| 96 | uid2offset_[ref_inst->unique_id()], stage_idx, | 
|---|
| 97 | {error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()}, | 
|---|
| 98 | &builder); | 
|---|
| 99 | // Gen zero for invalid load. If pointer type, need to convert uint64 | 
|---|
| 100 | // zero to pointer; cannot create ConstantNull of pointer type. | 
|---|
| 101 | uint32_t null_id = 0; | 
|---|
| 102 | if (new_ref_id != 0) { | 
|---|
| 103 | uint32_t ref_type_id = ref_inst->type_id(); | 
|---|
| 104 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 105 | analysis::Type* ref_type = type_mgr->GetType(ref_type_id); | 
|---|
| 106 | if (ref_type->AsPointer() != nullptr) { | 
|---|
| 107 | uint32_t null_u64_id = GetNullId(GetUint64Id()); | 
|---|
| 108 | Instruction* null_ptr_inst = | 
|---|
| 109 | builder.AddUnaryOp(ref_type_id, SpvOpConvertUToPtr, null_u64_id); | 
|---|
| 110 | null_id = null_ptr_inst->result_id(); | 
|---|
| 111 | } else { | 
|---|
| 112 | null_id = GetNullId(ref_type_id); | 
|---|
| 113 | } | 
|---|
| 114 | } | 
|---|
| 115 | (void)builder.AddBranch(merge_blk_id); | 
|---|
| 116 | new_blocks->push_back(std::move(new_blk_ptr)); | 
|---|
| 117 | // Gen merge block | 
|---|
| 118 | new_blk_ptr.reset(new BasicBlock(std::move(merge_label))); | 
|---|
| 119 | builder.SetInsertPoint(&*new_blk_ptr); | 
|---|
| 120 | // Gen phi of new reference and zero, if necessary, and replace the | 
|---|
| 121 | // result id of the original reference with that of the Phi. Kill original | 
|---|
| 122 | // reference. | 
|---|
| 123 | if (new_ref_id != 0) { | 
|---|
| 124 | Instruction* phi_inst = | 
|---|
| 125 | builder.AddPhi(ref_inst->type_id(), | 
|---|
| 126 | {new_ref_id, valid_blk_id, null_id, invalid_blk_id}); | 
|---|
| 127 | context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id()); | 
|---|
| 128 | } | 
|---|
| 129 | new_blocks->push_back(std::move(new_blk_ptr)); | 
|---|
| 130 | context()->KillInst(ref_inst); | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) { | 
|---|
| 134 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); | 
|---|
| 135 | switch (type_inst->opcode()) { | 
|---|
| 136 | case SpvOpTypeFloat: | 
|---|
| 137 | case SpvOpTypeInt: | 
|---|
| 138 | return type_inst->GetSingleWordInOperand(0) / 8u; | 
|---|
| 139 | case SpvOpTypeVector: | 
|---|
| 140 | case SpvOpTypeMatrix: | 
|---|
| 141 | return type_inst->GetSingleWordInOperand(1) * | 
|---|
| 142 | GetTypeLength(type_inst->GetSingleWordInOperand(0)); | 
|---|
| 143 | case SpvOpTypePointer: | 
|---|
| 144 | assert(type_inst->GetSingleWordInOperand(0) == | 
|---|
| 145 | SpvStorageClassPhysicalStorageBufferEXT && | 
|---|
| 146 | "unexpected pointer type"); | 
|---|
| 147 | return 8u; | 
|---|
| 148 | default: | 
|---|
| 149 | assert(false && "unexpected buffer reference type"); | 
|---|
| 150 | return 0; | 
|---|
| 151 | } | 
|---|
| 152 | } | 
|---|
| 153 |  | 
|---|
| 154 | void InstBuffAddrCheckPass::AddParam(uint32_t type_id, | 
|---|
| 155 | std::vector<uint32_t>* param_vec, | 
|---|
| 156 | std::unique_ptr<Function>* input_func) { | 
|---|
| 157 | uint32_t pid = TakeNextId(); | 
|---|
| 158 | param_vec->push_back(pid); | 
|---|
| 159 | std::unique_ptr<Instruction> param_inst(new Instruction( | 
|---|
| 160 | get_module()->context(), SpvOpFunctionParameter, type_id, pid, {})); | 
|---|
| 161 | get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); | 
|---|
| 162 | (*input_func)->AddParameter(std::move(param_inst)); | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() { | 
|---|
| 166 | if (search_test_func_id_ == 0) { | 
|---|
| 167 | // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)" | 
|---|
| 168 | // which searches input buffer for buffer which most likely contains the | 
|---|
| 169 | // pointer value |ref_ptr| and verifies that the entire reference of | 
|---|
| 170 | // length |len| bytes is contained in the buffer. | 
|---|
| 171 | search_test_func_id_ = TakeNextId(); | 
|---|
| 172 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); | 
|---|
| 173 | std::vector<const analysis::Type*> param_types = { | 
|---|
| 174 | type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())}; | 
|---|
| 175 | analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types); | 
|---|
| 176 | analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); | 
|---|
| 177 | std::unique_ptr<Instruction> func_inst( | 
|---|
| 178 | new Instruction(get_module()->context(), SpvOpFunction, GetBoolId(), | 
|---|
| 179 | search_test_func_id_, | 
|---|
| 180 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, | 
|---|
| 181 | {SpvFunctionControlMaskNone}}, | 
|---|
| 182 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, | 
|---|
| 183 | {type_mgr->GetTypeInstruction(reg_func_ty)}}})); | 
|---|
| 184 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); | 
|---|
| 185 | std::unique_ptr<Function> input_func = | 
|---|
| 186 | MakeUnique<Function>(std::move(func_inst)); | 
|---|
| 187 | std::vector<uint32_t> param_vec; | 
|---|
| 188 | // Add ref_ptr and length parameters | 
|---|
| 189 | AddParam(GetUint64Id(), ¶m_vec, &input_func); | 
|---|
| 190 | AddParam(GetUintId(), ¶m_vec, &input_func); | 
|---|
| 191 | // Empty first block. | 
|---|
| 192 | uint32_t first_blk_id = TakeNextId(); | 
|---|
| 193 | std::unique_ptr<Instruction> first_blk_label(NewLabel(first_blk_id)); | 
|---|
| 194 | std::unique_ptr<BasicBlock> first_blk_ptr = | 
|---|
| 195 | MakeUnique<BasicBlock>(std::move(first_blk_label)); | 
|---|
| 196 | InstructionBuilder builder( | 
|---|
| 197 | context(), &*first_blk_ptr, | 
|---|
| 198 | IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); | 
|---|
| 199 | uint32_t hdr_blk_id = TakeNextId(); | 
|---|
| 200 | // Branch to search loop header | 
|---|
| 201 | std::unique_ptr<Instruction> hdr_blk_label(NewLabel(hdr_blk_id)); | 
|---|
| 202 | (void)builder.AddInstruction(MakeUnique<Instruction>( | 
|---|
| 203 | context(), SpvOpBranch, 0, 0, | 
|---|
| 204 | std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {hdr_blk_id}}})); | 
|---|
| 205 | first_blk_ptr->SetParent(&*input_func); | 
|---|
| 206 | input_func->AddBasicBlock(std::move(first_blk_ptr)); | 
|---|
| 207 | // Linear search loop header block | 
|---|
| 208 | // TODO(greg-lunarg): Implement binary search | 
|---|
| 209 | std::unique_ptr<BasicBlock> hdr_blk_ptr = | 
|---|
| 210 | MakeUnique<BasicBlock>(std::move(hdr_blk_label)); | 
|---|
| 211 | builder.SetInsertPoint(&*hdr_blk_ptr); | 
|---|
| 212 | // Phi for search index. Starts with 1. | 
|---|
| 213 | uint32_t cont_blk_id = TakeNextId(); | 
|---|
| 214 | std::unique_ptr<Instruction> cont_blk_label(NewLabel(cont_blk_id)); | 
|---|
| 215 | // Deal with def-use cycle caused by search loop index computation. | 
|---|
| 216 | // Create Add and Phi instructions first, then do Def analysis on Add. | 
|---|
| 217 | // Add Phi and Add instructions and do Use analysis later. | 
|---|
| 218 | uint32_t idx_phi_id = TakeNextId(); | 
|---|
| 219 | uint32_t idx_inc_id = TakeNextId(); | 
|---|
| 220 | std::unique_ptr<Instruction> idx_inc_inst(new Instruction( | 
|---|
| 221 | context(), SpvOpIAdd, GetUintId(), idx_inc_id, | 
|---|
| 222 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}}, | 
|---|
| 223 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, | 
|---|
| 224 | {builder.GetUintConstantId(1u)}}})); | 
|---|
| 225 | std::unique_ptr<Instruction> idx_phi_inst(new Instruction( | 
|---|
| 226 | context(), SpvOpPhi, GetUintId(), idx_phi_id, | 
|---|
| 227 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, | 
|---|
| 228 | {builder.GetUintConstantId(1u)}}, | 
|---|
| 229 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}}, | 
|---|
| 230 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}}, | 
|---|
| 231 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}})); | 
|---|
| 232 | get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst); | 
|---|
| 233 | // Add (previously created) search index phi | 
|---|
| 234 | (void)builder.AddInstruction(std::move(idx_phi_inst)); | 
|---|
| 235 | // LoopMerge | 
|---|
| 236 | uint32_t bound_test_blk_id = TakeNextId(); | 
|---|
| 237 | std::unique_ptr<Instruction> bound_test_blk_label( | 
|---|
| 238 | NewLabel(bound_test_blk_id)); | 
|---|
| 239 | (void)builder.AddInstruction(MakeUnique<Instruction>( | 
|---|
| 240 | context(), SpvOpLoopMerge, 0, 0, | 
|---|
| 241 | std::initializer_list<Operand>{ | 
|---|
| 242 | {SPV_OPERAND_TYPE_ID, {bound_test_blk_id}}, | 
|---|
| 243 | {SPV_OPERAND_TYPE_ID, {cont_blk_id}}, | 
|---|
| 244 | {SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvLoopControlMaskNone}}})); | 
|---|
| 245 | // Branch to continue/work block | 
|---|
| 246 | (void)builder.AddInstruction(MakeUnique<Instruction>( | 
|---|
| 247 | context(), SpvOpBranch, 0, 0, | 
|---|
| 248 | std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cont_blk_id}}})); | 
|---|
| 249 | hdr_blk_ptr->SetParent(&*input_func); | 
|---|
| 250 | input_func->AddBasicBlock(std::move(hdr_blk_ptr)); | 
|---|
| 251 | // Continue/Work Block. Read next buffer pointer and break if greater | 
|---|
| 252 | // than ref_ptr arg. | 
|---|
| 253 | std::unique_ptr<BasicBlock> cont_blk_ptr = | 
|---|
| 254 | MakeUnique<BasicBlock>(std::move(cont_blk_label)); | 
|---|
| 255 | builder.SetInsertPoint(&*cont_blk_ptr); | 
|---|
| 256 | // Add (previously created) search index increment now. | 
|---|
| 257 | (void)builder.AddInstruction(std::move(idx_inc_inst)); | 
|---|
| 258 | // Load next buffer address from debug input buffer | 
|---|
| 259 | uint32_t ibuf_id = GetInputBufferId(); | 
|---|
| 260 | uint32_t ibuf_ptr_id = GetInputBufferPtrId(); | 
|---|
| 261 | Instruction* uptr_ac_inst = builder.AddTernaryOp( | 
|---|
| 262 | ibuf_ptr_id, SpvOpAccessChain, ibuf_id, | 
|---|
| 263 | builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id); | 
|---|
| 264 | uint32_t ibuf_type_id = GetInputBufferTypeId(); | 
|---|
| 265 | Instruction* uptr_load_inst = | 
|---|
| 266 | builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, uptr_ac_inst->result_id()); | 
|---|
| 267 | // If loaded address greater than ref_ptr arg, break, else branch back to | 
|---|
| 268 | // loop header | 
|---|
| 269 | Instruction* uptr_test_inst = | 
|---|
| 270 | builder.AddBinaryOp(GetBoolId(), SpvOpUGreaterThan, | 
|---|
| 271 | uptr_load_inst->result_id(), param_vec[0]); | 
|---|
| 272 | (void)builder.AddConditionalBranch(uptr_test_inst->result_id(), | 
|---|
| 273 | bound_test_blk_id, hdr_blk_id, | 
|---|
| 274 | kInvalidId, SpvSelectionControlMaskNone); | 
|---|
| 275 | cont_blk_ptr->SetParent(&*input_func); | 
|---|
| 276 | input_func->AddBasicBlock(std::move(cont_blk_ptr)); | 
|---|
| 277 | // Bounds test block. Read length of selected buffer and test that | 
|---|
| 278 | // all len arg bytes are in buffer. | 
|---|
| 279 | std::unique_ptr<BasicBlock> bound_test_blk_ptr = | 
|---|
| 280 | MakeUnique<BasicBlock>(std::move(bound_test_blk_label)); | 
|---|
| 281 | builder.SetInsertPoint(&*bound_test_blk_ptr); | 
|---|
| 282 | // Decrement index to point to previous/candidate buffer address | 
|---|
| 283 | Instruction* cand_idx_inst = builder.AddBinaryOp( | 
|---|
| 284 | GetUintId(), SpvOpISub, idx_inc_id, builder.GetUintConstantId(1u)); | 
|---|
| 285 | // Load candidate buffer address | 
|---|
| 286 | Instruction* cand_ac_inst = | 
|---|
| 287 | builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id, | 
|---|
| 288 | builder.GetUintConstantId(kDebugInputDataOffset), | 
|---|
| 289 | cand_idx_inst->result_id()); | 
|---|
| 290 | Instruction* cand_load_inst = | 
|---|
| 291 | builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, cand_ac_inst->result_id()); | 
|---|
| 292 | // Compute offset of ref_ptr from candidate buffer address | 
|---|
| 293 | Instruction* offset_inst = builder.AddBinaryOp( | 
|---|
| 294 | ibuf_type_id, SpvOpISub, param_vec[0], cand_load_inst->result_id()); | 
|---|
| 295 | // Convert ref length to uint64 | 
|---|
| 296 | Instruction* ref_len_64_inst = | 
|---|
| 297 | builder.AddUnaryOp(ibuf_type_id, SpvOpUConvert, param_vec[1]); | 
|---|
| 298 | // Add ref length to ref offset to compute end of reference | 
|---|
| 299 | Instruction* ref_end_inst = | 
|---|
| 300 | builder.AddBinaryOp(ibuf_type_id, SpvOpIAdd, offset_inst->result_id(), | 
|---|
| 301 | ref_len_64_inst->result_id()); | 
|---|
| 302 | // Load starting index of lengths in input buffer and convert to uint32 | 
|---|
| 303 | Instruction* len_start_ac_inst = | 
|---|
| 304 | builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id, | 
|---|
| 305 | builder.GetUintConstantId(kDebugInputDataOffset), | 
|---|
| 306 | builder.GetUintConstantId(0u)); | 
|---|
| 307 | Instruction* len_start_load_inst = builder.AddUnaryOp( | 
|---|
| 308 | ibuf_type_id, SpvOpLoad, len_start_ac_inst->result_id()); | 
|---|
| 309 | Instruction* len_start_32_inst = builder.AddUnaryOp( | 
|---|
| 310 | GetUintId(), SpvOpUConvert, len_start_load_inst->result_id()); | 
|---|
| 311 | // Decrement search index to get candidate buffer length index | 
|---|
| 312 | Instruction* cand_len_idx_inst = | 
|---|
| 313 | builder.AddBinaryOp(GetUintId(), SpvOpISub, cand_idx_inst->result_id(), | 
|---|
| 314 | builder.GetUintConstantId(1u)); | 
|---|
| 315 | // Add candidate length index to start index | 
|---|
| 316 | Instruction* len_idx_inst = builder.AddBinaryOp( | 
|---|
| 317 | GetUintId(), SpvOpIAdd, cand_len_idx_inst->result_id(), | 
|---|
| 318 | len_start_32_inst->result_id()); | 
|---|
| 319 | // Load candidate buffer length | 
|---|
| 320 | Instruction* len_ac_inst = | 
|---|
| 321 | builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id, | 
|---|
| 322 | builder.GetUintConstantId(kDebugInputDataOffset), | 
|---|
| 323 | len_idx_inst->result_id()); | 
|---|
| 324 | Instruction* len_load_inst = | 
|---|
| 325 | builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, len_ac_inst->result_id()); | 
|---|
| 326 | // Test if reference end within candidate buffer length | 
|---|
| 327 | Instruction* len_test_inst = builder.AddBinaryOp( | 
|---|
| 328 | GetBoolId(), SpvOpULessThanEqual, ref_end_inst->result_id(), | 
|---|
| 329 | len_load_inst->result_id()); | 
|---|
| 330 | // Return test result | 
|---|
| 331 | (void)builder.AddInstruction(MakeUnique<Instruction>( | 
|---|
| 332 | context(), SpvOpReturnValue, 0, 0, | 
|---|
| 333 | std::initializer_list<Operand>{ | 
|---|
| 334 | {SPV_OPERAND_TYPE_ID, {len_test_inst->result_id()}}})); | 
|---|
| 335 | // Close block | 
|---|
| 336 | bound_test_blk_ptr->SetParent(&*input_func); | 
|---|
| 337 | input_func->AddBasicBlock(std::move(bound_test_blk_ptr)); | 
|---|
| 338 | // Close function and add function to module | 
|---|
| 339 | std::unique_ptr<Instruction> func_end_inst( | 
|---|
| 340 | new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); | 
|---|
| 341 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); | 
|---|
| 342 | input_func->SetFunctionEnd(std::move(func_end_inst)); | 
|---|
| 343 | context()->AddFunction(std::move(input_func)); | 
|---|
| 344 | } | 
|---|
| 345 | return search_test_func_id_; | 
|---|
| 346 | } | 
|---|
| 347 |  | 
|---|
| 348 | uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst, | 
|---|
| 349 | InstructionBuilder* builder, | 
|---|
| 350 | uint32_t* ref_uptr_id) { | 
|---|
| 351 | // Enable Int64 if necessary | 
|---|
| 352 | if (!get_feature_mgr()->HasCapability(SpvCapabilityInt64)) { | 
|---|
| 353 | std::unique_ptr<Instruction> cap_int64_inst(new Instruction( | 
|---|
| 354 | context(), SpvOpCapability, 0, 0, | 
|---|
| 355 | std::initializer_list<Operand>{ | 
|---|
| 356 | {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityInt64}}})); | 
|---|
| 357 | get_def_use_mgr()->AnalyzeInstDefUse(&*cap_int64_inst); | 
|---|
| 358 | context()->AddCapability(std::move(cap_int64_inst)); | 
|---|
| 359 | } | 
|---|
| 360 | // Convert reference pointer to uint64 | 
|---|
| 361 | uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0); | 
|---|
| 362 | Instruction* ref_uptr_inst = | 
|---|
| 363 | builder->AddUnaryOp(GetUint64Id(), SpvOpConvertPtrToU, ref_ptr_id); | 
|---|
| 364 | *ref_uptr_id = ref_uptr_inst->result_id(); | 
|---|
| 365 | // Compute reference length in bytes | 
|---|
| 366 | analysis::DefUseManager* du_mgr = get_def_use_mgr(); | 
|---|
| 367 | Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id); | 
|---|
| 368 | uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id(); | 
|---|
| 369 | Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id); | 
|---|
| 370 | uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1)); | 
|---|
| 371 | uint32_t ref_len_id = builder->GetUintConstantId(ref_len); | 
|---|
| 372 | // Gen call to search and test function | 
|---|
| 373 | const std::vector<uint32_t> args = {GetSearchAndTestFuncId(), *ref_uptr_id, | 
|---|
| 374 | ref_len_id}; | 
|---|
| 375 | Instruction* call_inst = | 
|---|
| 376 | builder->AddNaryOp(GetBoolId(), SpvOpFunctionCall, args); | 
|---|
| 377 | uint32_t retval = call_inst->result_id(); | 
|---|
| 378 | return retval; | 
|---|
| 379 | } | 
|---|
| 380 |  | 
|---|
| 381 | void InstBuffAddrCheckPass::GenBuffAddrCheckCode( | 
|---|
| 382 | BasicBlock::iterator ref_inst_itr, | 
|---|
| 383 | UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, | 
|---|
| 384 | std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { | 
|---|
| 385 | // Look for reference through indexed descriptor. If found, analyze and | 
|---|
| 386 | // save components. If not, return. | 
|---|
| 387 | Instruction* ref_inst = &*ref_inst_itr; | 
|---|
| 388 | if (!IsPhysicalBuffAddrReference(ref_inst)) return; | 
|---|
| 389 | // Move original block's preceding instructions into first new block | 
|---|
| 390 | std::unique_ptr<BasicBlock> new_blk_ptr; | 
|---|
| 391 | MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); | 
|---|
| 392 | InstructionBuilder builder( | 
|---|
| 393 | context(), &*new_blk_ptr, | 
|---|
| 394 | IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); | 
|---|
| 395 | new_blocks->push_back(std::move(new_blk_ptr)); | 
|---|
| 396 | uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef); | 
|---|
| 397 | // Generate code to do search and test if all bytes of reference | 
|---|
| 398 | // are within a listed buffer. Return reference pointer converted to uint64. | 
|---|
| 399 | uint32_t ref_uptr_id; | 
|---|
| 400 | uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id); | 
|---|
| 401 | // Generate test of search results with true branch | 
|---|
| 402 | // being full reference and false branch being debug output and zero | 
|---|
| 403 | // for the referenced value. | 
|---|
| 404 | GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst, | 
|---|
| 405 | new_blocks); | 
|---|
| 406 | // Move original block's remaining code into remainder/merge block and add | 
|---|
| 407 | // to new blocks | 
|---|
| 408 | BasicBlock* back_blk_ptr = &*new_blocks->back(); | 
|---|
| 409 | MovePostludeCode(ref_block_itr, back_blk_ptr); | 
|---|
| 410 | } | 
|---|
| 411 |  | 
|---|
| 412 | void InstBuffAddrCheckPass::InitInstBuffAddrCheck() { | 
|---|
| 413 | // Initialize base class | 
|---|
| 414 | InitializeInstrument(); | 
|---|
| 415 | // Initialize class | 
|---|
| 416 | search_test_func_id_ = 0; | 
|---|
| 417 | } | 
|---|
| 418 |  | 
|---|
| 419 | Pass::Status InstBuffAddrCheckPass::ProcessImpl() { | 
|---|
| 420 | // Perform bindless bounds check on each entry point function in module | 
|---|
| 421 | InstProcessFunction pfn = | 
|---|
| 422 | [this](BasicBlock::iterator ref_inst_itr, | 
|---|
| 423 | UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, | 
|---|
| 424 | std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { | 
|---|
| 425 | return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx, | 
|---|
| 426 | new_blocks); | 
|---|
| 427 | }; | 
|---|
| 428 | bool modified = InstProcessEntryPointCallTree(pfn); | 
|---|
| 429 | return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; | 
|---|
| 430 | } | 
|---|
| 431 |  | 
|---|
| 432 | Pass::Status InstBuffAddrCheckPass::Process() { | 
|---|
| 433 | if (!get_feature_mgr()->HasCapability( | 
|---|
| 434 | SpvCapabilityPhysicalStorageBufferAddressesEXT)) | 
|---|
| 435 | return Status::SuccessWithoutChange; | 
|---|
| 436 | InitInstBuffAddrCheck(); | 
|---|
| 437 | return ProcessImpl(); | 
|---|
| 438 | } | 
|---|
| 439 |  | 
|---|
| 440 | }  // namespace opt | 
|---|
| 441 | }  // namespace spvtools | 
|---|
| 442 |  | 
|---|