1 | // Copyright (c) 2018 The Khronos Group Inc. |
2 | // Copyright (c) 2018 Valve Corporation |
3 | // Copyright (c) 2018 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 "instrument_pass.h" |
18 | |
19 | #include "source/cfa.h" |
20 | #include "source/spirv_constant.h" |
21 | |
22 | namespace { |
23 | |
24 | // Common Parameter Positions |
25 | static const int kInstCommonParamInstIdx = 0; |
26 | static const int kInstCommonParamCnt = 1; |
27 | |
28 | // Indices of operands in SPIR-V instructions |
29 | static const int kEntryPointExecutionModelInIdx = 0; |
30 | static const int kEntryPointFunctionIdInIdx = 1; |
31 | |
32 | } // anonymous namespace |
33 | |
34 | namespace spvtools { |
35 | namespace opt { |
36 | |
37 | void InstrumentPass::MovePreludeCode( |
38 | BasicBlock::iterator ref_inst_itr, |
39 | UptrVectorIterator<BasicBlock> ref_block_itr, |
40 | std::unique_ptr<BasicBlock>* new_blk_ptr) { |
41 | same_block_pre_.clear(); |
42 | same_block_post_.clear(); |
43 | // Initialize new block. Reuse label from original block. |
44 | new_blk_ptr->reset(new BasicBlock(std::move(ref_block_itr->GetLabel()))); |
45 | // Move contents of original ref block up to ref instruction. |
46 | for (auto cii = ref_block_itr->begin(); cii != ref_inst_itr; |
47 | cii = ref_block_itr->begin()) { |
48 | Instruction* inst = &*cii; |
49 | inst->RemoveFromList(); |
50 | std::unique_ptr<Instruction> mv_ptr(inst); |
51 | // Remember same-block ops for possible regeneration. |
52 | if (IsSameBlockOp(&*mv_ptr)) { |
53 | auto* sb_inst_ptr = mv_ptr.get(); |
54 | same_block_pre_[mv_ptr->result_id()] = sb_inst_ptr; |
55 | } |
56 | (*new_blk_ptr)->AddInstruction(std::move(mv_ptr)); |
57 | } |
58 | } |
59 | |
60 | void InstrumentPass::MovePostludeCode( |
61 | UptrVectorIterator<BasicBlock> ref_block_itr, BasicBlock* new_blk_ptr) { |
62 | // new_blk_ptr->reset(new BasicBlock(NewLabel(ref_block_itr->id()))); |
63 | // Move contents of original ref block. |
64 | for (auto cii = ref_block_itr->begin(); cii != ref_block_itr->end(); |
65 | cii = ref_block_itr->begin()) { |
66 | Instruction* inst = &*cii; |
67 | inst->RemoveFromList(); |
68 | std::unique_ptr<Instruction> mv_inst(inst); |
69 | // Regenerate any same-block instruction that has not been seen in the |
70 | // current block. |
71 | if (same_block_pre_.size() > 0) { |
72 | CloneSameBlockOps(&mv_inst, &same_block_post_, &same_block_pre_, |
73 | new_blk_ptr); |
74 | // Remember same-block ops in this block. |
75 | if (IsSameBlockOp(&*mv_inst)) { |
76 | const uint32_t rid = mv_inst->result_id(); |
77 | same_block_post_[rid] = rid; |
78 | } |
79 | } |
80 | new_blk_ptr->AddInstruction(std::move(mv_inst)); |
81 | } |
82 | } |
83 | |
84 | std::unique_ptr<Instruction> InstrumentPass::NewLabel(uint32_t label_id) { |
85 | std::unique_ptr<Instruction> newLabel( |
86 | new Instruction(context(), SpvOpLabel, 0, label_id, {})); |
87 | get_def_use_mgr()->AnalyzeInstDefUse(&*newLabel); |
88 | return newLabel; |
89 | } |
90 | |
91 | uint32_t InstrumentPass::GenUintCastCode(uint32_t val_id, |
92 | InstructionBuilder* builder) { |
93 | // Cast value to 32-bit unsigned if necessary |
94 | if (get_def_use_mgr()->GetDef(val_id)->type_id() == GetUintId()) |
95 | return val_id; |
96 | return builder->AddUnaryOp(GetUintId(), SpvOpBitcast, val_id)->result_id(); |
97 | } |
98 | |
99 | void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id, |
100 | uint32_t field_offset, |
101 | uint32_t field_value_id, |
102 | InstructionBuilder* builder) { |
103 | // Cast value to 32-bit unsigned if necessary |
104 | uint32_t val_id = GenUintCastCode(field_value_id, builder); |
105 | // Store value |
106 | Instruction* data_idx_inst = |
107 | builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id, |
108 | builder->GetUintConstantId(field_offset)); |
109 | uint32_t buf_id = GetOutputBufferId(); |
110 | uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); |
111 | Instruction* achain_inst = |
112 | builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, |
113 | builder->GetUintConstantId(kDebugOutputDataOffset), |
114 | data_idx_inst->result_id()); |
115 | (void)builder->AddBinaryOp(0, SpvOpStore, achain_inst->result_id(), val_id); |
116 | } |
117 | |
118 | void InstrumentPass::GenCommonStreamWriteCode(uint32_t record_sz, |
119 | uint32_t inst_id, |
120 | uint32_t stage_idx, |
121 | uint32_t base_offset_id, |
122 | InstructionBuilder* builder) { |
123 | // Store record size |
124 | GenDebugOutputFieldCode(base_offset_id, kInstCommonOutSize, |
125 | builder->GetUintConstantId(record_sz), builder); |
126 | // Store Shader Id |
127 | GenDebugOutputFieldCode(base_offset_id, kInstCommonOutShaderId, |
128 | builder->GetUintConstantId(shader_id_), builder); |
129 | // Store Instruction Idx |
130 | GenDebugOutputFieldCode(base_offset_id, kInstCommonOutInstructionIdx, inst_id, |
131 | builder); |
132 | // Store Stage Idx |
133 | GenDebugOutputFieldCode(base_offset_id, kInstCommonOutStageIdx, |
134 | builder->GetUintConstantId(stage_idx), builder); |
135 | } |
136 | |
137 | void InstrumentPass::GenFragCoordEltDebugOutputCode( |
138 | uint32_t base_offset_id, uint32_t uint_frag_coord_id, uint32_t element, |
139 | InstructionBuilder* builder) { |
140 | Instruction* element_val_inst = builder->AddIdLiteralOp( |
141 | GetUintId(), SpvOpCompositeExtract, uint_frag_coord_id, element); |
142 | GenDebugOutputFieldCode(base_offset_id, kInstFragOutFragCoordX + element, |
143 | element_val_inst->result_id(), builder); |
144 | } |
145 | |
146 | uint32_t InstrumentPass::GenVarLoad(uint32_t var_id, |
147 | InstructionBuilder* builder) { |
148 | Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); |
149 | uint32_t type_id = GetPointeeTypeId(var_inst); |
150 | Instruction* load_inst = builder->AddUnaryOp(type_id, SpvOpLoad, var_id); |
151 | return load_inst->result_id(); |
152 | } |
153 | |
154 | void InstrumentPass::GenBuiltinOutputCode(uint32_t builtin_id, |
155 | uint32_t builtin_off, |
156 | uint32_t base_offset_id, |
157 | InstructionBuilder* builder) { |
158 | // Load and store builtin |
159 | uint32_t load_id = GenVarLoad(builtin_id, builder); |
160 | GenDebugOutputFieldCode(base_offset_id, builtin_off, load_id, builder); |
161 | } |
162 | |
163 | void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx, |
164 | uint32_t base_offset_id, |
165 | InstructionBuilder* builder) { |
166 | // TODO(greg-lunarg): Add support for all stages |
167 | switch (stage_idx) { |
168 | case SpvExecutionModelVertex: { |
169 | // Load and store VertexId and InstanceId |
170 | GenBuiltinOutputCode( |
171 | context()->GetBuiltinInputVarId(SpvBuiltInVertexIndex), |
172 | kInstVertOutVertexIndex, base_offset_id, builder); |
173 | GenBuiltinOutputCode( |
174 | context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex), |
175 | kInstVertOutInstanceIndex, base_offset_id, builder); |
176 | } break; |
177 | case SpvExecutionModelGLCompute: { |
178 | // Load and store GlobalInvocationId. |
179 | uint32_t load_id = GenVarLoad( |
180 | context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId), |
181 | builder); |
182 | Instruction* x_inst = builder->AddIdLiteralOp( |
183 | GetUintId(), SpvOpCompositeExtract, load_id, 0); |
184 | Instruction* y_inst = builder->AddIdLiteralOp( |
185 | GetUintId(), SpvOpCompositeExtract, load_id, 1); |
186 | Instruction* z_inst = builder->AddIdLiteralOp( |
187 | GetUintId(), SpvOpCompositeExtract, load_id, 2); |
188 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX, |
189 | x_inst->result_id(), builder); |
190 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY, |
191 | y_inst->result_id(), builder); |
192 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ, |
193 | z_inst->result_id(), builder); |
194 | } break; |
195 | case SpvExecutionModelGeometry: { |
196 | // Load and store PrimitiveId and InvocationId. |
197 | GenBuiltinOutputCode( |
198 | context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), |
199 | kInstGeomOutPrimitiveId, base_offset_id, builder); |
200 | GenBuiltinOutputCode( |
201 | context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), |
202 | kInstGeomOutInvocationId, base_offset_id, builder); |
203 | } break; |
204 | case SpvExecutionModelTessellationControl: { |
205 | // Load and store InvocationId and PrimitiveId |
206 | GenBuiltinOutputCode( |
207 | context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), |
208 | kInstTessCtlOutInvocationId, base_offset_id, builder); |
209 | GenBuiltinOutputCode( |
210 | context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), |
211 | kInstTessCtlOutPrimitiveId, base_offset_id, builder); |
212 | } break; |
213 | case SpvExecutionModelTessellationEvaluation: { |
214 | // Load and store PrimitiveId and TessCoord.uv |
215 | GenBuiltinOutputCode( |
216 | context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), |
217 | kInstTessEvalOutPrimitiveId, base_offset_id, builder); |
218 | uint32_t load_id = GenVarLoad( |
219 | context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder); |
220 | Instruction* uvec3_cast_inst = |
221 | builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id); |
222 | uint32_t uvec3_cast_id = uvec3_cast_inst->result_id(); |
223 | Instruction* u_inst = builder->AddIdLiteralOp( |
224 | GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0); |
225 | Instruction* v_inst = builder->AddIdLiteralOp( |
226 | GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1); |
227 | GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU, |
228 | u_inst->result_id(), builder); |
229 | GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV, |
230 | v_inst->result_id(), builder); |
231 | } break; |
232 | case SpvExecutionModelFragment: { |
233 | // Load FragCoord and convert to Uint |
234 | Instruction* frag_coord_inst = builder->AddUnaryOp( |
235 | GetVec4FloatId(), SpvOpLoad, |
236 | context()->GetBuiltinInputVarId(SpvBuiltInFragCoord)); |
237 | Instruction* uint_frag_coord_inst = builder->AddUnaryOp( |
238 | GetVec4UintId(), SpvOpBitcast, frag_coord_inst->result_id()); |
239 | for (uint32_t u = 0; u < 2u; ++u) |
240 | GenFragCoordEltDebugOutputCode( |
241 | base_offset_id, uint_frag_coord_inst->result_id(), u, builder); |
242 | } break; |
243 | case SpvExecutionModelRayGenerationNV: |
244 | case SpvExecutionModelIntersectionNV: |
245 | case SpvExecutionModelAnyHitNV: |
246 | case SpvExecutionModelClosestHitNV: |
247 | case SpvExecutionModelMissNV: |
248 | case SpvExecutionModelCallableNV: { |
249 | // Load and store LaunchIdNV. |
250 | uint32_t launch_id = GenVarLoad( |
251 | context()->GetBuiltinInputVarId(SpvBuiltInLaunchIdNV), builder); |
252 | Instruction* x_launch_inst = builder->AddIdLiteralOp( |
253 | GetUintId(), SpvOpCompositeExtract, launch_id, 0); |
254 | Instruction* y_launch_inst = builder->AddIdLiteralOp( |
255 | GetUintId(), SpvOpCompositeExtract, launch_id, 1); |
256 | Instruction* z_launch_inst = builder->AddIdLiteralOp( |
257 | GetUintId(), SpvOpCompositeExtract, launch_id, 2); |
258 | GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdX, |
259 | x_launch_inst->result_id(), builder); |
260 | GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdY, |
261 | y_launch_inst->result_id(), builder); |
262 | GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdZ, |
263 | z_launch_inst->result_id(), builder); |
264 | } break; |
265 | default: { assert(false && "unsupported stage" ); } break; |
266 | } |
267 | } |
268 | |
269 | void InstrumentPass::GenDebugStreamWrite( |
270 | uint32_t instruction_idx, uint32_t stage_idx, |
271 | const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) { |
272 | // Call debug output function. Pass func_idx, instruction_idx and |
273 | // validation ids as args. |
274 | uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size()); |
275 | uint32_t output_func_id = GetStreamWriteFunctionId(stage_idx, val_id_cnt); |
276 | std::vector<uint32_t> args = {output_func_id, |
277 | builder->GetUintConstantId(instruction_idx)}; |
278 | (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end()); |
279 | (void)builder->AddNaryOp(GetVoidId(), SpvOpFunctionCall, args); |
280 | } |
281 | |
282 | uint32_t InstrumentPass::GenDebugDirectRead( |
283 | const std::vector<uint32_t>& offset_ids, InstructionBuilder* builder) { |
284 | // Call debug input function. Pass func_idx and offset ids as args. |
285 | uint32_t off_id_cnt = static_cast<uint32_t>(offset_ids.size()); |
286 | uint32_t input_func_id = GetDirectReadFunctionId(off_id_cnt); |
287 | std::vector<uint32_t> args = {input_func_id}; |
288 | (void)args.insert(args.end(), offset_ids.begin(), offset_ids.end()); |
289 | return builder->AddNaryOp(GetUintId(), SpvOpFunctionCall, args)->result_id(); |
290 | } |
291 | |
292 | bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const { |
293 | return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage; |
294 | } |
295 | |
296 | void InstrumentPass::CloneSameBlockOps( |
297 | std::unique_ptr<Instruction>* inst, |
298 | std::unordered_map<uint32_t, uint32_t>* same_blk_post, |
299 | std::unordered_map<uint32_t, Instruction*>* same_blk_pre, |
300 | BasicBlock* block_ptr) { |
301 | bool changed = false; |
302 | (*inst)->ForEachInId([&same_blk_post, &same_blk_pre, &block_ptr, &changed, |
303 | this](uint32_t* iid) { |
304 | const auto map_itr = (*same_blk_post).find(*iid); |
305 | if (map_itr == (*same_blk_post).end()) { |
306 | const auto map_itr2 = (*same_blk_pre).find(*iid); |
307 | if (map_itr2 != (*same_blk_pre).end()) { |
308 | // Clone pre-call same-block ops, map result id. |
309 | const Instruction* in_inst = map_itr2->second; |
310 | std::unique_ptr<Instruction> sb_inst(in_inst->Clone(context())); |
311 | const uint32_t rid = sb_inst->result_id(); |
312 | const uint32_t nid = this->TakeNextId(); |
313 | get_decoration_mgr()->CloneDecorations(rid, nid); |
314 | sb_inst->SetResultId(nid); |
315 | get_def_use_mgr()->AnalyzeInstDefUse(&*sb_inst); |
316 | (*same_blk_post)[rid] = nid; |
317 | *iid = nid; |
318 | changed = true; |
319 | CloneSameBlockOps(&sb_inst, same_blk_post, same_blk_pre, block_ptr); |
320 | block_ptr->AddInstruction(std::move(sb_inst)); |
321 | } |
322 | } else { |
323 | // Reset same-block op operand if necessary |
324 | if (*iid != map_itr->second) { |
325 | *iid = map_itr->second; |
326 | changed = true; |
327 | } |
328 | } |
329 | }); |
330 | if (changed) get_def_use_mgr()->AnalyzeInstUse(&**inst); |
331 | } |
332 | |
333 | void InstrumentPass::UpdateSucceedingPhis( |
334 | std::vector<std::unique_ptr<BasicBlock>>& new_blocks) { |
335 | const auto first_blk = new_blocks.begin(); |
336 | const auto last_blk = new_blocks.end() - 1; |
337 | const uint32_t first_id = (*first_blk)->id(); |
338 | const uint32_t last_id = (*last_blk)->id(); |
339 | const BasicBlock& const_last_block = *last_blk->get(); |
340 | const_last_block.ForEachSuccessorLabel( |
341 | [&first_id, &last_id, this](const uint32_t succ) { |
342 | BasicBlock* sbp = this->id2block_[succ]; |
343 | sbp->ForEachPhiInst([&first_id, &last_id, this](Instruction* phi) { |
344 | bool changed = false; |
345 | phi->ForEachInId([&first_id, &last_id, &changed](uint32_t* id) { |
346 | if (*id == first_id) { |
347 | *id = last_id; |
348 | changed = true; |
349 | } |
350 | }); |
351 | if (changed) get_def_use_mgr()->AnalyzeInstUse(phi); |
352 | }); |
353 | }); |
354 | } |
355 | |
356 | uint32_t InstrumentPass::GetOutputBufferPtrId() { |
357 | if (output_buffer_ptr_id_ == 0) { |
358 | output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( |
359 | GetUintId(), SpvStorageClassStorageBuffer); |
360 | } |
361 | return output_buffer_ptr_id_; |
362 | } |
363 | |
364 | uint32_t InstrumentPass::GetInputBufferTypeId() { |
365 | return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id() |
366 | : GetUintId(); |
367 | } |
368 | |
369 | uint32_t InstrumentPass::GetInputBufferPtrId() { |
370 | if (input_buffer_ptr_id_ == 0) { |
371 | input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( |
372 | GetInputBufferTypeId(), SpvStorageClassStorageBuffer); |
373 | } |
374 | return input_buffer_ptr_id_; |
375 | } |
376 | |
377 | uint32_t InstrumentPass::GetOutputBufferBinding() { |
378 | switch (validation_id_) { |
379 | case kInstValidationIdBindless: |
380 | return kDebugOutputBindingStream; |
381 | case kInstValidationIdBuffAddr: |
382 | return kDebugOutputBindingStream; |
383 | case kInstValidationIdDebugPrintf: |
384 | return kDebugOutputPrintfStream; |
385 | default: |
386 | assert(false && "unexpected validation id" ); |
387 | } |
388 | return 0; |
389 | } |
390 | |
391 | uint32_t InstrumentPass::GetInputBufferBinding() { |
392 | switch (validation_id_) { |
393 | case kInstValidationIdBindless: |
394 | return kDebugInputBindingBindless; |
395 | case kInstValidationIdBuffAddr: |
396 | return kDebugInputBindingBuffAddr; |
397 | default: |
398 | assert(false && "unexpected validation id" ); |
399 | } |
400 | return 0; |
401 | } |
402 | |
403 | analysis::Type* InstrumentPass::GetUintXRuntimeArrayType( |
404 | uint32_t width, analysis::Type** rarr_ty) { |
405 | if (*rarr_ty == nullptr) { |
406 | analysis::DecorationManager* deco_mgr = get_decoration_mgr(); |
407 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
408 | analysis::Integer uint_ty(width, false); |
409 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); |
410 | analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty); |
411 | *rarr_ty = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp); |
412 | uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(*rarr_ty); |
413 | // By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of |
414 | // a block, and will therefore be decorated with an ArrayStride. Therefore |
415 | // the undecorated type returned here will not be pre-existing and can |
416 | // safely be decorated. Since this type is now decorated, it is out of |
417 | // sync with the TypeManager and therefore the TypeManager must be |
418 | // invalidated after this pass. |
419 | assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 && |
420 | "used RuntimeArray type returned" ); |
421 | deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, |
422 | width / 8u); |
423 | } |
424 | return *rarr_ty; |
425 | } |
426 | |
427 | analysis::Type* InstrumentPass::GetUintRuntimeArrayType(uint32_t width) { |
428 | analysis::Type** rarr_ty = |
429 | (width == 64) ? &uint64_rarr_ty_ : &uint32_rarr_ty_; |
430 | return GetUintXRuntimeArrayType(width, rarr_ty); |
431 | } |
432 | |
433 | void InstrumentPass::AddStorageBufferExt() { |
434 | if (storage_buffer_ext_defined_) return; |
435 | if (!get_feature_mgr()->HasExtension(kSPV_KHR_storage_buffer_storage_class)) { |
436 | context()->AddExtension("SPV_KHR_storage_buffer_storage_class" ); |
437 | } |
438 | storage_buffer_ext_defined_ = true; |
439 | } |
440 | |
441 | // Return id for output buffer |
442 | uint32_t InstrumentPass::GetOutputBufferId() { |
443 | if (output_buffer_id_ == 0) { |
444 | // If not created yet, create one |
445 | analysis::DecorationManager* deco_mgr = get_decoration_mgr(); |
446 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
447 | analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(32); |
448 | analysis::Integer uint_ty(32, false); |
449 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); |
450 | analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty}); |
451 | analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); |
452 | uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); |
453 | // By the Vulkan spec, a pre-existing struct containing a RuntimeArray |
454 | // must be a block, and will therefore be decorated with Block. Therefore |
455 | // the undecorated type returned here will not be pre-existing and can |
456 | // safely be decorated. Since this type is now decorated, it is out of |
457 | // sync with the TypeManager and therefore the TypeManager must be |
458 | // invalidated after this pass. |
459 | assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 && |
460 | "used struct type returned" ); |
461 | deco_mgr->AddDecoration(obufTyId, SpvDecorationBlock); |
462 | deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset, |
463 | SpvDecorationOffset, 0); |
464 | deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset, |
465 | SpvDecorationOffset, 4); |
466 | uint32_t obufTyPtrId_ = |
467 | type_mgr->FindPointerToType(obufTyId, SpvStorageClassStorageBuffer); |
468 | output_buffer_id_ = TakeNextId(); |
469 | std::unique_ptr<Instruction> newVarOp(new Instruction( |
470 | context(), SpvOpVariable, obufTyPtrId_, output_buffer_id_, |
471 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, |
472 | {SpvStorageClassStorageBuffer}}})); |
473 | context()->AddGlobalValue(std::move(newVarOp)); |
474 | deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationDescriptorSet, |
475 | desc_set_); |
476 | deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding, |
477 | GetOutputBufferBinding()); |
478 | AddStorageBufferExt(); |
479 | if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { |
480 | // Add the new buffer to all entry points. |
481 | for (auto& entry : get_module()->entry_points()) { |
482 | entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}}); |
483 | context()->AnalyzeUses(&entry); |
484 | } |
485 | } |
486 | } |
487 | return output_buffer_id_; |
488 | } |
489 | |
490 | uint32_t InstrumentPass::GetInputBufferId() { |
491 | if (input_buffer_id_ == 0) { |
492 | // If not created yet, create one |
493 | analysis::DecorationManager* deco_mgr = get_decoration_mgr(); |
494 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
495 | uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u; |
496 | analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width); |
497 | analysis::Struct buf_ty({reg_uint_rarr_ty}); |
498 | analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); |
499 | uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); |
500 | // By the Vulkan spec, a pre-existing struct containing a RuntimeArray |
501 | // must be a block, and will therefore be decorated with Block. Therefore |
502 | // the undecorated type returned here will not be pre-existing and can |
503 | // safely be decorated. Since this type is now decorated, it is out of |
504 | // sync with the TypeManager and therefore the TypeManager must be |
505 | // invalidated after this pass. |
506 | assert(context()->get_def_use_mgr()->NumUses(ibufTyId) == 0 && |
507 | "used struct type returned" ); |
508 | deco_mgr->AddDecoration(ibufTyId, SpvDecorationBlock); |
509 | deco_mgr->AddMemberDecoration(ibufTyId, 0, SpvDecorationOffset, 0); |
510 | uint32_t ibufTyPtrId_ = |
511 | type_mgr->FindPointerToType(ibufTyId, SpvStorageClassStorageBuffer); |
512 | input_buffer_id_ = TakeNextId(); |
513 | std::unique_ptr<Instruction> newVarOp(new Instruction( |
514 | context(), SpvOpVariable, ibufTyPtrId_, input_buffer_id_, |
515 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, |
516 | {SpvStorageClassStorageBuffer}}})); |
517 | context()->AddGlobalValue(std::move(newVarOp)); |
518 | deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationDescriptorSet, |
519 | desc_set_); |
520 | deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding, |
521 | GetInputBufferBinding()); |
522 | AddStorageBufferExt(); |
523 | if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { |
524 | // Add the new buffer to all entry points. |
525 | for (auto& entry : get_module()->entry_points()) { |
526 | entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}}); |
527 | context()->AnalyzeUses(&entry); |
528 | } |
529 | } |
530 | } |
531 | return input_buffer_id_; |
532 | } |
533 | |
534 | uint32_t InstrumentPass::GetFloatId() { |
535 | if (float_id_ == 0) { |
536 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
537 | analysis::Float float_ty(32); |
538 | analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); |
539 | float_id_ = type_mgr->GetTypeInstruction(reg_float_ty); |
540 | } |
541 | return float_id_; |
542 | } |
543 | |
544 | uint32_t InstrumentPass::GetVec4FloatId() { |
545 | if (v4float_id_ == 0) { |
546 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
547 | analysis::Float float_ty(32); |
548 | analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); |
549 | analysis::Vector v4float_ty(reg_float_ty, 4); |
550 | analysis::Type* reg_v4float_ty = type_mgr->GetRegisteredType(&v4float_ty); |
551 | v4float_id_ = type_mgr->GetTypeInstruction(reg_v4float_ty); |
552 | } |
553 | return v4float_id_; |
554 | } |
555 | |
556 | uint32_t InstrumentPass::GetUintId() { |
557 | if (uint_id_ == 0) { |
558 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
559 | analysis::Integer uint_ty(32, false); |
560 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); |
561 | uint_id_ = type_mgr->GetTypeInstruction(reg_uint_ty); |
562 | } |
563 | return uint_id_; |
564 | } |
565 | |
566 | uint32_t InstrumentPass::GetUint64Id() { |
567 | if (uint64_id_ == 0) { |
568 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
569 | analysis::Integer uint64_ty(64, false); |
570 | analysis::Type* reg_uint64_ty = type_mgr->GetRegisteredType(&uint64_ty); |
571 | uint64_id_ = type_mgr->GetTypeInstruction(reg_uint64_ty); |
572 | } |
573 | return uint64_id_; |
574 | } |
575 | |
576 | uint32_t InstrumentPass::GetUint8Id() { |
577 | if (uint8_id_ == 0) { |
578 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
579 | analysis::Integer uint8_ty(8, false); |
580 | analysis::Type* reg_uint8_ty = type_mgr->GetRegisteredType(&uint8_ty); |
581 | uint8_id_ = type_mgr->GetTypeInstruction(reg_uint8_ty); |
582 | } |
583 | return uint8_id_; |
584 | } |
585 | |
586 | uint32_t InstrumentPass::GetVecUintId(uint32_t len) { |
587 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
588 | analysis::Integer uint_ty(32, false); |
589 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); |
590 | analysis::Vector v_uint_ty(reg_uint_ty, len); |
591 | analysis::Type* reg_v_uint_ty = type_mgr->GetRegisteredType(&v_uint_ty); |
592 | uint32_t v_uint_id = type_mgr->GetTypeInstruction(reg_v_uint_ty); |
593 | return v_uint_id; |
594 | } |
595 | |
596 | uint32_t InstrumentPass::GetVec4UintId() { |
597 | if (v4uint_id_ == 0) v4uint_id_ = GetVecUintId(4u); |
598 | return v4uint_id_; |
599 | } |
600 | |
601 | uint32_t InstrumentPass::GetVec3UintId() { |
602 | if (v3uint_id_ == 0) v3uint_id_ = GetVecUintId(3u); |
603 | return v3uint_id_; |
604 | } |
605 | |
606 | uint32_t InstrumentPass::GetBoolId() { |
607 | if (bool_id_ == 0) { |
608 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
609 | analysis::Bool bool_ty; |
610 | analysis::Type* reg_bool_ty = type_mgr->GetRegisteredType(&bool_ty); |
611 | bool_id_ = type_mgr->GetTypeInstruction(reg_bool_ty); |
612 | } |
613 | return bool_id_; |
614 | } |
615 | |
616 | uint32_t InstrumentPass::GetVoidId() { |
617 | if (void_id_ == 0) { |
618 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
619 | analysis::Void void_ty; |
620 | analysis::Type* reg_void_ty = type_mgr->GetRegisteredType(&void_ty); |
621 | void_id_ = type_mgr->GetTypeInstruction(reg_void_ty); |
622 | } |
623 | return void_id_; |
624 | } |
625 | |
626 | uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, |
627 | uint32_t val_spec_param_cnt) { |
628 | // Total param count is common params plus validation-specific |
629 | // params |
630 | uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt; |
631 | if (param2output_func_id_[param_cnt] == 0) { |
632 | // Create function |
633 | param2output_func_id_[param_cnt] = TakeNextId(); |
634 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
635 | std::vector<const analysis::Type*> param_types; |
636 | for (uint32_t c = 0; c < param_cnt; ++c) |
637 | param_types.push_back(type_mgr->GetType(GetUintId())); |
638 | analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types); |
639 | analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); |
640 | std::unique_ptr<Instruction> func_inst( |
641 | new Instruction(get_module()->context(), SpvOpFunction, GetVoidId(), |
642 | param2output_func_id_[param_cnt], |
643 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, |
644 | {SpvFunctionControlMaskNone}}, |
645 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, |
646 | {type_mgr->GetTypeInstruction(reg_func_ty)}}})); |
647 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); |
648 | std::unique_ptr<Function> output_func = |
649 | MakeUnique<Function>(std::move(func_inst)); |
650 | // Add parameters |
651 | std::vector<uint32_t> param_vec; |
652 | for (uint32_t c = 0; c < param_cnt; ++c) { |
653 | uint32_t pid = TakeNextId(); |
654 | param_vec.push_back(pid); |
655 | std::unique_ptr<Instruction> param_inst( |
656 | new Instruction(get_module()->context(), SpvOpFunctionParameter, |
657 | GetUintId(), pid, {})); |
658 | get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); |
659 | output_func->AddParameter(std::move(param_inst)); |
660 | } |
661 | // Create first block |
662 | uint32_t test_blk_id = TakeNextId(); |
663 | std::unique_ptr<Instruction> test_label(NewLabel(test_blk_id)); |
664 | std::unique_ptr<BasicBlock> new_blk_ptr = |
665 | MakeUnique<BasicBlock>(std::move(test_label)); |
666 | InstructionBuilder builder( |
667 | context(), &*new_blk_ptr, |
668 | IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); |
669 | // Gen test if debug output buffer size will not be exceeded. |
670 | uint32_t val_spec_offset = kInstStageOutCnt; |
671 | uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt; |
672 | uint32_t buf_id = GetOutputBufferId(); |
673 | uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); |
674 | Instruction* obuf_curr_sz_ac_inst = |
675 | builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, |
676 | builder.GetUintConstantId(kDebugOutputSizeOffset)); |
677 | // Fetch the current debug buffer written size atomically, adding the |
678 | // size of the record to be written. |
679 | uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz); |
680 | uint32_t mask_none_id = builder.GetUintConstantId(SpvMemoryAccessMaskNone); |
681 | uint32_t scope_invok_id = builder.GetUintConstantId(SpvScopeInvocation); |
682 | Instruction* obuf_curr_sz_inst = builder.AddQuadOp( |
683 | GetUintId(), SpvOpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(), |
684 | scope_invok_id, mask_none_id, obuf_record_sz_id); |
685 | uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id(); |
686 | // Compute new written size |
687 | Instruction* obuf_new_sz_inst = |
688 | builder.AddBinaryOp(GetUintId(), SpvOpIAdd, obuf_curr_sz_id, |
689 | builder.GetUintConstantId(obuf_record_sz)); |
690 | // Fetch the data bound |
691 | Instruction* obuf_bnd_inst = |
692 | builder.AddIdLiteralOp(GetUintId(), SpvOpArrayLength, |
693 | GetOutputBufferId(), kDebugOutputDataOffset); |
694 | // Test that new written size is less than or equal to debug output |
695 | // data bound |
696 | Instruction* obuf_safe_inst = builder.AddBinaryOp( |
697 | GetBoolId(), SpvOpULessThanEqual, obuf_new_sz_inst->result_id(), |
698 | obuf_bnd_inst->result_id()); |
699 | uint32_t merge_blk_id = TakeNextId(); |
700 | uint32_t write_blk_id = TakeNextId(); |
701 | std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id)); |
702 | std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id)); |
703 | (void)builder.AddConditionalBranch(obuf_safe_inst->result_id(), |
704 | write_blk_id, merge_blk_id, merge_blk_id, |
705 | SpvSelectionControlMaskNone); |
706 | // Close safety test block and gen write block |
707 | new_blk_ptr->SetParent(&*output_func); |
708 | output_func->AddBasicBlock(std::move(new_blk_ptr)); |
709 | new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label)); |
710 | builder.SetInsertPoint(&*new_blk_ptr); |
711 | // Generate common and stage-specific debug record members |
712 | GenCommonStreamWriteCode(obuf_record_sz, param_vec[kInstCommonParamInstIdx], |
713 | stage_idx, obuf_curr_sz_id, &builder); |
714 | GenStageStreamWriteCode(stage_idx, obuf_curr_sz_id, &builder); |
715 | // Gen writes of validation specific data |
716 | for (uint32_t i = 0; i < val_spec_param_cnt; ++i) { |
717 | GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i, |
718 | param_vec[kInstCommonParamCnt + i], &builder); |
719 | } |
720 | // Close write block and gen merge block |
721 | (void)builder.AddBranch(merge_blk_id); |
722 | new_blk_ptr->SetParent(&*output_func); |
723 | output_func->AddBasicBlock(std::move(new_blk_ptr)); |
724 | new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); |
725 | builder.SetInsertPoint(&*new_blk_ptr); |
726 | // Close merge block and function and add function to module |
727 | (void)builder.AddNullaryOp(0, SpvOpReturn); |
728 | new_blk_ptr->SetParent(&*output_func); |
729 | output_func->AddBasicBlock(std::move(new_blk_ptr)); |
730 | std::unique_ptr<Instruction> func_end_inst( |
731 | new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); |
732 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); |
733 | output_func->SetFunctionEnd(std::move(func_end_inst)); |
734 | context()->AddFunction(std::move(output_func)); |
735 | } |
736 | return param2output_func_id_[param_cnt]; |
737 | } |
738 | |
739 | uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) { |
740 | uint32_t func_id = param2input_func_id_[param_cnt]; |
741 | if (func_id != 0) return func_id; |
742 | // Create input function for param_cnt. |
743 | func_id = TakeNextId(); |
744 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
745 | std::vector<const analysis::Type*> param_types; |
746 | for (uint32_t c = 0; c < param_cnt; ++c) |
747 | param_types.push_back(type_mgr->GetType(GetUintId())); |
748 | uint32_t ibuf_type_id = GetInputBufferTypeId(); |
749 | analysis::Function func_ty(type_mgr->GetType(ibuf_type_id), param_types); |
750 | analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); |
751 | std::unique_ptr<Instruction> func_inst(new Instruction( |
752 | get_module()->context(), SpvOpFunction, ibuf_type_id, func_id, |
753 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, |
754 | {SpvFunctionControlMaskNone}}, |
755 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, |
756 | {type_mgr->GetTypeInstruction(reg_func_ty)}}})); |
757 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); |
758 | std::unique_ptr<Function> input_func = |
759 | MakeUnique<Function>(std::move(func_inst)); |
760 | // Add parameters |
761 | std::vector<uint32_t> param_vec; |
762 | for (uint32_t c = 0; c < param_cnt; ++c) { |
763 | uint32_t pid = TakeNextId(); |
764 | param_vec.push_back(pid); |
765 | std::unique_ptr<Instruction> param_inst(new Instruction( |
766 | get_module()->context(), SpvOpFunctionParameter, GetUintId(), pid, {})); |
767 | get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); |
768 | input_func->AddParameter(std::move(param_inst)); |
769 | } |
770 | // Create block |
771 | uint32_t blk_id = TakeNextId(); |
772 | std::unique_ptr<Instruction> blk_label(NewLabel(blk_id)); |
773 | std::unique_ptr<BasicBlock> new_blk_ptr = |
774 | MakeUnique<BasicBlock>(std::move(blk_label)); |
775 | InstructionBuilder builder( |
776 | context(), &*new_blk_ptr, |
777 | IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); |
778 | // For each offset parameter, generate new offset with parameter, adding last |
779 | // loaded value if it exists, and load value from input buffer at new offset. |
780 | // Return last loaded value. |
781 | uint32_t buf_id = GetInputBufferId(); |
782 | uint32_t buf_ptr_id = GetInputBufferPtrId(); |
783 | uint32_t last_value_id = 0; |
784 | for (uint32_t p = 0; p < param_cnt; ++p) { |
785 | uint32_t offset_id; |
786 | if (p == 0) { |
787 | offset_id = param_vec[0]; |
788 | } else { |
789 | if (ibuf_type_id != GetUintId()) { |
790 | Instruction* ucvt_inst = |
791 | builder.AddUnaryOp(GetUintId(), SpvOpUConvert, last_value_id); |
792 | last_value_id = ucvt_inst->result_id(); |
793 | } |
794 | Instruction* offset_inst = builder.AddBinaryOp( |
795 | GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]); |
796 | offset_id = offset_inst->result_id(); |
797 | } |
798 | Instruction* ac_inst = builder.AddTernaryOp( |
799 | buf_ptr_id, SpvOpAccessChain, buf_id, |
800 | builder.GetUintConstantId(kDebugInputDataOffset), offset_id); |
801 | Instruction* load_inst = |
802 | builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, ac_inst->result_id()); |
803 | last_value_id = load_inst->result_id(); |
804 | } |
805 | (void)builder.AddInstruction(MakeUnique<Instruction>( |
806 | context(), SpvOpReturnValue, 0, 0, |
807 | std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {last_value_id}}})); |
808 | // Close block and function and add function to module |
809 | new_blk_ptr->SetParent(&*input_func); |
810 | input_func->AddBasicBlock(std::move(new_blk_ptr)); |
811 | std::unique_ptr<Instruction> func_end_inst( |
812 | new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); |
813 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); |
814 | input_func->SetFunctionEnd(std::move(func_end_inst)); |
815 | context()->AddFunction(std::move(input_func)); |
816 | param2input_func_id_[param_cnt] = func_id; |
817 | return func_id; |
818 | } |
819 | |
820 | bool InstrumentPass::InstrumentFunction(Function* func, uint32_t stage_idx, |
821 | InstProcessFunction& pfn) { |
822 | bool modified = false; |
823 | // Compute function index |
824 | uint32_t function_idx = 0; |
825 | for (auto fii = get_module()->begin(); fii != get_module()->end(); ++fii) { |
826 | if (&*fii == func) break; |
827 | ++function_idx; |
828 | } |
829 | std::vector<std::unique_ptr<BasicBlock>> new_blks; |
830 | // Using block iterators here because of block erasures and insertions. |
831 | for (auto bi = func->begin(); bi != func->end(); ++bi) { |
832 | for (auto ii = bi->begin(); ii != bi->end();) { |
833 | // Generate instrumentation if warranted |
834 | pfn(ii, bi, stage_idx, &new_blks); |
835 | if (new_blks.size() == 0) { |
836 | ++ii; |
837 | continue; |
838 | } |
839 | // Add new blocks to label id map |
840 | for (auto& blk : new_blks) id2block_[blk->id()] = &*blk; |
841 | // If there are new blocks we know there will always be two or |
842 | // more, so update succeeding phis with label of new last block. |
843 | size_t newBlocksSize = new_blks.size(); |
844 | assert(newBlocksSize > 1); |
845 | UpdateSucceedingPhis(new_blks); |
846 | // Replace original block with new block(s) |
847 | bi = bi.Erase(); |
848 | for (auto& bb : new_blks) { |
849 | bb->SetParent(func); |
850 | } |
851 | bi = bi.InsertBefore(&new_blks); |
852 | // Reset block iterator to last new block |
853 | for (size_t i = 0; i < newBlocksSize - 1; i++) ++bi; |
854 | modified = true; |
855 | // Restart instrumenting at beginning of last new block, |
856 | // but skip over any new phi or copy instruction. |
857 | ii = bi->begin(); |
858 | if (ii->opcode() == SpvOpPhi || ii->opcode() == SpvOpCopyObject) ++ii; |
859 | new_blks.clear(); |
860 | } |
861 | } |
862 | return modified; |
863 | } |
864 | |
865 | bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn, |
866 | std::queue<uint32_t>* roots, |
867 | uint32_t stage_idx) { |
868 | bool modified = false; |
869 | std::unordered_set<uint32_t> done; |
870 | // Don't process input and output functions |
871 | for (auto& ifn : param2input_func_id_) done.insert(ifn.second); |
872 | for (auto& ofn : param2output_func_id_) done.insert(ofn.second); |
873 | // Process all functions from roots |
874 | while (!roots->empty()) { |
875 | const uint32_t fi = roots->front(); |
876 | roots->pop(); |
877 | if (done.insert(fi).second) { |
878 | Function* fn = id2function_.at(fi); |
879 | // Add calls first so we don't add new output function |
880 | context()->AddCalls(fn, roots); |
881 | modified = InstrumentFunction(fn, stage_idx, pfn) || modified; |
882 | } |
883 | } |
884 | return modified; |
885 | } |
886 | |
887 | bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { |
888 | // Check that format version 2 requested |
889 | if (version_ != 2u) { |
890 | if (consumer()) { |
891 | std::string message = "Unsupported instrumentation format requested" ; |
892 | consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); |
893 | } |
894 | return false; |
895 | } |
896 | // Make sure all entry points have the same execution model. Do not |
897 | // instrument if they do not. |
898 | // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module |
899 | // can contain entry points with different execution models, although |
900 | // such modules will likely be rare as GLSL and HLSL are geared toward |
901 | // one model per module. In such cases we will need |
902 | // to clone any functions which are in the call trees of entrypoints |
903 | // with differing execution models. |
904 | uint32_t ecnt = 0; |
905 | uint32_t stage = SpvExecutionModelMax; |
906 | for (auto& e : get_module()->entry_points()) { |
907 | if (ecnt == 0) |
908 | stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx); |
909 | else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != |
910 | stage) { |
911 | if (consumer()) { |
912 | std::string message = "Mixed stage shader module not supported" ; |
913 | consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); |
914 | } |
915 | return false; |
916 | } |
917 | ++ecnt; |
918 | } |
919 | // Check for supported stages |
920 | if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment && |
921 | stage != SpvExecutionModelGeometry && |
922 | stage != SpvExecutionModelGLCompute && |
923 | stage != SpvExecutionModelTessellationControl && |
924 | stage != SpvExecutionModelTessellationEvaluation && |
925 | stage != SpvExecutionModelRayGenerationNV && |
926 | stage != SpvExecutionModelIntersectionNV && |
927 | stage != SpvExecutionModelAnyHitNV && |
928 | stage != SpvExecutionModelClosestHitNV && |
929 | stage != SpvExecutionModelMissNV && |
930 | stage != SpvExecutionModelCallableNV) { |
931 | if (consumer()) { |
932 | std::string message = "Stage not supported by instrumentation" ; |
933 | consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); |
934 | } |
935 | return false; |
936 | } |
937 | // Add together the roots of all entry points |
938 | std::queue<uint32_t> roots; |
939 | for (auto& e : get_module()->entry_points()) { |
940 | roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); |
941 | } |
942 | bool modified = InstProcessCallTreeFromRoots(pfn, &roots, stage); |
943 | return modified; |
944 | } |
945 | |
946 | void InstrumentPass::InitializeInstrument() { |
947 | output_buffer_id_ = 0; |
948 | output_buffer_ptr_id_ = 0; |
949 | input_buffer_ptr_id_ = 0; |
950 | input_buffer_id_ = 0; |
951 | float_id_ = 0; |
952 | v4float_id_ = 0; |
953 | uint_id_ = 0; |
954 | uint64_id_ = 0; |
955 | uint8_id_ = 0; |
956 | v4uint_id_ = 0; |
957 | v3uint_id_ = 0; |
958 | bool_id_ = 0; |
959 | void_id_ = 0; |
960 | storage_buffer_ext_defined_ = false; |
961 | uint32_rarr_ty_ = nullptr; |
962 | uint64_rarr_ty_ = nullptr; |
963 | |
964 | // clear collections |
965 | id2function_.clear(); |
966 | id2block_.clear(); |
967 | |
968 | // clear maps |
969 | param2input_func_id_.clear(); |
970 | param2output_func_id_.clear(); |
971 | |
972 | // Initialize function and block maps. |
973 | for (auto& fn : *get_module()) { |
974 | id2function_[fn.result_id()] = &fn; |
975 | for (auto& blk : fn) { |
976 | id2block_[blk.id()] = &blk; |
977 | } |
978 | } |
979 | |
980 | // Remember original instruction offsets |
981 | uint32_t module_offset = 0; |
982 | Module* module = get_module(); |
983 | for (auto& i : context()->capabilities()) { |
984 | (void)i; |
985 | ++module_offset; |
986 | } |
987 | for (auto& i : module->extensions()) { |
988 | (void)i; |
989 | ++module_offset; |
990 | } |
991 | for (auto& i : module->ext_inst_imports()) { |
992 | (void)i; |
993 | ++module_offset; |
994 | } |
995 | ++module_offset; // memory_model |
996 | for (auto& i : module->entry_points()) { |
997 | (void)i; |
998 | ++module_offset; |
999 | } |
1000 | for (auto& i : module->execution_modes()) { |
1001 | (void)i; |
1002 | ++module_offset; |
1003 | } |
1004 | for (auto& i : module->debugs1()) { |
1005 | (void)i; |
1006 | ++module_offset; |
1007 | } |
1008 | for (auto& i : module->debugs2()) { |
1009 | (void)i; |
1010 | ++module_offset; |
1011 | } |
1012 | for (auto& i : module->debugs3()) { |
1013 | (void)i; |
1014 | ++module_offset; |
1015 | } |
1016 | for (auto& i : module->ext_inst_debuginfo()) { |
1017 | (void)i; |
1018 | ++module_offset; |
1019 | } |
1020 | for (auto& i : module->annotations()) { |
1021 | (void)i; |
1022 | ++module_offset; |
1023 | } |
1024 | for (auto& i : module->types_values()) { |
1025 | module_offset += 1; |
1026 | module_offset += static_cast<uint32_t>(i.dbg_line_insts().size()); |
1027 | } |
1028 | |
1029 | auto curr_fn = get_module()->begin(); |
1030 | for (; curr_fn != get_module()->end(); ++curr_fn) { |
1031 | // Count function instruction |
1032 | module_offset += 1; |
1033 | curr_fn->ForEachParam( |
1034 | [&module_offset](const Instruction*) { module_offset += 1; }, true); |
1035 | for (auto& blk : *curr_fn) { |
1036 | // Count label |
1037 | module_offset += 1; |
1038 | for (auto& inst : blk) { |
1039 | module_offset += static_cast<uint32_t>(inst.dbg_line_insts().size()); |
1040 | uid2offset_[inst.unique_id()] = module_offset; |
1041 | module_offset += 1; |
1042 | } |
1043 | } |
1044 | // Count function end instruction |
1045 | module_offset += 1; |
1046 | } |
1047 | } |
1048 | |
1049 | } // namespace opt |
1050 | } // namespace spvtools |
1051 | |