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 | |