blob: 6935a43dca1d34ba7c32d50101a40da32853f389 [file] [log] [blame]
Chris Forbescc5697f2019-01-30 11:54:08 -08001// 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
21namespace {
22
23// Common Parameter Positions
24static const int kInstCommonParamInstIdx = 0;
25static const int kInstCommonParamCnt = 1;
26
27// Indices of operands in SPIR-V instructions
28static const int kEntryPointExecutionModelInIdx = 0;
29static const int kEntryPointFunctionIdInIdx = 1;
30
31} // anonymous namespace
32
33namespace spvtools {
34namespace opt {
35
36void InstrumentPass::MovePreludeCode(
37 BasicBlock::iterator ref_inst_itr,
38 UptrVectorIterator<BasicBlock> ref_block_itr,
39 std::unique_ptr<BasicBlock>* new_blk_ptr) {
40 same_block_pre_.clear();
41 same_block_post_.clear();
42 // Initialize new block. Reuse label from original block.
43 new_blk_ptr->reset(new BasicBlock(std::move(ref_block_itr->GetLabel())));
44 // Move contents of original ref block up to ref instruction.
45 for (auto cii = ref_block_itr->begin(); cii != ref_inst_itr;
46 cii = ref_block_itr->begin()) {
47 Instruction* inst = &*cii;
48 inst->RemoveFromList();
49 std::unique_ptr<Instruction> mv_ptr(inst);
50 // Remember same-block ops for possible regeneration.
51 if (IsSameBlockOp(&*mv_ptr)) {
52 auto* sb_inst_ptr = mv_ptr.get();
53 same_block_pre_[mv_ptr->result_id()] = sb_inst_ptr;
54 }
55 (*new_blk_ptr)->AddInstruction(std::move(mv_ptr));
56 }
57}
58
59void InstrumentPass::MovePostludeCode(
60 UptrVectorIterator<BasicBlock> ref_block_itr,
61 std::unique_ptr<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 = GetOutputBufferUintPtrId();
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
146void InstrumentPass::GenBuiltinOutputCode(uint32_t builtin_id,
147 uint32_t builtin_off,
148 uint32_t base_offset_id,
149 InstructionBuilder* builder) {
150 // Load and store builtin
151 Instruction* load_inst =
152 builder->AddUnaryOp(GetUintId(), SpvOpLoad, builtin_id);
153 GenDebugOutputFieldCode(base_offset_id, builtin_off, load_inst->result_id(),
154 builder);
155}
156
157void InstrumentPass::GenUintNullOutputCode(uint32_t field_off,
158 uint32_t base_offset_id,
159 InstructionBuilder* builder) {
160 GenDebugOutputFieldCode(base_offset_id, field_off,
161 builder->GetNullId(GetUintId()), builder);
162}
163
164void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
165 uint32_t base_offset_id,
166 InstructionBuilder* builder) {
167 // TODO(greg-lunarg): Add support for all stages
168 switch (stage_idx) {
169 case SpvExecutionModelVertex: {
170 // Load and store VertexId and InstanceId
171 GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInVertexIndex),
172 kInstVertOutVertexIndex, base_offset_id, builder);
173 GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInInstanceIndex),
174 kInstVertOutInstanceIndex, base_offset_id, builder);
175 } break;
176 case SpvExecutionModelGLCompute: {
177 // Load and store GlobalInvocationId. Second word is unused; store zero.
178 GenBuiltinOutputCode(
179 context()->GetBuiltinVarId(SpvBuiltInGlobalInvocationId),
180 kInstCompOutGlobalInvocationId, base_offset_id, builder);
181 GenUintNullOutputCode(kInstCompOutUnused, base_offset_id, builder);
182 } break;
183 case SpvExecutionModelGeometry: {
184 // Load and store PrimitiveId and InvocationId.
185 GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInPrimitiveId),
186 kInstGeomOutPrimitiveId, base_offset_id, builder);
187 GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInInvocationId),
188 kInstGeomOutInvocationId, base_offset_id, builder);
189 } break;
190 case SpvExecutionModelTessellationControl:
191 case SpvExecutionModelTessellationEvaluation: {
192 // Load and store InvocationId. Second word is unused; store zero.
193 GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInInvocationId),
194 kInstTessOutInvocationId, base_offset_id, builder);
195 GenUintNullOutputCode(kInstTessOutUnused, base_offset_id, builder);
196 } break;
197 case SpvExecutionModelFragment: {
198 // Load FragCoord and convert to Uint
199 Instruction* frag_coord_inst =
200 builder->AddUnaryOp(GetVec4FloatId(), SpvOpLoad,
201 context()->GetBuiltinVarId(SpvBuiltInFragCoord));
202 Instruction* uint_frag_coord_inst = builder->AddUnaryOp(
203 GetVec4UintId(), SpvOpBitcast, frag_coord_inst->result_id());
204 for (uint32_t u = 0; u < 2u; ++u)
205 GenFragCoordEltDebugOutputCode(
206 base_offset_id, uint_frag_coord_inst->result_id(), u, builder);
207 } break;
208 default: { assert(false && "unsupported stage"); } break;
209 }
210}
211
212void InstrumentPass::GenDebugStreamWrite(
213 uint32_t instruction_idx, uint32_t stage_idx,
214 const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) {
215 // Call debug output function. Pass func_idx, instruction_idx and
216 // validation ids as args.
217 uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size());
218 uint32_t output_func_id = GetStreamWriteFunctionId(stage_idx, val_id_cnt);
219 std::vector<uint32_t> args = {output_func_id,
220 builder->GetUintConstantId(instruction_idx)};
221 (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end());
222 (void)builder->AddNaryOp(GetVoidId(), SpvOpFunctionCall, args);
223}
224
225bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const {
226 return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage;
227}
228
229void InstrumentPass::CloneSameBlockOps(
230 std::unique_ptr<Instruction>* inst,
231 std::unordered_map<uint32_t, uint32_t>* same_blk_post,
232 std::unordered_map<uint32_t, Instruction*>* same_blk_pre,
233 std::unique_ptr<BasicBlock>* block_ptr) {
234 (*inst)->ForEachInId(
235 [&same_blk_post, &same_blk_pre, &block_ptr, this](uint32_t* iid) {
236 const auto map_itr = (*same_blk_post).find(*iid);
237 if (map_itr == (*same_blk_post).end()) {
238 const auto map_itr2 = (*same_blk_pre).find(*iid);
239 if (map_itr2 != (*same_blk_pre).end()) {
240 // Clone pre-call same-block ops, map result id.
241 const Instruction* in_inst = map_itr2->second;
242 std::unique_ptr<Instruction> sb_inst(in_inst->Clone(context()));
243 CloneSameBlockOps(&sb_inst, same_blk_post, same_blk_pre, block_ptr);
244 const uint32_t rid = sb_inst->result_id();
245 const uint32_t nid = this->TakeNextId();
246 get_decoration_mgr()->CloneDecorations(rid, nid);
247 sb_inst->SetResultId(nid);
248 (*same_blk_post)[rid] = nid;
249 *iid = nid;
250 (*block_ptr)->AddInstruction(std::move(sb_inst));
251 }
252 } else {
253 // Reset same-block op operand.
254 *iid = map_itr->second;
255 }
256 });
257}
258
259void InstrumentPass::UpdateSucceedingPhis(
260 std::vector<std::unique_ptr<BasicBlock>>& new_blocks) {
261 const auto first_blk = new_blocks.begin();
262 const auto last_blk = new_blocks.end() - 1;
263 const uint32_t first_id = (*first_blk)->id();
264 const uint32_t last_id = (*last_blk)->id();
265 const BasicBlock& const_last_block = *last_blk->get();
266 const_last_block.ForEachSuccessorLabel(
267 [&first_id, &last_id, this](const uint32_t succ) {
268 BasicBlock* sbp = this->id2block_[succ];
269 sbp->ForEachPhiInst([&first_id, &last_id, this](Instruction* phi) {
270 bool changed = false;
271 phi->ForEachInId([&first_id, &last_id, &changed](uint32_t* id) {
272 if (*id == first_id) {
273 *id = last_id;
274 changed = true;
275 }
276 });
277 if (changed) get_def_use_mgr()->AnalyzeInstUse(phi);
278 });
279 });
280}
281
282// Return id for output buffer uint ptr type
283uint32_t InstrumentPass::GetOutputBufferUintPtrId() {
284 if (output_buffer_uint_ptr_id_ == 0) {
285 output_buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
286 GetUintId(), SpvStorageClassStorageBuffer);
287 }
288 return output_buffer_uint_ptr_id_;
289}
290
291uint32_t InstrumentPass::GetOutputBufferBinding() {
292 switch (validation_id_) {
293 case kInstValidationIdBindless:
294 return kDebugOutputBindingStream;
295 default:
296 assert(false && "unexpected validation id");
297 }
298 return 0;
299}
300
301// Return id for output buffer
302uint32_t InstrumentPass::GetOutputBufferId() {
303 if (output_buffer_id_ == 0) {
304 // If not created yet, create one
305 analysis::DecorationManager* deco_mgr = get_decoration_mgr();
306 analysis::TypeManager* type_mgr = context()->get_type_mgr();
307 analysis::Integer uint_ty(32, false);
308 analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
309 analysis::RuntimeArray uint_rarr_ty(reg_uint_ty);
310 analysis::Type* reg_uint_rarr_ty =
311 type_mgr->GetRegisteredType(&uint_rarr_ty);
312 uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(reg_uint_rarr_ty);
313 deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u);
314 analysis::Struct obuf_ty({reg_uint_ty, reg_uint_rarr_ty});
315 analysis::Type* reg_obuf_ty = type_mgr->GetRegisteredType(&obuf_ty);
316 uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_obuf_ty);
317 deco_mgr->AddDecoration(obufTyId, SpvDecorationBlock);
318 deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset,
319 SpvDecorationOffset, 0);
320 deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset,
321 SpvDecorationOffset, 4);
322 uint32_t obufTyPtrId_ =
323 type_mgr->FindPointerToType(obufTyId, SpvStorageClassStorageBuffer);
324 output_buffer_id_ = TakeNextId();
325 std::unique_ptr<Instruction> newVarOp(new Instruction(
326 context(), SpvOpVariable, obufTyPtrId_, output_buffer_id_,
327 {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
328 {SpvStorageClassStorageBuffer}}}));
329 context()->AddGlobalValue(std::move(newVarOp));
330 deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationDescriptorSet,
331 desc_set_);
332 deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding,
333 GetOutputBufferBinding());
334 // Look for storage buffer extension. If none, create one.
335 if (!get_feature_mgr()->HasExtension(
336 kSPV_KHR_storage_buffer_storage_class)) {
337 const std::string ext_name("SPV_KHR_storage_buffer_storage_class");
338 const auto num_chars = ext_name.size();
339 // Compute num words, accommodate the terminating null character.
340 const auto num_words = (num_chars + 1 + 3) / 4;
341 std::vector<uint32_t> ext_words(num_words, 0u);
342 std::memcpy(ext_words.data(), ext_name.data(), num_chars);
343 context()->AddExtension(std::unique_ptr<Instruction>(
344 new Instruction(context(), SpvOpExtension, 0u, 0u,
345 {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
346 }
347 }
348 return output_buffer_id_;
349}
350
351uint32_t InstrumentPass::GetVec4FloatId() {
352 if (v4float_id_ == 0) {
353 analysis::TypeManager* type_mgr = context()->get_type_mgr();
354 analysis::Float float_ty(32);
355 analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty);
356 analysis::Vector v4float_ty(reg_float_ty, 4);
357 analysis::Type* reg_v4float_ty = type_mgr->GetRegisteredType(&v4float_ty);
358 v4float_id_ = type_mgr->GetTypeInstruction(reg_v4float_ty);
359 }
360 return v4float_id_;
361}
362
363uint32_t InstrumentPass::GetUintId() {
364 if (uint_id_ == 0) {
365 analysis::TypeManager* type_mgr = context()->get_type_mgr();
366 analysis::Integer uint_ty(32, false);
367 analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
368 uint_id_ = type_mgr->GetTypeInstruction(reg_uint_ty);
369 }
370 return uint_id_;
371}
372
373uint32_t InstrumentPass::GetVec4UintId() {
374 if (v4uint_id_ == 0) {
375 analysis::TypeManager* type_mgr = context()->get_type_mgr();
376 analysis::Integer uint_ty(32, false);
377 analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
378 analysis::Vector v4uint_ty(reg_uint_ty, 4);
379 analysis::Type* reg_v4uint_ty = type_mgr->GetRegisteredType(&v4uint_ty);
380 v4uint_id_ = type_mgr->GetTypeInstruction(reg_v4uint_ty);
381 }
382 return v4uint_id_;
383}
384
385uint32_t InstrumentPass::GetBoolId() {
386 if (bool_id_ == 0) {
387 analysis::TypeManager* type_mgr = context()->get_type_mgr();
388 analysis::Bool bool_ty;
389 analysis::Type* reg_bool_ty = type_mgr->GetRegisteredType(&bool_ty);
390 bool_id_ = type_mgr->GetTypeInstruction(reg_bool_ty);
391 }
392 return bool_id_;
393}
394
395uint32_t InstrumentPass::GetVoidId() {
396 if (void_id_ == 0) {
397 analysis::TypeManager* type_mgr = context()->get_type_mgr();
398 analysis::Void void_ty;
399 analysis::Type* reg_void_ty = type_mgr->GetRegisteredType(&void_ty);
400 void_id_ = type_mgr->GetTypeInstruction(reg_void_ty);
401 }
402 return void_id_;
403}
404
405uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
406 uint32_t val_spec_param_cnt) {
407 // Total param count is common params plus validation-specific
408 // params
409 uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt;
410 if (output_func_id_ == 0) {
411 // Create function
412 output_func_id_ = TakeNextId();
413 analysis::TypeManager* type_mgr = context()->get_type_mgr();
414 std::vector<const analysis::Type*> param_types;
415 for (uint32_t c = 0; c < param_cnt; ++c)
416 param_types.push_back(type_mgr->GetType(GetUintId()));
417 analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types);
418 analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
419 std::unique_ptr<Instruction> func_inst(new Instruction(
420 get_module()->context(), SpvOpFunction, GetVoidId(), output_func_id_,
421 {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
422 {SpvFunctionControlMaskNone}},
423 {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
424 {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
425 get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
426 std::unique_ptr<Function> output_func =
427 MakeUnique<Function>(std::move(func_inst));
428 // Add parameters
429 std::vector<uint32_t> param_vec;
430 for (uint32_t c = 0; c < param_cnt; ++c) {
431 uint32_t pid = TakeNextId();
432 param_vec.push_back(pid);
433 std::unique_ptr<Instruction> param_inst(
434 new Instruction(get_module()->context(), SpvOpFunctionParameter,
435 GetUintId(), pid, {}));
436 get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
437 output_func->AddParameter(std::move(param_inst));
438 }
439 // Create first block
440 uint32_t test_blk_id = TakeNextId();
441 std::unique_ptr<Instruction> test_label(NewLabel(test_blk_id));
442 std::unique_ptr<BasicBlock> new_blk_ptr =
443 MakeUnique<BasicBlock>(std::move(test_label));
444 InstructionBuilder builder(
445 context(), &*new_blk_ptr,
446 IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
447 // Gen test if debug output buffer size will not be exceeded.
448 uint32_t obuf_record_sz = kInstStageOutCnt + val_spec_param_cnt;
449 uint32_t buf_id = GetOutputBufferId();
450 uint32_t buf_uint_ptr_id = GetOutputBufferUintPtrId();
451 Instruction* obuf_curr_sz_ac_inst =
452 builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
453 builder.GetUintConstantId(kDebugOutputSizeOffset));
454 // Fetch the current debug buffer written size atomically, adding the
455 // size of the record to be written.
456 uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz);
457 uint32_t mask_none_id = builder.GetUintConstantId(SpvMemoryAccessMaskNone);
458 uint32_t scope_invok_id = builder.GetUintConstantId(SpvScopeInvocation);
459 Instruction* obuf_curr_sz_inst = builder.AddQuadOp(
460 GetUintId(), SpvOpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(),
461 scope_invok_id, mask_none_id, obuf_record_sz_id);
462 uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id();
463 // Compute new written size
464 Instruction* obuf_new_sz_inst =
465 builder.AddBinaryOp(GetUintId(), SpvOpIAdd, obuf_curr_sz_id,
466 builder.GetUintConstantId(obuf_record_sz));
467 // Fetch the data bound
468 Instruction* obuf_bnd_inst =
469 builder.AddIdLiteralOp(GetUintId(), SpvOpArrayLength,
470 GetOutputBufferId(), kDebugOutputDataOffset);
471 // Test that new written size is less than or equal to debug output
472 // data bound
473 Instruction* obuf_safe_inst = builder.AddBinaryOp(
474 GetBoolId(), SpvOpULessThanEqual, obuf_new_sz_inst->result_id(),
475 obuf_bnd_inst->result_id());
476 uint32_t merge_blk_id = TakeNextId();
477 uint32_t write_blk_id = TakeNextId();
478 std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
479 std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id));
480 (void)builder.AddConditionalBranch(obuf_safe_inst->result_id(),
481 write_blk_id, merge_blk_id, merge_blk_id,
482 SpvSelectionControlMaskNone);
483 // Close safety test block and gen write block
484 new_blk_ptr->SetParent(&*output_func);
485 output_func->AddBasicBlock(std::move(new_blk_ptr));
486 new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label));
487 builder.SetInsertPoint(&*new_blk_ptr);
488 // Generate common and stage-specific debug record members
489 GenCommonStreamWriteCode(obuf_record_sz, param_vec[kInstCommonParamInstIdx],
490 stage_idx, obuf_curr_sz_id, &builder);
491 GenStageStreamWriteCode(stage_idx, obuf_curr_sz_id, &builder);
492 // Gen writes of validation specific data
493 for (uint32_t i = 0; i < val_spec_param_cnt; ++i) {
494 GenDebugOutputFieldCode(obuf_curr_sz_id, kInstStageOutCnt + i,
495 param_vec[kInstCommonParamCnt + i], &builder);
496 }
497 // Close write block and gen merge block
498 (void)builder.AddBranch(merge_blk_id);
499 new_blk_ptr->SetParent(&*output_func);
500 output_func->AddBasicBlock(std::move(new_blk_ptr));
501 new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
502 builder.SetInsertPoint(&*new_blk_ptr);
503 // Close merge block and function and add function to module
504 (void)builder.AddNullaryOp(0, SpvOpReturn);
505 new_blk_ptr->SetParent(&*output_func);
506 output_func->AddBasicBlock(std::move(new_blk_ptr));
507 std::unique_ptr<Instruction> func_end_inst(
508 new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {}));
509 get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
510 output_func->SetFunctionEnd(std::move(func_end_inst));
511 context()->AddFunction(std::move(output_func));
512 output_func_param_cnt_ = param_cnt;
513 }
514 assert(param_cnt == output_func_param_cnt_ && "bad arg count");
515 return output_func_id_;
516}
517
518bool InstrumentPass::InstrumentFunction(Function* func, uint32_t stage_idx,
519 InstProcessFunction& pfn) {
520 bool modified = false;
521 // Compute function index
522 uint32_t function_idx = 0;
523 for (auto fii = get_module()->begin(); fii != get_module()->end(); ++fii) {
524 if (&*fii == func) break;
525 ++function_idx;
526 }
527 std::vector<std::unique_ptr<BasicBlock>> new_blks;
528 // Start count after function instruction
529 uint32_t instruction_idx = funcIdx2offset_[function_idx] + 1;
530 // Using block iterators here because of block erasures and insertions.
531 for (auto bi = func->begin(); bi != func->end(); ++bi) {
532 // Count block's label
533 ++instruction_idx;
534 for (auto ii = bi->begin(); ii != bi->end(); ++instruction_idx) {
535 // Bump instruction count if debug instructions
536 instruction_idx += static_cast<uint32_t>(ii->dbg_line_insts().size());
537 // Generate instrumentation if warranted
538 pfn(ii, bi, instruction_idx, stage_idx, &new_blks);
539 if (new_blks.size() == 0) {
540 ++ii;
541 continue;
542 }
543 // If there are new blocks we know there will always be two or
544 // more, so update succeeding phis with label of new last block.
545 size_t newBlocksSize = new_blks.size();
546 assert(newBlocksSize > 1);
547 UpdateSucceedingPhis(new_blks);
548 // Replace original block with new block(s)
549 bi = bi.Erase();
550 for (auto& bb : new_blks) {
551 bb->SetParent(func);
552 }
553 bi = bi.InsertBefore(&new_blks);
554 // Reset block iterator to last new block
555 for (size_t i = 0; i < newBlocksSize - 1; i++) ++bi;
556 modified = true;
557 // Restart instrumenting at beginning of last new block,
558 // but skip over any new phi or copy instruction.
559 ii = bi->begin();
560 if (ii->opcode() == SpvOpPhi || ii->opcode() == SpvOpCopyObject) ++ii;
561 new_blks.clear();
562 }
563 }
564 return modified;
565}
566
567bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn,
568 std::queue<uint32_t>* roots,
569 uint32_t stage_idx) {
570 bool modified = false;
571 std::unordered_set<uint32_t> done;
572 // Process all functions from roots
573 while (!roots->empty()) {
574 const uint32_t fi = roots->front();
575 roots->pop();
576 if (done.insert(fi).second) {
577 Function* fn = id2function_.at(fi);
578 // Add calls first so we don't add new output function
579 context()->AddCalls(fn, roots);
580 modified = InstrumentFunction(fn, stage_idx, pfn) || modified;
581 }
582 }
583 return modified;
584}
585
586bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
587 // Make sure all entry points have the same execution model. Do not
588 // instrument if they do not.
589 // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module
590 // can contain entry points with different execution models, although
591 // such modules will likely be rare as GLSL and HLSL are geared toward
592 // one model per module. In such cases we will need
593 // to clone any functions which are in the call trees of entrypoints
594 // with differing execution models.
595 uint32_t ecnt = 0;
596 uint32_t stage = SpvExecutionModelMax;
597 for (auto& e : get_module()->entry_points()) {
598 if (ecnt == 0)
599 stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx);
600 else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != stage)
601 return false;
602 ++ecnt;
603 }
604 // Only supporting vertex, fragment and compute shaders at the moment.
605 // TODO(greg-lunarg): Handle all stages.
606 if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment &&
607 stage != SpvExecutionModelGeometry &&
608 stage != SpvExecutionModelGLCompute &&
609 stage != SpvExecutionModelTessellationControl &&
610 stage != SpvExecutionModelTessellationEvaluation)
611 return false;
612 // Add together the roots of all entry points
613 std::queue<uint32_t> roots;
614 for (auto& e : get_module()->entry_points()) {
615 roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx));
616 }
617 bool modified = InstProcessCallTreeFromRoots(pfn, &roots, stage);
618 return modified;
619}
620
621void InstrumentPass::InitializeInstrument() {
622 output_buffer_id_ = 0;
623 output_buffer_uint_ptr_id_ = 0;
624 output_func_id_ = 0;
625 output_func_param_cnt_ = 0;
626 v4float_id_ = 0;
627 uint_id_ = 0;
628 v4uint_id_ = 0;
629 bool_id_ = 0;
630 void_id_ = 0;
631
632 // clear collections
633 id2function_.clear();
634 id2block_.clear();
635
636 // Initialize function and block maps.
637 for (auto& fn : *get_module()) {
638 id2function_[fn.result_id()] = &fn;
639 for (auto& blk : fn) {
640 id2block_[blk.id()] = &blk;
641 }
642 }
643
644 // Calculate instruction offset of first function
645 uint32_t pre_func_size = 0;
646 Module* module = get_module();
647 for (auto& i : context()->capabilities()) {
648 (void)i;
649 ++pre_func_size;
650 }
651 for (auto& i : module->extensions()) {
652 (void)i;
653 ++pre_func_size;
654 }
655 for (auto& i : module->ext_inst_imports()) {
656 (void)i;
657 ++pre_func_size;
658 }
659 ++pre_func_size; // memory_model
660 for (auto& i : module->entry_points()) {
661 (void)i;
662 ++pre_func_size;
663 }
664 for (auto& i : module->execution_modes()) {
665 (void)i;
666 ++pre_func_size;
667 }
668 for (auto& i : module->debugs1()) {
669 (void)i;
670 ++pre_func_size;
671 }
672 for (auto& i : module->debugs2()) {
673 (void)i;
674 ++pre_func_size;
675 }
676 for (auto& i : module->debugs3()) {
677 (void)i;
678 ++pre_func_size;
679 }
680 for (auto& i : module->annotations()) {
681 (void)i;
682 ++pre_func_size;
683 }
684 for (auto& i : module->types_values()) {
685 pre_func_size += 1;
686 pre_func_size += static_cast<uint32_t>(i.dbg_line_insts().size());
687 }
688 funcIdx2offset_[0] = pre_func_size;
689
690 // Set instruction offsets for all other functions.
691 uint32_t func_idx = 1;
692 auto prev_fn = get_module()->begin();
693 auto curr_fn = prev_fn;
694 for (++curr_fn; curr_fn != get_module()->end(); ++curr_fn) {
695 // Count function and end instructions
696 uint32_t func_size = 2;
697 for (auto& blk : *prev_fn) {
698 // Count label
699 func_size += 1;
700 for (auto& inst : blk) {
701 func_size += 1;
702 func_size += static_cast<uint32_t>(inst.dbg_line_insts().size());
703 }
704 }
705 funcIdx2offset_[func_idx] = func_size;
706 ++prev_fn;
707 ++func_idx;
708 }
709}
710
711} // namespace opt
712} // namespace spvtools