• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 The Khronos Group Inc.
2 // Copyright (c) 2020 Valve Corporation
3 // Copyright (c) 2020 LunarG Inc.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include "inst_debug_printf_pass.h"
18 
19 #include "source/spirv_constant.h"
20 #include "source/util/string_utils.h"
21 #include "spirv/unified1/NonSemanticDebugPrintf.h"
22 
23 namespace spvtools {
24 namespace opt {
25 
GenOutputValues(Instruction * val_inst,std::vector<uint32_t> * val_ids,InstructionBuilder * builder)26 void InstDebugPrintfPass::GenOutputValues(Instruction* val_inst,
27                                           std::vector<uint32_t>* val_ids,
28                                           InstructionBuilder* builder) {
29   uint32_t val_ty_id = val_inst->type_id();
30   analysis::TypeManager* type_mgr = context()->get_type_mgr();
31   analysis::Type* val_ty = type_mgr->GetType(val_ty_id);
32   switch (val_ty->kind()) {
33     case analysis::Type::kVector: {
34       analysis::Vector* v_ty = val_ty->AsVector();
35       const analysis::Type* c_ty = v_ty->element_type();
36       uint32_t c_ty_id = type_mgr->GetId(c_ty);
37       for (uint32_t c = 0; c < v_ty->element_count(); ++c) {
38         Instruction* c_inst =
39             builder->AddCompositeExtract(c_ty_id, val_inst->result_id(), {c});
40         GenOutputValues(c_inst, val_ids, builder);
41       }
42       return;
43     }
44     case analysis::Type::kBool: {
45       // Select between uint32 zero or one
46       uint32_t zero_id = builder->GetUintConstantId(0);
47       uint32_t one_id = builder->GetUintConstantId(1);
48       Instruction* sel_inst = builder->AddSelect(
49           GetUintId(), val_inst->result_id(), one_id, zero_id);
50       val_ids->push_back(sel_inst->result_id());
51       return;
52     }
53     case analysis::Type::kFloat: {
54       analysis::Float* f_ty = val_ty->AsFloat();
55       switch (f_ty->width()) {
56         case 16: {
57           // Convert float16 to float32 and recurse
58           Instruction* f32_inst = builder->AddUnaryOp(
59               GetFloatId(), spv::Op::OpFConvert, val_inst->result_id());
60           GenOutputValues(f32_inst, val_ids, builder);
61           return;
62         }
63         case 64: {
64           // Bitcast float64 to uint64 and recurse
65           Instruction* ui64_inst = builder->AddUnaryOp(
66               GetUint64Id(), spv::Op::OpBitcast, val_inst->result_id());
67           GenOutputValues(ui64_inst, val_ids, builder);
68           return;
69         }
70         case 32: {
71           // Bitcase float32 to uint32
72           Instruction* bc_inst = builder->AddUnaryOp(
73               GetUintId(), spv::Op::OpBitcast, val_inst->result_id());
74           val_ids->push_back(bc_inst->result_id());
75           return;
76         }
77         default:
78           assert(false && "unsupported float width");
79           return;
80       }
81     }
82     case analysis::Type::kInteger: {
83       analysis::Integer* i_ty = val_ty->AsInteger();
84       switch (i_ty->width()) {
85         case 64: {
86           Instruction* ui64_inst = val_inst;
87           if (i_ty->IsSigned()) {
88             // Bitcast sint64 to uint64
89             ui64_inst = builder->AddUnaryOp(GetUint64Id(), spv::Op::OpBitcast,
90                                             val_inst->result_id());
91           }
92           // Break uint64 into 2x uint32
93           Instruction* lo_ui64_inst = builder->AddUnaryOp(
94               GetUintId(), spv::Op::OpUConvert, ui64_inst->result_id());
95           Instruction* rshift_ui64_inst = builder->AddBinaryOp(
96               GetUint64Id(), spv::Op::OpShiftRightLogical,
97               ui64_inst->result_id(), builder->GetUintConstantId(32));
98           Instruction* hi_ui64_inst = builder->AddUnaryOp(
99               GetUintId(), spv::Op::OpUConvert, rshift_ui64_inst->result_id());
100           val_ids->push_back(lo_ui64_inst->result_id());
101           val_ids->push_back(hi_ui64_inst->result_id());
102           return;
103         }
104         case 8: {
105           Instruction* ui8_inst = val_inst;
106           if (i_ty->IsSigned()) {
107             // Bitcast sint8 to uint8
108             ui8_inst = builder->AddUnaryOp(GetUint8Id(), spv::Op::OpBitcast,
109                                            val_inst->result_id());
110           }
111           // Convert uint8 to uint32
112           Instruction* ui32_inst = builder->AddUnaryOp(
113               GetUintId(), spv::Op::OpUConvert, ui8_inst->result_id());
114           val_ids->push_back(ui32_inst->result_id());
115           return;
116         }
117         case 32: {
118           Instruction* ui32_inst = val_inst;
119           if (i_ty->IsSigned()) {
120             // Bitcast sint32 to uint32
121             ui32_inst = builder->AddUnaryOp(GetUintId(), spv::Op::OpBitcast,
122                                             val_inst->result_id());
123           }
124           // uint32 needs no further processing
125           val_ids->push_back(ui32_inst->result_id());
126           return;
127         }
128         default:
129           // TODO(greg-lunarg): Support non-32-bit int
130           assert(false && "unsupported int width");
131           return;
132       }
133     }
134     default:
135       assert(false && "unsupported type");
136       return;
137   }
138 }
139 
GenOutputCode(Instruction * printf_inst,std::vector<std::unique_ptr<BasicBlock>> * new_blocks)140 void InstDebugPrintfPass::GenOutputCode(
141     Instruction* printf_inst,
142     std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
143   BasicBlock* back_blk_ptr = &*new_blocks->back();
144   InstructionBuilder builder(
145       context(), back_blk_ptr,
146       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
147   // Gen debug printf record validation-specific values. The format string
148   // will have its id written. Vectors will need to be broken down into
149   // component values. float16 will need to be converted to float32. Pointer
150   // and uint64 will need to be converted to two uint32 values. float32 will
151   // need to be bitcast to uint32. int32 will need to be bitcast to uint32.
152   std::vector<uint32_t> val_ids;
153   bool is_first_operand = false;
154   printf_inst->ForEachInId(
155       [&is_first_operand, &val_ids, &builder, this](const uint32_t* iid) {
156         // skip set operand
157         if (!is_first_operand) {
158           is_first_operand = true;
159           return;
160         }
161         Instruction* opnd_inst = get_def_use_mgr()->GetDef(*iid);
162         if (opnd_inst->opcode() == spv::Op::OpString) {
163           uint32_t string_id_id = builder.GetUintConstantId(*iid);
164           val_ids.push_back(string_id_id);
165         } else {
166           GenOutputValues(opnd_inst, &val_ids, &builder);
167         }
168       });
169   GenDebugStreamWrite(
170       builder.GetUintConstantId(shader_id_),
171       builder.GetUintConstantId(uid2offset_[printf_inst->unique_id()]), val_ids,
172       &builder);
173   context()->KillInst(printf_inst);
174 }
175 
GenDebugPrintfCode(BasicBlock::iterator ref_inst_itr,UptrVectorIterator<BasicBlock> ref_block_itr,std::vector<std::unique_ptr<BasicBlock>> * new_blocks)176 void InstDebugPrintfPass::GenDebugPrintfCode(
177     BasicBlock::iterator ref_inst_itr,
178     UptrVectorIterator<BasicBlock> ref_block_itr,
179     std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
180   // If not DebugPrintf OpExtInst, return.
181   Instruction* printf_inst = &*ref_inst_itr;
182   if (printf_inst->opcode() != spv::Op::OpExtInst) return;
183   if (printf_inst->GetSingleWordInOperand(0) != ext_inst_printf_id_) return;
184   if (printf_inst->GetSingleWordInOperand(1) !=
185       NonSemanticDebugPrintfDebugPrintf)
186     return;
187   // Initialize DefUse manager before dismantling module
188   (void)get_def_use_mgr();
189   // Move original block's preceding instructions into first new block
190   std::unique_ptr<BasicBlock> new_blk_ptr;
191   MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
192   new_blocks->push_back(std::move(new_blk_ptr));
193   // Generate instructions to output printf args to printf buffer
194   GenOutputCode(printf_inst, new_blocks);
195   // Caller expects at least two blocks with last block containing remaining
196   // code, so end block after instrumentation, create remainder block, and
197   // branch to it
198   uint32_t rem_blk_id = TakeNextId();
199   std::unique_ptr<Instruction> rem_label(NewLabel(rem_blk_id));
200   BasicBlock* back_blk_ptr = &*new_blocks->back();
201   InstructionBuilder builder(
202       context(), back_blk_ptr,
203       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
204   (void)builder.AddBranch(rem_blk_id);
205   // Gen remainder block
206   new_blk_ptr.reset(new BasicBlock(std::move(rem_label)));
207   builder.SetInsertPoint(&*new_blk_ptr);
208   // Move original block's remaining code into remainder block and add
209   // to new blocks
210   MovePostludeCode(ref_block_itr, &*new_blk_ptr);
211   new_blocks->push_back(std::move(new_blk_ptr));
212 }
213 
214 // Return id for output buffer
GetOutputBufferId()215 uint32_t InstDebugPrintfPass::GetOutputBufferId() {
216   if (output_buffer_id_ == 0) {
217     // If not created yet, create one
218     analysis::DecorationManager* deco_mgr = get_decoration_mgr();
219     analysis::TypeManager* type_mgr = context()->get_type_mgr();
220     analysis::RuntimeArray* reg_uint_rarr_ty = GetUintRuntimeArrayType(32);
221     analysis::Integer* reg_uint_ty = GetInteger(32, false);
222     analysis::Type* reg_buf_ty =
223         GetStruct({reg_uint_ty, reg_uint_ty, reg_uint_rarr_ty});
224     uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
225     // By the Vulkan spec, a pre-existing struct containing a RuntimeArray
226     // must be a block, and will therefore be decorated with Block. Therefore
227     // the undecorated type returned here will not be pre-existing and can
228     // safely be decorated. Since this type is now decorated, it is out of
229     // sync with the TypeManager and therefore the TypeManager must be
230     // invalidated after this pass.
231     assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 &&
232            "used struct type returned");
233     deco_mgr->AddDecoration(obufTyId, uint32_t(spv::Decoration::Block));
234     deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputFlagsOffset,
235                                   uint32_t(spv::Decoration::Offset), 0);
236     deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset,
237                                   uint32_t(spv::Decoration::Offset), 4);
238     deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset,
239                                   uint32_t(spv::Decoration::Offset), 8);
240     uint32_t obufTyPtrId_ =
241         type_mgr->FindPointerToType(obufTyId, spv::StorageClass::StorageBuffer);
242     output_buffer_id_ = TakeNextId();
243     std::unique_ptr<Instruction> newVarOp(new Instruction(
244         context(), spv::Op::OpVariable, obufTyPtrId_, output_buffer_id_,
245         {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
246           {uint32_t(spv::StorageClass::StorageBuffer)}}}));
247     context()->AddGlobalValue(std::move(newVarOp));
248     context()->AddDebug2Inst(NewGlobalName(obufTyId, "OutputBuffer"));
249     context()->AddDebug2Inst(NewMemberName(obufTyId, 0, "flags"));
250     context()->AddDebug2Inst(NewMemberName(obufTyId, 1, "written_count"));
251     context()->AddDebug2Inst(NewMemberName(obufTyId, 2, "data"));
252     context()->AddDebug2Inst(NewGlobalName(output_buffer_id_, "output_buffer"));
253     deco_mgr->AddDecorationVal(
254         output_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_);
255     deco_mgr->AddDecorationVal(output_buffer_id_,
256                                uint32_t(spv::Decoration::Binding),
257                                GetOutputBufferBinding());
258     AddStorageBufferExt();
259     if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
260       // Add the new buffer to all entry points.
261       for (auto& entry : get_module()->entry_points()) {
262         entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}});
263         context()->AnalyzeUses(&entry);
264       }
265     }
266   }
267   return output_buffer_id_;
268 }
269 
GetOutputBufferPtrId()270 uint32_t InstDebugPrintfPass::GetOutputBufferPtrId() {
271   if (output_buffer_ptr_id_ == 0) {
272     output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
273         GetUintId(), spv::StorageClass::StorageBuffer);
274   }
275   return output_buffer_ptr_id_;
276 }
277 
GetOutputBufferBinding()278 uint32_t InstDebugPrintfPass::GetOutputBufferBinding() {
279   return kDebugOutputPrintfStream;
280 }
281 
GenDebugOutputFieldCode(uint32_t base_offset_id,uint32_t field_offset,uint32_t field_value_id,InstructionBuilder * builder)282 void InstDebugPrintfPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
283                                                   uint32_t field_offset,
284                                                   uint32_t field_value_id,
285                                                   InstructionBuilder* builder) {
286   // Cast value to 32-bit unsigned if necessary
287   uint32_t val_id = GenUintCastCode(field_value_id, builder);
288   // Store value
289   Instruction* data_idx_inst = builder->AddIAdd(
290       GetUintId(), base_offset_id, builder->GetUintConstantId(field_offset));
291   uint32_t buf_id = GetOutputBufferId();
292   uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
293   Instruction* achain_inst = builder->AddAccessChain(
294       buf_uint_ptr_id, buf_id,
295       {builder->GetUintConstantId(kDebugOutputDataOffset),
296        data_idx_inst->result_id()});
297   (void)builder->AddStore(achain_inst->result_id(), val_id);
298 }
299 
GetStreamWriteFunctionId(uint32_t param_cnt)300 uint32_t InstDebugPrintfPass::GetStreamWriteFunctionId(uint32_t param_cnt) {
301   enum {
302     kShaderId = 0,
303     kInstructionIndex = 1,
304     kFirstParam = 2,
305   };
306   // Total param count is common params plus validation-specific
307   // params
308   if (param2output_func_id_[param_cnt] == 0) {
309     // Create function
310     param2output_func_id_[param_cnt] = TakeNextId();
311     analysis::TypeManager* type_mgr = context()->get_type_mgr();
312 
313     const analysis::Type* uint_type = GetInteger(32, false);
314 
315     std::vector<const analysis::Type*> param_types(kFirstParam + param_cnt,
316                                                    uint_type);
317     std::unique_ptr<Function> output_func = StartFunction(
318         param2output_func_id_[param_cnt], type_mgr->GetVoidType(), param_types);
319 
320     std::vector<uint32_t> param_ids = AddParameters(*output_func, param_types);
321 
322     // Create first block
323     auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId()));
324 
325     InstructionBuilder builder(
326         context(), &*new_blk_ptr,
327         IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
328     // Gen test if debug output buffer size will not be exceeded.
329     const uint32_t first_param_offset = kInstCommonOutInstructionIdx + 1;
330     const uint32_t obuf_record_sz = first_param_offset + param_cnt;
331     const uint32_t buf_id = GetOutputBufferId();
332     const uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
333     Instruction* obuf_curr_sz_ac_inst = builder.AddAccessChain(
334         buf_uint_ptr_id, buf_id,
335         {builder.GetUintConstantId(kDebugOutputSizeOffset)});
336     // Fetch the current debug buffer written size atomically, adding the
337     // size of the record to be written.
338     uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz);
339     uint32_t mask_none_id =
340         builder.GetUintConstantId(uint32_t(spv::MemoryAccessMask::MaskNone));
341     uint32_t scope_invok_id =
342         builder.GetUintConstantId(uint32_t(spv::Scope::Invocation));
343     Instruction* obuf_curr_sz_inst = builder.AddQuadOp(
344         GetUintId(), spv::Op::OpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(),
345         scope_invok_id, mask_none_id, obuf_record_sz_id);
346     uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id();
347     // Compute new written size
348     Instruction* obuf_new_sz_inst =
349         builder.AddIAdd(GetUintId(), obuf_curr_sz_id,
350                         builder.GetUintConstantId(obuf_record_sz));
351     // Fetch the data bound
352     Instruction* obuf_bnd_inst =
353         builder.AddIdLiteralOp(GetUintId(), spv::Op::OpArrayLength,
354                                GetOutputBufferId(), kDebugOutputDataOffset);
355     // Test that new written size is less than or equal to debug output
356     // data bound
357     Instruction* obuf_safe_inst = builder.AddBinaryOp(
358         GetBoolId(), spv::Op::OpULessThanEqual, obuf_new_sz_inst->result_id(),
359         obuf_bnd_inst->result_id());
360     uint32_t merge_blk_id = TakeNextId();
361     uint32_t write_blk_id = TakeNextId();
362     std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
363     std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id));
364     (void)builder.AddConditionalBranch(
365         obuf_safe_inst->result_id(), write_blk_id, merge_blk_id, merge_blk_id,
366         uint32_t(spv::SelectionControlMask::MaskNone));
367     // Close safety test block and gen write block
368     output_func->AddBasicBlock(std::move(new_blk_ptr));
369     new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label));
370     builder.SetInsertPoint(&*new_blk_ptr);
371     // Generate common and stage-specific debug record members
372     GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutSize,
373                             builder.GetUintConstantId(obuf_record_sz),
374                             &builder);
375     // Store Shader Id
376     GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutShaderId,
377                             param_ids[kShaderId], &builder);
378     // Store Instruction Idx
379     GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutInstructionIdx,
380                             param_ids[kInstructionIndex], &builder);
381     // Gen writes of validation specific data
382     for (uint32_t i = 0; i < param_cnt; ++i) {
383       GenDebugOutputFieldCode(obuf_curr_sz_id, first_param_offset + i,
384                               param_ids[kFirstParam + i], &builder);
385     }
386     // Close write block and gen merge block
387     (void)builder.AddBranch(merge_blk_id);
388     output_func->AddBasicBlock(std::move(new_blk_ptr));
389     new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
390     builder.SetInsertPoint(&*new_blk_ptr);
391     // Close merge block and function and add function to module
392     (void)builder.AddNullaryOp(0, spv::Op::OpReturn);
393 
394     output_func->AddBasicBlock(std::move(new_blk_ptr));
395     output_func->SetFunctionEnd(EndFunction());
396     context()->AddFunction(std::move(output_func));
397 
398     std::string name("stream_write_");
399     name += std::to_string(param_cnt);
400 
401     context()->AddDebug2Inst(
402         NewGlobalName(param2output_func_id_[param_cnt], name));
403   }
404   return param2output_func_id_[param_cnt];
405 }
406 
GenDebugStreamWrite(uint32_t shader_id,uint32_t instruction_idx_id,const std::vector<uint32_t> & validation_ids,InstructionBuilder * builder)407 void InstDebugPrintfPass::GenDebugStreamWrite(
408     uint32_t shader_id, uint32_t instruction_idx_id,
409     const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) {
410   // Call debug output function. Pass func_idx, instruction_idx and
411   // validation ids as args.
412   uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size());
413   std::vector<uint32_t> args = {shader_id, instruction_idx_id};
414   (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end());
415   (void)builder->AddFunctionCall(GetVoidId(),
416                                  GetStreamWriteFunctionId(val_id_cnt), args);
417 }
418 
NewGlobalName(uint32_t id,const std::string & name_str)419 std::unique_ptr<Instruction> InstDebugPrintfPass::NewGlobalName(
420     uint32_t id, const std::string& name_str) {
421   std::string prefixed_name{"inst_printf_"};
422   prefixed_name += name_str;
423   return NewName(id, prefixed_name);
424 }
425 
NewMemberName(uint32_t id,uint32_t member_index,const std::string & name_str)426 std::unique_ptr<Instruction> InstDebugPrintfPass::NewMemberName(
427     uint32_t id, uint32_t member_index, const std::string& name_str) {
428   return MakeUnique<Instruction>(
429       context(), spv::Op::OpMemberName, 0, 0,
430       std::initializer_list<Operand>{
431           {SPV_OPERAND_TYPE_ID, {id}},
432           {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index}},
433           {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}});
434 }
435 
InitializeInstDebugPrintf()436 void InstDebugPrintfPass::InitializeInstDebugPrintf() {
437   // Initialize base class
438   InitializeInstrument();
439   output_buffer_id_ = 0;
440   output_buffer_ptr_id_ = 0;
441 }
442 
ProcessImpl()443 Pass::Status InstDebugPrintfPass::ProcessImpl() {
444   // Perform printf instrumentation on each entry point function in module
445   InstProcessFunction pfn =
446       [this](BasicBlock::iterator ref_inst_itr,
447              UptrVectorIterator<BasicBlock> ref_block_itr,
448              [[maybe_unused]] uint32_t stage_idx,
449              std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
450         return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, new_blocks);
451       };
452   (void)InstProcessEntryPointCallTree(pfn);
453   // Remove DebugPrintf OpExtInstImport instruction
454   Instruction* ext_inst_import_inst =
455       get_def_use_mgr()->GetDef(ext_inst_printf_id_);
456   context()->KillInst(ext_inst_import_inst);
457   // If no remaining non-semantic instruction sets, remove non-semantic debug
458   // info extension from module and feature manager
459   bool non_sem_set_seen = false;
460   for (auto c_itr = context()->module()->ext_inst_import_begin();
461        c_itr != context()->module()->ext_inst_import_end(); ++c_itr) {
462     const std::string set_name = c_itr->GetInOperand(0).AsString();
463     if (spvtools::utils::starts_with(set_name, "NonSemantic.")) {
464       non_sem_set_seen = true;
465       break;
466     }
467   }
468   if (!non_sem_set_seen) {
469     context()->RemoveExtension(kSPV_KHR_non_semantic_info);
470   }
471   return Status::SuccessWithChange;
472 }
473 
Process()474 Pass::Status InstDebugPrintfPass::Process() {
475   ext_inst_printf_id_ =
476       get_module()->GetExtInstImportId("NonSemantic.DebugPrintf");
477   if (ext_inst_printf_id_ == 0) return Status::SuccessWithoutChange;
478   InitializeInstDebugPrintf();
479   return ProcessImpl();
480 }
481 
482 }  // namespace opt
483 }  // namespace spvtools
484