1 // Copyright (c) 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "source/opt/debug_info_manager.h"
16
17 #include <cassert>
18
19 #include "source/opt/ir_context.h"
20
21 // Constants for OpenCL.DebugInfo.100 extension instructions.
22
23 static const uint32_t kOpLineOperandLineIndex = 1;
24 static const uint32_t kLineOperandIndexDebugFunction = 7;
25 static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
26 static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
27 static const uint32_t kDebugFunctionOperandParentIndex = 9;
28 static const uint32_t kDebugTypeCompositeOperandParentIndex = 9;
29 static const uint32_t kDebugLexicalBlockOperandParentIndex = 7;
30 static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
31 static const uint32_t kDebugExpressOperandOperationIndex = 4;
32 static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
33 static const uint32_t kDebugDeclareOperandVariableIndex = 5;
34 static const uint32_t kDebugValueOperandExpressionIndex = 6;
35 static const uint32_t kDebugOperationOperandOperationIndex = 4;
36 static const uint32_t kOpVariableOperandStorageClassIndex = 2;
37 static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
38 static const uint32_t kExtInstInstructionInIdx = 1;
39 static const uint32_t kDebugGlobalVariableOperandFlagsIndex = 12;
40 static const uint32_t kDebugLocalVariableOperandFlagsIndex = 10;
41
42 namespace spvtools {
43 namespace opt {
44 namespace analysis {
45 namespace {
46
SetInlinedOperand(Instruction * dbg_inlined_at,uint32_t inlined_operand)47 void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) {
48 assert(dbg_inlined_at);
49 assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
50 OpenCLDebugInfo100DebugInlinedAt);
51 if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) {
52 dbg_inlined_at->AddOperand(
53 {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}});
54 } else {
55 dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex,
56 {inlined_operand});
57 }
58 }
59
GetInlinedOperand(Instruction * dbg_inlined_at)60 uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) {
61 assert(dbg_inlined_at);
62 assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
63 OpenCLDebugInfo100DebugInlinedAt);
64 if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex)
65 return kNoInlinedAt;
66 return dbg_inlined_at->GetSingleWordOperand(
67 kDebugInlinedAtOperandInlinedIndex);
68 }
69
IsEmptyDebugExpression(Instruction * instr)70 bool IsEmptyDebugExpression(Instruction* instr) {
71 return instr->GetOpenCL100DebugOpcode() ==
72 OpenCLDebugInfo100DebugExpression &&
73 instr->NumOperands() == kDebugExpressOperandOperationIndex;
74 }
75
76 } // namespace
77
DebugInfoManager(IRContext * c)78 DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
79 AnalyzeDebugInsts(*c->module());
80 }
81
GetDbgInst(uint32_t id)82 Instruction* DebugInfoManager::GetDbgInst(uint32_t id) {
83 auto dbg_inst_it = id_to_dbg_inst_.find(id);
84 return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second;
85 }
86
RegisterDbgInst(Instruction * inst)87 void DebugInfoManager::RegisterDbgInst(Instruction* inst) {
88 assert(
89 inst->NumInOperands() != 0 &&
90 context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
91 inst->GetInOperand(0).words[0] &&
92 "Given instruction is not a debug instruction");
93 id_to_dbg_inst_[inst->result_id()] = inst;
94 }
95
RegisterDbgFunction(Instruction * inst)96 void DebugInfoManager::RegisterDbgFunction(Instruction* inst) {
97 assert(inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction &&
98 "inst is not a DebugFunction");
99 auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
100 // Do not register function that has been optimized away
101 auto fn_inst = GetDbgInst(fn_id);
102 if (fn_inst != nullptr) {
103 assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() ==
104 OpenCLDebugInfo100DebugInfoNone);
105 return;
106 }
107 assert(
108 fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() &&
109 "Register DebugFunction for a function that already has DebugFunction");
110 fn_id_to_dbg_fn_[fn_id] = inst;
111 }
112
RegisterDbgDeclare(uint32_t var_id,Instruction * dbg_declare)113 void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
114 Instruction* dbg_declare) {
115 assert(dbg_declare->GetOpenCL100DebugOpcode() ==
116 OpenCLDebugInfo100DebugDeclare ||
117 dbg_declare->GetOpenCL100DebugOpcode() ==
118 OpenCLDebugInfo100DebugValue);
119 auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id);
120 if (dbg_decl_itr == var_id_to_dbg_decl_.end()) {
121 var_id_to_dbg_decl_[var_id] = {dbg_declare};
122 } else {
123 dbg_decl_itr->second.insert(dbg_declare);
124 }
125 }
126
CreateDebugInlinedAt(const Instruction * line,const DebugScope & scope)127 uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
128 const DebugScope& scope) {
129 if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
130 0)
131 return kNoInlinedAt;
132
133 uint32_t line_number = 0;
134 if (line == nullptr) {
135 auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope());
136 if (lexical_scope_inst == nullptr) return kNoInlinedAt;
137 OpenCLDebugInfo100Instructions debug_opcode =
138 lexical_scope_inst->GetOpenCL100DebugOpcode();
139 switch (debug_opcode) {
140 case OpenCLDebugInfo100DebugFunction:
141 line_number = lexical_scope_inst->GetSingleWordOperand(
142 kLineOperandIndexDebugFunction);
143 break;
144 case OpenCLDebugInfo100DebugLexicalBlock:
145 line_number = lexical_scope_inst->GetSingleWordOperand(
146 kLineOperandIndexDebugLexicalBlock);
147 break;
148 case OpenCLDebugInfo100DebugTypeComposite:
149 case OpenCLDebugInfo100DebugCompilationUnit:
150 assert(false &&
151 "DebugTypeComposite and DebugCompilationUnit are lexical "
152 "scopes, but we inline functions into a function or a block "
153 "of a function, not into a struct/class or a global scope.");
154 break;
155 default:
156 assert(false &&
157 "Unreachable. a debug extension instruction for a "
158 "lexical scope must be DebugFunction, DebugTypeComposite, "
159 "DebugLexicalBlock, or DebugCompilationUnit.");
160 break;
161 }
162 } else {
163 line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
164 }
165
166 uint32_t result_id = context()->TakeNextId();
167 std::unique_ptr<Instruction> inlined_at(new Instruction(
168 context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
169 result_id,
170 {
171 {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
172 {context()
173 ->get_feature_mgr()
174 ->GetExtInstImportId_OpenCL100DebugInfo()}},
175 {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
176 {static_cast<uint32_t>(OpenCLDebugInfo100DebugInlinedAt)}},
177 {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
178 {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}},
179 }));
180 // |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt
181 // into the Inlined operand of this new DebugInlinedAt.
182 if (scope.GetInlinedAt() != kNoInlinedAt) {
183 inlined_at->AddOperand(
184 {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetInlinedAt()}});
185 }
186 RegisterDbgInst(inlined_at.get());
187 if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
188 context()->get_def_use_mgr()->AnalyzeInstDefUse(inlined_at.get());
189 context()->module()->AddExtInstDebugInfo(std::move(inlined_at));
190 return result_id;
191 }
192
BuildDebugScope(const DebugScope & callee_instr_scope,DebugInlinedAtContext * inlined_at_ctx)193 DebugScope DebugInfoManager::BuildDebugScope(
194 const DebugScope& callee_instr_scope,
195 DebugInlinedAtContext* inlined_at_ctx) {
196 return DebugScope(callee_instr_scope.GetLexicalScope(),
197 BuildDebugInlinedAtChain(callee_instr_scope.GetInlinedAt(),
198 inlined_at_ctx));
199 }
200
BuildDebugInlinedAtChain(uint32_t callee_inlined_at,DebugInlinedAtContext * inlined_at_ctx)201 uint32_t DebugInfoManager::BuildDebugInlinedAtChain(
202 uint32_t callee_inlined_at, DebugInlinedAtContext* inlined_at_ctx) {
203 if (inlined_at_ctx->GetScopeOfCallInstruction().GetLexicalScope() ==
204 kNoDebugScope)
205 return kNoInlinedAt;
206
207 // Reuse the already generated DebugInlinedAt chain if exists.
208 uint32_t already_generated_chain_head_id =
209 inlined_at_ctx->GetDebugInlinedAtChain(callee_inlined_at);
210 if (already_generated_chain_head_id != kNoInlinedAt) {
211 return already_generated_chain_head_id;
212 }
213
214 const uint32_t new_dbg_inlined_at_id =
215 CreateDebugInlinedAt(inlined_at_ctx->GetLineOfCallInstruction(),
216 inlined_at_ctx->GetScopeOfCallInstruction());
217 if (new_dbg_inlined_at_id == kNoInlinedAt) return kNoInlinedAt;
218
219 if (callee_inlined_at == kNoInlinedAt) {
220 inlined_at_ctx->SetDebugInlinedAtChain(kNoInlinedAt, new_dbg_inlined_at_id);
221 return new_dbg_inlined_at_id;
222 }
223
224 uint32_t chain_head_id = kNoInlinedAt;
225 uint32_t chain_iter_id = callee_inlined_at;
226 Instruction* last_inlined_at_in_chain = nullptr;
227 do {
228 Instruction* new_inlined_at_in_chain = CloneDebugInlinedAt(
229 chain_iter_id, /* insert_before */ last_inlined_at_in_chain);
230 assert(new_inlined_at_in_chain != nullptr);
231
232 // Set DebugInlinedAt of the new scope as the head of the chain.
233 if (chain_head_id == kNoInlinedAt)
234 chain_head_id = new_inlined_at_in_chain->result_id();
235
236 // Previous DebugInlinedAt of the chain must point to the new
237 // DebugInlinedAt as its Inlined operand to build a recursive
238 // chain.
239 if (last_inlined_at_in_chain != nullptr) {
240 SetInlinedOperand(last_inlined_at_in_chain,
241 new_inlined_at_in_chain->result_id());
242 }
243 last_inlined_at_in_chain = new_inlined_at_in_chain;
244
245 chain_iter_id = GetInlinedOperand(new_inlined_at_in_chain);
246 } while (chain_iter_id != kNoInlinedAt);
247
248 // Put |new_dbg_inlined_at_id| into the end of the chain.
249 SetInlinedOperand(last_inlined_at_in_chain, new_dbg_inlined_at_id);
250
251 // Keep the new chain information that will be reused it.
252 inlined_at_ctx->SetDebugInlinedAtChain(callee_inlined_at, chain_head_id);
253 return chain_head_id;
254 }
255
GetDebugOperationWithDeref()256 Instruction* DebugInfoManager::GetDebugOperationWithDeref() {
257 if (deref_operation_ != nullptr) return deref_operation_;
258
259 uint32_t result_id = context()->TakeNextId();
260 std::unique_ptr<Instruction> deref_operation(new Instruction(
261 context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
262 result_id,
263 {
264 {SPV_OPERAND_TYPE_ID,
265 {context()
266 ->get_feature_mgr()
267 ->GetExtInstImportId_OpenCL100DebugInfo()}},
268 {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
269 {static_cast<uint32_t>(OpenCLDebugInfo100DebugOperation)}},
270 {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,
271 {static_cast<uint32_t>(OpenCLDebugInfo100Deref)}},
272 }));
273
274 // Add to the front of |ext_inst_debuginfo_|.
275 deref_operation_ =
276 context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
277 std::move(deref_operation));
278
279 RegisterDbgInst(deref_operation_);
280 if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
281 context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_operation_);
282 return deref_operation_;
283 }
284
DerefDebugExpression(Instruction * dbg_expr)285 Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) {
286 assert(dbg_expr->GetOpenCL100DebugOpcode() ==
287 OpenCLDebugInfo100DebugExpression);
288 std::unique_ptr<Instruction> deref_expr(dbg_expr->Clone(context()));
289 deref_expr->SetResultId(context()->TakeNextId());
290 deref_expr->InsertOperand(
291 kDebugExpressOperandOperationIndex,
292 {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}});
293 auto* deref_expr_instr =
294 context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr));
295 AnalyzeDebugInst(deref_expr_instr);
296 if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
297 context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_expr_instr);
298 return deref_expr_instr;
299 }
300
GetDebugInfoNone()301 Instruction* DebugInfoManager::GetDebugInfoNone() {
302 if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
303
304 uint32_t result_id = context()->TakeNextId();
305 std::unique_ptr<Instruction> dbg_info_none_inst(new Instruction(
306 context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
307 result_id,
308 {
309 {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
310 {context()
311 ->get_feature_mgr()
312 ->GetExtInstImportId_OpenCL100DebugInfo()}},
313 {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
314 {static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
315 }));
316
317 // Add to the front of |ext_inst_debuginfo_|.
318 debug_info_none_inst_ =
319 context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
320 std::move(dbg_info_none_inst));
321
322 RegisterDbgInst(debug_info_none_inst_);
323 if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
324 context()->get_def_use_mgr()->AnalyzeInstDefUse(debug_info_none_inst_);
325 return debug_info_none_inst_;
326 }
327
GetEmptyDebugExpression()328 Instruction* DebugInfoManager::GetEmptyDebugExpression() {
329 if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_;
330
331 uint32_t result_id = context()->TakeNextId();
332 std::unique_ptr<Instruction> empty_debug_expr(new Instruction(
333 context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
334 result_id,
335 {
336 {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
337 {context()
338 ->get_feature_mgr()
339 ->GetExtInstImportId_OpenCL100DebugInfo()}},
340 {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
341 {static_cast<uint32_t>(OpenCLDebugInfo100DebugExpression)}},
342 }));
343
344 // Add to the front of |ext_inst_debuginfo_|.
345 empty_debug_expr_inst_ =
346 context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
347 std::move(empty_debug_expr));
348
349 RegisterDbgInst(empty_debug_expr_inst_);
350 if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
351 context()->get_def_use_mgr()->AnalyzeInstDefUse(empty_debug_expr_inst_);
352 return empty_debug_expr_inst_;
353 }
354
GetDebugInlinedAt(uint32_t dbg_inlined_at_id)355 Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) {
356 auto* inlined_at = GetDbgInst(dbg_inlined_at_id);
357 if (inlined_at == nullptr) return nullptr;
358 if (inlined_at->GetOpenCL100DebugOpcode() !=
359 OpenCLDebugInfo100DebugInlinedAt) {
360 return nullptr;
361 }
362 return inlined_at;
363 }
364
CloneDebugInlinedAt(uint32_t clone_inlined_at_id,Instruction * insert_before)365 Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
366 Instruction* insert_before) {
367 auto* inlined_at = GetDebugInlinedAt(clone_inlined_at_id);
368 if (inlined_at == nullptr) return nullptr;
369 std::unique_ptr<Instruction> new_inlined_at(inlined_at->Clone(context()));
370 new_inlined_at->SetResultId(context()->TakeNextId());
371 RegisterDbgInst(new_inlined_at.get());
372 if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
373 context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get());
374 if (insert_before != nullptr)
375 return insert_before->InsertBefore(std::move(new_inlined_at));
376 return context()->module()->ext_inst_debuginfo_end()->InsertBefore(
377 std::move(new_inlined_at));
378 }
379
IsVariableDebugDeclared(uint32_t variable_id)380 bool DebugInfoManager::IsVariableDebugDeclared(uint32_t variable_id) {
381 auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
382 return dbg_decl_itr != var_id_to_dbg_decl_.end();
383 }
384
KillDebugDeclares(uint32_t variable_id)385 bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
386 bool modified = false;
387 auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
388 if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
389 // We intentionally copy the list of DebugDeclare instructions because
390 // context()->KillInst(dbg_decl) will update |var_id_to_dbg_decl_|. If we
391 // directly use |dbg_decl_itr->second|, it accesses a dangling pointer.
392 auto copy_dbg_decls = dbg_decl_itr->second;
393
394 for (auto* dbg_decl : copy_dbg_decls) {
395 context()->KillInst(dbg_decl);
396 modified = true;
397 }
398 var_id_to_dbg_decl_.erase(dbg_decl_itr);
399 }
400 return modified;
401 }
402
GetParentScope(uint32_t child_scope)403 uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
404 auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope);
405 assert(dbg_scope_itr != id_to_dbg_inst_.end());
406 OpenCLDebugInfo100Instructions debug_opcode =
407 dbg_scope_itr->second->GetOpenCL100DebugOpcode();
408 uint32_t parent_scope = kNoDebugScope;
409 switch (debug_opcode) {
410 case OpenCLDebugInfo100DebugFunction:
411 parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
412 kDebugFunctionOperandParentIndex);
413 break;
414 case OpenCLDebugInfo100DebugLexicalBlock:
415 parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
416 kDebugLexicalBlockOperandParentIndex);
417 break;
418 case OpenCLDebugInfo100DebugTypeComposite:
419 parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
420 kDebugTypeCompositeOperandParentIndex);
421 break;
422 case OpenCLDebugInfo100DebugCompilationUnit:
423 // DebugCompilationUnit does not have a parent scope.
424 break;
425 default:
426 assert(false &&
427 "Unreachable. A debug scope instruction must be "
428 "DebugFunction, DebugTypeComposite, DebugLexicalBlock, "
429 "or DebugCompilationUnit.");
430 break;
431 }
432 return parent_scope;
433 }
434
IsAncestorOfScope(uint32_t scope,uint32_t ancestor)435 bool DebugInfoManager::IsAncestorOfScope(uint32_t scope, uint32_t ancestor) {
436 uint32_t ancestor_scope_itr = scope;
437 while (ancestor_scope_itr != kNoDebugScope) {
438 if (ancestor == ancestor_scope_itr) return true;
439 ancestor_scope_itr = GetParentScope(ancestor_scope_itr);
440 }
441 return false;
442 }
443
IsDeclareVisibleToInstr(Instruction * dbg_declare,Instruction * scope)444 bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
445 Instruction* scope) {
446 assert(dbg_declare != nullptr);
447 assert(scope != nullptr);
448
449 std::vector<uint32_t> scope_ids;
450 if (scope->opcode() == SpvOpPhi) {
451 scope_ids.push_back(scope->GetDebugScope().GetLexicalScope());
452 for (uint32_t i = 0; i < scope->NumInOperands(); i += 2) {
453 auto* value = context()->get_def_use_mgr()->GetDef(
454 scope->GetSingleWordInOperand(i));
455 if (value != nullptr)
456 scope_ids.push_back(value->GetDebugScope().GetLexicalScope());
457 }
458 } else {
459 scope_ids.push_back(scope->GetDebugScope().GetLexicalScope());
460 }
461
462 uint32_t dbg_local_var_id =
463 dbg_declare->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex);
464 auto dbg_local_var_itr = id_to_dbg_inst_.find(dbg_local_var_id);
465 assert(dbg_local_var_itr != id_to_dbg_inst_.end());
466 uint32_t decl_scope_id = dbg_local_var_itr->second->GetSingleWordOperand(
467 kDebugLocalVariableOperandParentIndex);
468
469 // If the scope of DebugDeclare is an ancestor scope of the instruction's
470 // scope, the local variable is visible to the instruction.
471 for (uint32_t scope_id : scope_ids) {
472 if (scope_id != kNoDebugScope &&
473 IsAncestorOfScope(scope_id, decl_scope_id)) {
474 return true;
475 }
476 }
477 return false;
478 }
479
AddDebugValueIfVarDeclIsVisible(Instruction * scope_and_line,uint32_t variable_id,uint32_t value_id,Instruction * insert_pos,std::unordered_set<Instruction * > * invisible_decls)480 bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
481 Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
482 Instruction* insert_pos,
483 std::unordered_set<Instruction*>* invisible_decls) {
484 assert(scope_and_line != nullptr);
485
486 auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
487 if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return false;
488
489 bool modified = false;
490 for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
491 if (!IsDeclareVisibleToInstr(dbg_decl_or_val, scope_and_line)) {
492 if (invisible_decls) invisible_decls->insert(dbg_decl_or_val);
493 continue;
494 }
495
496 // Avoid inserting the new DebugValue between OpPhi or OpVariable
497 // instructions.
498 Instruction* insert_before = insert_pos->NextNode();
499 while (insert_before->opcode() == SpvOpPhi ||
500 insert_before->opcode() == SpvOpVariable) {
501 insert_before = insert_before->NextNode();
502 }
503 modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, insert_before,
504 scope_and_line) != nullptr;
505 }
506 return modified;
507 }
508
AddDebugValueForDecl(Instruction * dbg_decl,uint32_t value_id,Instruction * insert_before,Instruction * scope_and_line)509 Instruction* DebugInfoManager::AddDebugValueForDecl(
510 Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before,
511 Instruction* scope_and_line) {
512 if (dbg_decl == nullptr || !IsDebugDeclare(dbg_decl)) return nullptr;
513
514 std::unique_ptr<Instruction> dbg_val(dbg_decl->Clone(context()));
515 dbg_val->SetResultId(context()->TakeNextId());
516 dbg_val->SetInOperand(kExtInstInstructionInIdx,
517 {OpenCLDebugInfo100DebugValue});
518 dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id});
519 dbg_val->SetOperand(kDebugValueOperandExpressionIndex,
520 {GetEmptyDebugExpression()->result_id()});
521 dbg_val->UpdateDebugInfoFrom(scope_and_line);
522
523 auto* added_dbg_val = insert_before->InsertBefore(std::move(dbg_val));
524 AnalyzeDebugInst(added_dbg_val);
525 if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
526 context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_val);
527 if (context()->AreAnalysesValid(
528 IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
529 auto insert_blk = context()->get_instr_block(insert_before);
530 context()->set_instr_block(added_dbg_val, insert_blk);
531 }
532 return added_dbg_val;
533 }
534
GetVariableIdOfDebugValueUsedForDeclare(Instruction * inst)535 uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
536 Instruction* inst) {
537 if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0;
538
539 auto* expr =
540 GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
541 if (expr == nullptr) return 0;
542 if (expr->NumOperands() != kDebugExpressOperandOperationIndex + 1) return 0;
543
544 auto* operation = GetDbgInst(
545 expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex));
546 if (operation == nullptr) return 0;
547 if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) !=
548 OpenCLDebugInfo100Deref) {
549 return 0;
550 }
551
552 uint32_t var_id =
553 inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
554 if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) {
555 assert(false &&
556 "Checking a DebugValue can be used for declare needs DefUseManager");
557 return 0;
558 }
559
560 auto* var = context()->get_def_use_mgr()->GetDef(var_id);
561 if (var->opcode() == SpvOpVariable &&
562 SpvStorageClass(var->GetSingleWordOperand(
563 kOpVariableOperandStorageClassIndex)) == SpvStorageClassFunction) {
564 return var_id;
565 }
566 return 0;
567 }
568
IsDebugDeclare(Instruction * instr)569 bool DebugInfoManager::IsDebugDeclare(Instruction* instr) {
570 if (!instr->IsOpenCL100DebugInstr()) return false;
571 return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
572 GetVariableIdOfDebugValueUsedForDeclare(instr) != 0;
573 }
574
ReplaceAllUsesInDebugScopeWithPredicate(uint32_t before,uint32_t after,const std::function<bool (Instruction *)> & predicate)575 void DebugInfoManager::ReplaceAllUsesInDebugScopeWithPredicate(
576 uint32_t before, uint32_t after,
577 const std::function<bool(Instruction*)>& predicate) {
578 auto scope_id_to_users_itr = scope_id_to_users_.find(before);
579 if (scope_id_to_users_itr != scope_id_to_users_.end()) {
580 for (Instruction* inst : scope_id_to_users_itr->second) {
581 if (predicate(inst)) inst->UpdateLexicalScope(after);
582 }
583 scope_id_to_users_[after] = scope_id_to_users_itr->second;
584 scope_id_to_users_.erase(scope_id_to_users_itr);
585 }
586 auto inlinedat_id_to_users_itr = inlinedat_id_to_users_.find(before);
587 if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) {
588 for (Instruction* inst : inlinedat_id_to_users_itr->second) {
589 if (predicate(inst)) inst->UpdateDebugInlinedAt(after);
590 }
591 inlinedat_id_to_users_[after] = inlinedat_id_to_users_itr->second;
592 inlinedat_id_to_users_.erase(inlinedat_id_to_users_itr);
593 }
594 }
595
ClearDebugScopeAndInlinedAtUses(Instruction * inst)596 void DebugInfoManager::ClearDebugScopeAndInlinedAtUses(Instruction* inst) {
597 auto scope_id_to_users_itr = scope_id_to_users_.find(inst->result_id());
598 if (scope_id_to_users_itr != scope_id_to_users_.end()) {
599 scope_id_to_users_.erase(scope_id_to_users_itr);
600 }
601 auto inlinedat_id_to_users_itr =
602 inlinedat_id_to_users_.find(inst->result_id());
603 if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) {
604 inlinedat_id_to_users_.erase(inlinedat_id_to_users_itr);
605 }
606 }
607
AnalyzeDebugInst(Instruction * inst)608 void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) {
609 if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
610 auto& users = scope_id_to_users_[inst->GetDebugScope().GetLexicalScope()];
611 users.insert(inst);
612 }
613 if (inst->GetDebugInlinedAt() != kNoInlinedAt) {
614 auto& users = inlinedat_id_to_users_[inst->GetDebugInlinedAt()];
615 users.insert(inst);
616 }
617
618 if (!inst->IsOpenCL100DebugInstr()) return;
619
620 RegisterDbgInst(inst);
621
622 if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
623 assert(GetDebugFunction(inst->GetSingleWordOperand(
624 kDebugFunctionOperandFunctionIndex)) == nullptr &&
625 "Two DebugFunction instruction exists for a single OpFunction.");
626 RegisterDbgFunction(inst);
627 }
628
629 if (deref_operation_ == nullptr &&
630 inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
631 inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) ==
632 OpenCLDebugInfo100Deref) {
633 deref_operation_ = inst;
634 }
635
636 if (debug_info_none_inst_ == nullptr &&
637 inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
638 debug_info_none_inst_ = inst;
639 }
640
641 if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(inst)) {
642 empty_debug_expr_inst_ = inst;
643 }
644
645 if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
646 uint32_t var_id =
647 inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
648 RegisterDbgDeclare(var_id, inst);
649 }
650
651 if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(inst)) {
652 RegisterDbgDeclare(var_id, inst);
653 }
654 }
655
ConvertDebugGlobalToLocalVariable(Instruction * dbg_global_var,Instruction * local_var)656 void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
657 Instruction* dbg_global_var, Instruction* local_var) {
658 if (dbg_global_var->GetOpenCL100DebugOpcode() !=
659 OpenCLDebugInfo100DebugGlobalVariable) {
660 return;
661 }
662 assert(local_var->opcode() == SpvOpVariable ||
663 local_var->opcode() == SpvOpFunctionParameter);
664
665 // Convert |dbg_global_var| to DebugLocalVariable
666 dbg_global_var->SetInOperand(kExtInstInstructionInIdx,
667 {OpenCLDebugInfo100DebugLocalVariable});
668 auto flags = dbg_global_var->GetSingleWordOperand(
669 kDebugGlobalVariableOperandFlagsIndex);
670 for (uint32_t i = dbg_global_var->NumInOperands() - 1;
671 i >= kDebugLocalVariableOperandFlagsIndex; --i) {
672 dbg_global_var->RemoveOperand(i);
673 }
674 dbg_global_var->SetOperand(kDebugLocalVariableOperandFlagsIndex, {flags});
675 context()->ForgetUses(dbg_global_var);
676 context()->AnalyzeUses(dbg_global_var);
677
678 // Create a DebugDeclare
679 std::unique_ptr<Instruction> new_dbg_decl(new Instruction(
680 context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
681 context()->TakeNextId(),
682 {
683 {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
684 {context()
685 ->get_feature_mgr()
686 ->GetExtInstImportId_OpenCL100DebugInfo()}},
687 {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
688 {static_cast<uint32_t>(OpenCLDebugInfo100DebugDeclare)}},
689 {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
690 {dbg_global_var->result_id()}},
691 {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}},
692 {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
693 {GetEmptyDebugExpression()->result_id()}},
694 }));
695 auto* added_dbg_decl =
696 local_var->NextNode()->InsertBefore(std::move(new_dbg_decl));
697 if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
698 context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl);
699 if (context()->AreAnalysesValid(
700 IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
701 auto insert_blk = context()->get_instr_block(local_var);
702 context()->set_instr_block(added_dbg_decl, insert_blk);
703 }
704 }
705
AnalyzeDebugInsts(Module & module)706 void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
707 deref_operation_ = nullptr;
708 debug_info_none_inst_ = nullptr;
709 empty_debug_expr_inst_ = nullptr;
710 module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
711
712 // Move |empty_debug_expr_inst_| to the beginning of the debug instruction
713 // list.
714 if (empty_debug_expr_inst_ != nullptr &&
715 empty_debug_expr_inst_->PreviousNode() != nullptr &&
716 empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
717 empty_debug_expr_inst_->InsertBefore(
718 &*context()->module()->ext_inst_debuginfo_begin());
719 }
720
721 // Move |debug_info_none_inst_| to the beginning of the debug instruction
722 // list.
723 if (debug_info_none_inst_ != nullptr &&
724 debug_info_none_inst_->PreviousNode() != nullptr &&
725 debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
726 debug_info_none_inst_->InsertBefore(
727 &*context()->module()->ext_inst_debuginfo_begin());
728 }
729 }
730
ClearDebugInfo(Instruction * instr)731 void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
732 auto scope_id_to_users_itr =
733 scope_id_to_users_.find(instr->GetDebugScope().GetLexicalScope());
734 if (scope_id_to_users_itr != scope_id_to_users_.end()) {
735 scope_id_to_users_itr->second.erase(instr);
736 }
737 auto inlinedat_id_to_users_itr =
738 inlinedat_id_to_users_.find(instr->GetDebugInlinedAt());
739 if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) {
740 inlinedat_id_to_users_itr->second.erase(instr);
741 }
742
743 if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) {
744 return;
745 }
746
747 id_to_dbg_inst_.erase(instr->result_id());
748
749 if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
750 auto fn_id =
751 instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
752 fn_id_to_dbg_fn_.erase(fn_id);
753 }
754
755 if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
756 instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
757 auto var_or_value_id =
758 instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
759 auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id);
760 if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
761 dbg_decl_itr->second.erase(instr);
762 }
763 }
764
765 if (deref_operation_ == instr) {
766 deref_operation_ = nullptr;
767 for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
768 dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
769 ++dbg_instr_itr) {
770 if (instr != &*dbg_instr_itr &&
771 dbg_instr_itr->GetOpenCL100DebugOpcode() ==
772 OpenCLDebugInfo100DebugOperation &&
773 dbg_instr_itr->GetSingleWordOperand(
774 kDebugOperationOperandOperationIndex) ==
775 OpenCLDebugInfo100Deref) {
776 deref_operation_ = &*dbg_instr_itr;
777 break;
778 }
779 }
780 }
781
782 if (debug_info_none_inst_ == instr) {
783 debug_info_none_inst_ = nullptr;
784 for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
785 dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
786 ++dbg_instr_itr) {
787 if (instr != &*dbg_instr_itr &&
788 dbg_instr_itr->GetOpenCL100DebugOpcode() ==
789 OpenCLDebugInfo100DebugInfoNone) {
790 debug_info_none_inst_ = &*dbg_instr_itr;
791 break;
792 }
793 }
794 }
795
796 if (empty_debug_expr_inst_ == instr) {
797 empty_debug_expr_inst_ = nullptr;
798 for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
799 dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
800 ++dbg_instr_itr) {
801 if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) {
802 empty_debug_expr_inst_ = &*dbg_instr_itr;
803 break;
804 }
805 }
806 }
807 }
808
809 } // namespace analysis
810 } // namespace opt
811 } // namespace spvtools
812