• 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 "spirv/unified1/NonSemanticDebugPrintf.h"
20 
21 namespace spvtools {
22 namespace opt {
23 
GenOutputValues(Instruction * val_inst,std::vector<uint32_t> * val_ids,InstructionBuilder * builder)24 void InstDebugPrintfPass::GenOutputValues(Instruction* val_inst,
25                                           std::vector<uint32_t>* val_ids,
26                                           InstructionBuilder* builder) {
27   uint32_t val_ty_id = val_inst->type_id();
28   analysis::TypeManager* type_mgr = context()->get_type_mgr();
29   analysis::Type* val_ty = type_mgr->GetType(val_ty_id);
30   switch (val_ty->kind()) {
31     case analysis::Type::kVector: {
32       analysis::Vector* v_ty = val_ty->AsVector();
33       const analysis::Type* c_ty = v_ty->element_type();
34       uint32_t c_ty_id = type_mgr->GetId(c_ty);
35       for (uint32_t c = 0; c < v_ty->element_count(); ++c) {
36         Instruction* c_inst = builder->AddIdLiteralOp(
37             c_ty_id, SpvOpCompositeExtract, val_inst->result_id(), c);
38         GenOutputValues(c_inst, val_ids, builder);
39       }
40       return;
41     }
42     case analysis::Type::kBool: {
43       // Select between uint32 zero or one
44       uint32_t zero_id = builder->GetUintConstantId(0);
45       uint32_t one_id = builder->GetUintConstantId(1);
46       Instruction* sel_inst = builder->AddTernaryOp(
47           GetUintId(), SpvOpSelect, val_inst->result_id(), one_id, zero_id);
48       val_ids->push_back(sel_inst->result_id());
49       return;
50     }
51     case analysis::Type::kFloat: {
52       analysis::Float* f_ty = val_ty->AsFloat();
53       switch (f_ty->width()) {
54         case 16: {
55           // Convert float16 to float32 and recurse
56           Instruction* f32_inst = builder->AddUnaryOp(
57               GetFloatId(), SpvOpFConvert, val_inst->result_id());
58           GenOutputValues(f32_inst, val_ids, builder);
59           return;
60         }
61         case 64: {
62           // Bitcast float64 to uint64 and recurse
63           Instruction* ui64_inst = builder->AddUnaryOp(
64               GetUint64Id(), SpvOpBitcast, val_inst->result_id());
65           GenOutputValues(ui64_inst, val_ids, builder);
66           return;
67         }
68         case 32: {
69           // Bitcase float32 to uint32
70           Instruction* bc_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
71                                                      val_inst->result_id());
72           val_ids->push_back(bc_inst->result_id());
73           return;
74         }
75         default:
76           assert(false && "unsupported float width");
77           return;
78       }
79     }
80     case analysis::Type::kInteger: {
81       analysis::Integer* i_ty = val_ty->AsInteger();
82       switch (i_ty->width()) {
83         case 64: {
84           Instruction* ui64_inst = val_inst;
85           if (i_ty->IsSigned()) {
86             // Bitcast sint64 to uint64
87             ui64_inst = builder->AddUnaryOp(GetUint64Id(), SpvOpBitcast,
88                                             val_inst->result_id());
89           }
90           // Break uint64 into 2x uint32
91           Instruction* lo_ui64_inst = builder->AddUnaryOp(
92               GetUintId(), SpvOpUConvert, ui64_inst->result_id());
93           Instruction* rshift_ui64_inst = builder->AddBinaryOp(
94               GetUint64Id(), SpvOpShiftRightLogical, ui64_inst->result_id(),
95               builder->GetUintConstantId(32));
96           Instruction* hi_ui64_inst = builder->AddUnaryOp(
97               GetUintId(), SpvOpUConvert, rshift_ui64_inst->result_id());
98           val_ids->push_back(lo_ui64_inst->result_id());
99           val_ids->push_back(hi_ui64_inst->result_id());
100           return;
101         }
102         case 8: {
103           Instruction* ui8_inst = val_inst;
104           if (i_ty->IsSigned()) {
105             // Bitcast sint8 to uint8
106             ui8_inst = builder->AddUnaryOp(GetUint8Id(), SpvOpBitcast,
107                                            val_inst->result_id());
108           }
109           // Convert uint8 to uint32
110           Instruction* ui32_inst = builder->AddUnaryOp(
111               GetUintId(), SpvOpUConvert, ui8_inst->result_id());
112           val_ids->push_back(ui32_inst->result_id());
113           return;
114         }
115         case 32: {
116           Instruction* ui32_inst = val_inst;
117           if (i_ty->IsSigned()) {
118             // Bitcast sint32 to uint32
119             ui32_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
120                                             val_inst->result_id());
121           }
122           // uint32 needs no further processing
123           val_ids->push_back(ui32_inst->result_id());
124           return;
125         }
126         default:
127           // TODO(greg-lunarg): Support non-32-bit int
128           assert(false && "unsupported int width");
129           return;
130       }
131     }
132     default:
133       assert(false && "unsupported type");
134       return;
135   }
136 }
137 
GenOutputCode(Instruction * printf_inst,uint32_t stage_idx,std::vector<std::unique_ptr<BasicBlock>> * new_blocks)138 void InstDebugPrintfPass::GenOutputCode(
139     Instruction* printf_inst, uint32_t stage_idx,
140     std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
141   BasicBlock* back_blk_ptr = &*new_blocks->back();
142   InstructionBuilder builder(
143       context(), back_blk_ptr,
144       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
145   // Gen debug printf record validation-specific values. The format string
146   // will have its id written. Vectors will need to be broken down into
147   // component values. float16 will need to be converted to float32. Pointer
148   // and uint64 will need to be converted to two uint32 values. float32 will
149   // need to be bitcast to uint32. int32 will need to be bitcast to uint32.
150   std::vector<uint32_t> val_ids;
151   bool is_first_operand = false;
152   printf_inst->ForEachInId(
153       [&is_first_operand, &val_ids, &builder, this](const uint32_t* iid) {
154         // skip set operand
155         if (!is_first_operand) {
156           is_first_operand = true;
157           return;
158         }
159         Instruction* opnd_inst = get_def_use_mgr()->GetDef(*iid);
160         if (opnd_inst->opcode() == SpvOpString) {
161           uint32_t string_id_id = builder.GetUintConstantId(*iid);
162           val_ids.push_back(string_id_id);
163         } else {
164           GenOutputValues(opnd_inst, &val_ids, &builder);
165         }
166       });
167   GenDebugStreamWrite(uid2offset_[printf_inst->unique_id()], stage_idx, val_ids,
168                       &builder);
169   context()->KillInst(printf_inst);
170 }
171 
GenDebugPrintfCode(BasicBlock::iterator ref_inst_itr,UptrVectorIterator<BasicBlock> ref_block_itr,uint32_t stage_idx,std::vector<std::unique_ptr<BasicBlock>> * new_blocks)172 void InstDebugPrintfPass::GenDebugPrintfCode(
173     BasicBlock::iterator ref_inst_itr,
174     UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
175     std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
176   // If not DebugPrintf OpExtInst, return.
177   Instruction* printf_inst = &*ref_inst_itr;
178   if (printf_inst->opcode() != SpvOpExtInst) return;
179   if (printf_inst->GetSingleWordInOperand(0) != ext_inst_printf_id_) return;
180   if (printf_inst->GetSingleWordInOperand(1) !=
181       NonSemanticDebugPrintfDebugPrintf)
182     return;
183   // Initialize DefUse manager before dismantling module
184   (void)get_def_use_mgr();
185   // Move original block's preceding instructions into first new block
186   std::unique_ptr<BasicBlock> new_blk_ptr;
187   MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
188   new_blocks->push_back(std::move(new_blk_ptr));
189   // Generate instructions to output printf args to printf buffer
190   GenOutputCode(printf_inst, stage_idx, new_blocks);
191   // Caller expects at least two blocks with last block containing remaining
192   // code, so end block after instrumentation, create remainder block, and
193   // branch to it
194   uint32_t rem_blk_id = TakeNextId();
195   std::unique_ptr<Instruction> rem_label(NewLabel(rem_blk_id));
196   BasicBlock* back_blk_ptr = &*new_blocks->back();
197   InstructionBuilder builder(
198       context(), back_blk_ptr,
199       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
200   (void)builder.AddBranch(rem_blk_id);
201   // Gen remainder block
202   new_blk_ptr.reset(new BasicBlock(std::move(rem_label)));
203   builder.SetInsertPoint(&*new_blk_ptr);
204   // Move original block's remaining code into remainder block and add
205   // to new blocks
206   MovePostludeCode(ref_block_itr, &*new_blk_ptr);
207   new_blocks->push_back(std::move(new_blk_ptr));
208 }
209 
InitializeInstDebugPrintf()210 void InstDebugPrintfPass::InitializeInstDebugPrintf() {
211   // Initialize base class
212   InitializeInstrument();
213 }
214 
ProcessImpl()215 Pass::Status InstDebugPrintfPass::ProcessImpl() {
216   // Perform printf instrumentation on each entry point function in module
217   InstProcessFunction pfn =
218       [this](BasicBlock::iterator ref_inst_itr,
219              UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
220              std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
221         return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, stage_idx,
222                                   new_blocks);
223       };
224   (void)InstProcessEntryPointCallTree(pfn);
225   // Remove DebugPrintf OpExtInstImport instruction
226   Instruction* ext_inst_import_inst =
227       get_def_use_mgr()->GetDef(ext_inst_printf_id_);
228   context()->KillInst(ext_inst_import_inst);
229   // If no remaining non-semantic instruction sets, remove non-semantic debug
230   // info extension from module and feature manager
231   bool non_sem_set_seen = false;
232   for (auto c_itr = context()->module()->ext_inst_import_begin();
233        c_itr != context()->module()->ext_inst_import_end(); ++c_itr) {
234     const char* set_name =
235         reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
236     const char* non_sem_str = "NonSemantic.";
237     if (!strncmp(set_name, non_sem_str, strlen(non_sem_str))) {
238       non_sem_set_seen = true;
239       break;
240     }
241   }
242   if (!non_sem_set_seen) {
243     for (auto c_itr = context()->module()->extension_begin();
244          c_itr != context()->module()->extension_end(); ++c_itr) {
245       const char* ext_name =
246           reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
247       if (!strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
248         context()->KillInst(&*c_itr);
249         break;
250       }
251     }
252     context()->get_feature_mgr()->RemoveExtension(kSPV_KHR_non_semantic_info);
253   }
254   return Status::SuccessWithChange;
255 }
256 
Process()257 Pass::Status InstDebugPrintfPass::Process() {
258   ext_inst_printf_id_ =
259       get_module()->GetExtInstImportId("NonSemantic.DebugPrintf");
260   if (ext_inst_printf_id_ == 0) return Status::SuccessWithoutChange;
261   InitializeInstDebugPrintf();
262   return ProcessImpl();
263 }
264 
265 }  // namespace opt
266 }  // namespace spvtools
267