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
22namespace {
23
24// Common Parameter Positions
25static const int kInstCommonParamInstIdx = 0;
26static const int kInstCommonParamCnt = 1;
27
28// Indices of operands in SPIR-V instructions
29static const int kEntryPointExecutionModelInIdx = 0;
30static const int kEntryPointFunctionIdInIdx = 1;
31
32} // anonymous namespace
33
34namespace spvtools {
35namespace opt {
36
37void 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
60void 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
84std::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
91uint32_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
99void 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
118void 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
137void 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
146uint32_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
154void 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
163void 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
269void 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
282uint32_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
292bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const {
293 return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage;
294}
295
296void 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
333void 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
356uint32_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
364uint32_t InstrumentPass::GetInputBufferTypeId() {
365 return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id()
366 : GetUintId();
367}
368
369uint32_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
377uint32_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
391uint32_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
403analysis::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
427analysis::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
433void 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
442uint32_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
490uint32_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
534uint32_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
544uint32_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
556uint32_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
566uint32_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
576uint32_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
586uint32_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
596uint32_t InstrumentPass::GetVec4UintId() {
597 if (v4uint_id_ == 0) v4uint_id_ = GetVecUintId(4u);
598 return v4uint_id_;
599}
600
601uint32_t InstrumentPass::GetVec3UintId() {
602 if (v3uint_id_ == 0) v3uint_id_ = GetVecUintId(3u);
603 return v3uint_id_;
604}
605
606uint32_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
616uint32_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
626uint32_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
739uint32_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
820bool 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
865bool 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
887bool 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
946void 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