// Copyright (c) 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "source/opt/debug_info_manager.h" #include #include "source/opt/ir_context.h" // Constants for OpenCL.DebugInfo.100 extension instructions. static const uint32_t kOpLineOperandLineIndex = 1; static const uint32_t kLineOperandIndexDebugFunction = 7; static const uint32_t kLineOperandIndexDebugLexicalBlock = 5; static const uint32_t kDebugFunctionOperandFunctionIndex = 13; static const uint32_t kDebugFunctionOperandParentIndex = 9; static const uint32_t kDebugTypeCompositeOperandParentIndex = 9; static const uint32_t kDebugLexicalBlockOperandParentIndex = 7; static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6; static const uint32_t kDebugExpressOperandOperationIndex = 4; static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4; static const uint32_t kDebugDeclareOperandVariableIndex = 5; static const uint32_t kDebugValueOperandExpressionIndex = 6; static const uint32_t kDebugOperationOperandOperationIndex = 4; static const uint32_t kOpVariableOperandStorageClassIndex = 2; static const uint32_t kDebugLocalVariableOperandParentIndex = 9; static const uint32_t kExtInstInstructionInIdx = 1; static const uint32_t kDebugGlobalVariableOperandFlagsIndex = 12; static const uint32_t kDebugLocalVariableOperandFlagsIndex = 10; namespace spvtools { namespace opt { namespace analysis { namespace { void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) { assert(dbg_inlined_at); assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInlinedAt); if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) { dbg_inlined_at->AddOperand( {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}}); } else { dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex, {inlined_operand}); } } uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) { assert(dbg_inlined_at); assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInlinedAt); if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) return kNoInlinedAt; return dbg_inlined_at->GetSingleWordOperand( kDebugInlinedAtOperandInlinedIndex); } bool IsEmptyDebugExpression(Instruction* instr) { return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugExpression && instr->NumOperands() == kDebugExpressOperandOperationIndex; } } // namespace DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) { AnalyzeDebugInsts(*c->module()); } Instruction* DebugInfoManager::GetDbgInst(uint32_t id) { auto dbg_inst_it = id_to_dbg_inst_.find(id); return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second; } void DebugInfoManager::RegisterDbgInst(Instruction* inst) { assert( inst->NumInOperands() != 0 && context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == inst->GetInOperand(0).words[0] && "Given instruction is not a debug instruction"); id_to_dbg_inst_[inst->result_id()] = inst; } void DebugInfoManager::RegisterDbgFunction(Instruction* inst) { assert(inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction && "inst is not a DebugFunction"); auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); // Do not register function that has been optimized away auto fn_inst = GetDbgInst(fn_id); if (fn_inst != nullptr) { assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone); return; } assert( fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && "Register DebugFunction for a function that already has DebugFunction"); fn_id_to_dbg_fn_[fn_id] = inst; } void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare) { assert(dbg_declare->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || dbg_declare->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue); auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id); if (dbg_decl_itr == var_id_to_dbg_decl_.end()) { var_id_to_dbg_decl_[var_id] = {dbg_declare}; } else { dbg_decl_itr->second.insert(dbg_declare); } } uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, const DebugScope& scope) { if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == 0) return kNoInlinedAt; uint32_t line_number = 0; if (line == nullptr) { auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope()); if (lexical_scope_inst == nullptr) return kNoInlinedAt; OpenCLDebugInfo100Instructions debug_opcode = lexical_scope_inst->GetOpenCL100DebugOpcode(); switch (debug_opcode) { case OpenCLDebugInfo100DebugFunction: line_number = lexical_scope_inst->GetSingleWordOperand( kLineOperandIndexDebugFunction); break; case OpenCLDebugInfo100DebugLexicalBlock: line_number = lexical_scope_inst->GetSingleWordOperand( kLineOperandIndexDebugLexicalBlock); break; case OpenCLDebugInfo100DebugTypeComposite: case OpenCLDebugInfo100DebugCompilationUnit: assert(false && "DebugTypeComposite and DebugCompilationUnit are lexical " "scopes, but we inline functions into a function or a block " "of a function, not into a struct/class or a global scope."); break; default: assert(false && "Unreachable. a debug extension instruction for a " "lexical scope must be DebugFunction, DebugTypeComposite, " "DebugLexicalBlock, or DebugCompilationUnit."); break; } } else { line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex); } uint32_t result_id = context()->TakeNextId(); std::unique_ptr inlined_at(new Instruction( context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {context() ->get_feature_mgr() ->GetExtInstImportId_OpenCL100DebugInfo()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {static_cast(OpenCLDebugInfo100DebugInlinedAt)}}, {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}}, })); // |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt // into the Inlined operand of this new DebugInlinedAt. if (scope.GetInlinedAt() != kNoInlinedAt) { inlined_at->AddOperand( {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetInlinedAt()}}); } RegisterDbgInst(inlined_at.get()); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(inlined_at.get()); context()->module()->AddExtInstDebugInfo(std::move(inlined_at)); return result_id; } DebugScope DebugInfoManager::BuildDebugScope( const DebugScope& callee_instr_scope, DebugInlinedAtContext* inlined_at_ctx) { return DebugScope(callee_instr_scope.GetLexicalScope(), BuildDebugInlinedAtChain(callee_instr_scope.GetInlinedAt(), inlined_at_ctx)); } uint32_t DebugInfoManager::BuildDebugInlinedAtChain( uint32_t callee_inlined_at, DebugInlinedAtContext* inlined_at_ctx) { if (inlined_at_ctx->GetScopeOfCallInstruction().GetLexicalScope() == kNoDebugScope) return kNoInlinedAt; // Reuse the already generated DebugInlinedAt chain if exists. uint32_t already_generated_chain_head_id = inlined_at_ctx->GetDebugInlinedAtChain(callee_inlined_at); if (already_generated_chain_head_id != kNoInlinedAt) { return already_generated_chain_head_id; } const uint32_t new_dbg_inlined_at_id = CreateDebugInlinedAt(inlined_at_ctx->GetLineOfCallInstruction(), inlined_at_ctx->GetScopeOfCallInstruction()); if (new_dbg_inlined_at_id == kNoInlinedAt) return kNoInlinedAt; if (callee_inlined_at == kNoInlinedAt) { inlined_at_ctx->SetDebugInlinedAtChain(kNoInlinedAt, new_dbg_inlined_at_id); return new_dbg_inlined_at_id; } uint32_t chain_head_id = kNoInlinedAt; uint32_t chain_iter_id = callee_inlined_at; Instruction* last_inlined_at_in_chain = nullptr; do { Instruction* new_inlined_at_in_chain = CloneDebugInlinedAt( chain_iter_id, /* insert_before */ last_inlined_at_in_chain); assert(new_inlined_at_in_chain != nullptr); // Set DebugInlinedAt of the new scope as the head of the chain. if (chain_head_id == kNoInlinedAt) chain_head_id = new_inlined_at_in_chain->result_id(); // Previous DebugInlinedAt of the chain must point to the new // DebugInlinedAt as its Inlined operand to build a recursive // chain. if (last_inlined_at_in_chain != nullptr) { SetInlinedOperand(last_inlined_at_in_chain, new_inlined_at_in_chain->result_id()); } last_inlined_at_in_chain = new_inlined_at_in_chain; chain_iter_id = GetInlinedOperand(new_inlined_at_in_chain); } while (chain_iter_id != kNoInlinedAt); // Put |new_dbg_inlined_at_id| into the end of the chain. SetInlinedOperand(last_inlined_at_in_chain, new_dbg_inlined_at_id); // Keep the new chain information that will be reused it. inlined_at_ctx->SetDebugInlinedAtChain(callee_inlined_at, chain_head_id); return chain_head_id; } Instruction* DebugInfoManager::GetDebugOperationWithDeref() { if (deref_operation_ != nullptr) return deref_operation_; uint32_t result_id = context()->TakeNextId(); std::unique_ptr deref_operation(new Instruction( context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { {SPV_OPERAND_TYPE_ID, {context() ->get_feature_mgr() ->GetExtInstImportId_OpenCL100DebugInfo()}}, {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {static_cast(OpenCLDebugInfo100DebugOperation)}}, {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, {static_cast(OpenCLDebugInfo100Deref)}}, })); // Add to the front of |ext_inst_debuginfo_|. deref_operation_ = context()->module()->ext_inst_debuginfo_begin()->InsertBefore( std::move(deref_operation)); RegisterDbgInst(deref_operation_); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_operation_); return deref_operation_; } Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) { assert(dbg_expr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugExpression); std::unique_ptr deref_expr(dbg_expr->Clone(context())); deref_expr->SetResultId(context()->TakeNextId()); deref_expr->InsertOperand( kDebugExpressOperandOperationIndex, {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}}); auto* deref_expr_instr = context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr)); AnalyzeDebugInst(deref_expr_instr); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_expr_instr); return deref_expr_instr; } Instruction* DebugInfoManager::GetDebugInfoNone() { if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_; uint32_t result_id = context()->TakeNextId(); std::unique_ptr dbg_info_none_inst(new Instruction( context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {context() ->get_feature_mgr() ->GetExtInstImportId_OpenCL100DebugInfo()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {static_cast(OpenCLDebugInfo100DebugInfoNone)}}, })); // Add to the front of |ext_inst_debuginfo_|. debug_info_none_inst_ = context()->module()->ext_inst_debuginfo_begin()->InsertBefore( std::move(dbg_info_none_inst)); RegisterDbgInst(debug_info_none_inst_); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(debug_info_none_inst_); return debug_info_none_inst_; } Instruction* DebugInfoManager::GetEmptyDebugExpression() { if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_; uint32_t result_id = context()->TakeNextId(); std::unique_ptr empty_debug_expr(new Instruction( context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {context() ->get_feature_mgr() ->GetExtInstImportId_OpenCL100DebugInfo()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {static_cast(OpenCLDebugInfo100DebugExpression)}}, })); // Add to the front of |ext_inst_debuginfo_|. empty_debug_expr_inst_ = context()->module()->ext_inst_debuginfo_begin()->InsertBefore( std::move(empty_debug_expr)); RegisterDbgInst(empty_debug_expr_inst_); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(empty_debug_expr_inst_); return empty_debug_expr_inst_; } Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) { auto* inlined_at = GetDbgInst(dbg_inlined_at_id); if (inlined_at == nullptr) return nullptr; if (inlined_at->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugInlinedAt) { return nullptr; } return inlined_at; } Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id, Instruction* insert_before) { auto* inlined_at = GetDebugInlinedAt(clone_inlined_at_id); if (inlined_at == nullptr) return nullptr; std::unique_ptr new_inlined_at(inlined_at->Clone(context())); new_inlined_at->SetResultId(context()->TakeNextId()); RegisterDbgInst(new_inlined_at.get()); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get()); if (insert_before != nullptr) return insert_before->InsertBefore(std::move(new_inlined_at)); return context()->module()->ext_inst_debuginfo_end()->InsertBefore( std::move(new_inlined_at)); } bool DebugInfoManager::IsVariableDebugDeclared(uint32_t variable_id) { auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); return dbg_decl_itr != var_id_to_dbg_decl_.end(); } bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) { bool modified = false; auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); if (dbg_decl_itr != var_id_to_dbg_decl_.end()) { // We intentionally copy the list of DebugDeclare instructions because // context()->KillInst(dbg_decl) will update |var_id_to_dbg_decl_|. If we // directly use |dbg_decl_itr->second|, it accesses a dangling pointer. auto copy_dbg_decls = dbg_decl_itr->second; for (auto* dbg_decl : copy_dbg_decls) { context()->KillInst(dbg_decl); modified = true; } var_id_to_dbg_decl_.erase(dbg_decl_itr); } return modified; } uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) { auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope); assert(dbg_scope_itr != id_to_dbg_inst_.end()); OpenCLDebugInfo100Instructions debug_opcode = dbg_scope_itr->second->GetOpenCL100DebugOpcode(); uint32_t parent_scope = kNoDebugScope; switch (debug_opcode) { case OpenCLDebugInfo100DebugFunction: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugFunctionOperandParentIndex); break; case OpenCLDebugInfo100DebugLexicalBlock: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugLexicalBlockOperandParentIndex); break; case OpenCLDebugInfo100DebugTypeComposite: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugTypeCompositeOperandParentIndex); break; case OpenCLDebugInfo100DebugCompilationUnit: // DebugCompilationUnit does not have a parent scope. break; default: assert(false && "Unreachable. A debug scope instruction must be " "DebugFunction, DebugTypeComposite, DebugLexicalBlock, " "or DebugCompilationUnit."); break; } return parent_scope; } bool DebugInfoManager::IsAncestorOfScope(uint32_t scope, uint32_t ancestor) { uint32_t ancestor_scope_itr = scope; while (ancestor_scope_itr != kNoDebugScope) { if (ancestor == ancestor_scope_itr) return true; ancestor_scope_itr = GetParentScope(ancestor_scope_itr); } return false; } bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare, Instruction* scope) { assert(dbg_declare != nullptr); assert(scope != nullptr); std::vector scope_ids; if (scope->opcode() == SpvOpPhi) { scope_ids.push_back(scope->GetDebugScope().GetLexicalScope()); for (uint32_t i = 0; i < scope->NumInOperands(); i += 2) { auto* value = context()->get_def_use_mgr()->GetDef( scope->GetSingleWordInOperand(i)); if (value != nullptr) scope_ids.push_back(value->GetDebugScope().GetLexicalScope()); } } else { scope_ids.push_back(scope->GetDebugScope().GetLexicalScope()); } uint32_t dbg_local_var_id = dbg_declare->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex); auto dbg_local_var_itr = id_to_dbg_inst_.find(dbg_local_var_id); assert(dbg_local_var_itr != id_to_dbg_inst_.end()); uint32_t decl_scope_id = dbg_local_var_itr->second->GetSingleWordOperand( kDebugLocalVariableOperandParentIndex); // If the scope of DebugDeclare is an ancestor scope of the instruction's // scope, the local variable is visible to the instruction. for (uint32_t scope_id : scope_ids) { if (scope_id != kNoDebugScope && IsAncestorOfScope(scope_id, decl_scope_id)) { return true; } } return false; } bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible( Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id, Instruction* insert_pos, std::unordered_set* invisible_decls) { assert(scope_and_line != nullptr); auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return false; bool modified = false; for (auto* dbg_decl_or_val : dbg_decl_itr->second) { if (!IsDeclareVisibleToInstr(dbg_decl_or_val, scope_and_line)) { if (invisible_decls) invisible_decls->insert(dbg_decl_or_val); continue; } // Avoid inserting the new DebugValue between OpPhi or OpVariable // instructions. Instruction* insert_before = insert_pos->NextNode(); while (insert_before->opcode() == SpvOpPhi || insert_before->opcode() == SpvOpVariable) { insert_before = insert_before->NextNode(); } modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, insert_before, scope_and_line) != nullptr; } return modified; } Instruction* DebugInfoManager::AddDebugValueForDecl( Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before, Instruction* scope_and_line) { if (dbg_decl == nullptr || !IsDebugDeclare(dbg_decl)) return nullptr; std::unique_ptr dbg_val(dbg_decl->Clone(context())); dbg_val->SetResultId(context()->TakeNextId()); dbg_val->SetInOperand(kExtInstInstructionInIdx, {OpenCLDebugInfo100DebugValue}); dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id}); dbg_val->SetOperand(kDebugValueOperandExpressionIndex, {GetEmptyDebugExpression()->result_id()}); dbg_val->UpdateDebugInfoFrom(scope_and_line); auto* added_dbg_val = insert_before->InsertBefore(std::move(dbg_val)); AnalyzeDebugInst(added_dbg_val); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_val); if (context()->AreAnalysesValid( IRContext::Analysis::kAnalysisInstrToBlockMapping)) { auto insert_blk = context()->get_instr_block(insert_before); context()->set_instr_block(added_dbg_val, insert_blk); } return added_dbg_val; } uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( Instruction* inst) { if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0; auto* expr = GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex)); if (expr == nullptr) return 0; if (expr->NumOperands() != kDebugExpressOperandOperationIndex + 1) return 0; auto* operation = GetDbgInst( expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex)); if (operation == nullptr) return 0; if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) != OpenCLDebugInfo100Deref) { return 0; } uint32_t var_id = inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) { assert(false && "Checking a DebugValue can be used for declare needs DefUseManager"); return 0; } auto* var = context()->get_def_use_mgr()->GetDef(var_id); if (var->opcode() == SpvOpVariable && SpvStorageClass(var->GetSingleWordOperand( kOpVariableOperandStorageClassIndex)) == SpvStorageClassFunction) { return var_id; } return 0; } bool DebugInfoManager::IsDebugDeclare(Instruction* instr) { if (!instr->IsOpenCL100DebugInstr()) return false; return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || GetVariableIdOfDebugValueUsedForDeclare(instr) != 0; } void DebugInfoManager::ReplaceAllUsesInDebugScopeWithPredicate( uint32_t before, uint32_t after, const std::function& predicate) { auto scope_id_to_users_itr = scope_id_to_users_.find(before); if (scope_id_to_users_itr != scope_id_to_users_.end()) { for (Instruction* inst : scope_id_to_users_itr->second) { if (predicate(inst)) inst->UpdateLexicalScope(after); } scope_id_to_users_[after] = scope_id_to_users_itr->second; scope_id_to_users_.erase(scope_id_to_users_itr); } auto inlinedat_id_to_users_itr = inlinedat_id_to_users_.find(before); if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) { for (Instruction* inst : inlinedat_id_to_users_itr->second) { if (predicate(inst)) inst->UpdateDebugInlinedAt(after); } inlinedat_id_to_users_[after] = inlinedat_id_to_users_itr->second; inlinedat_id_to_users_.erase(inlinedat_id_to_users_itr); } } void DebugInfoManager::ClearDebugScopeAndInlinedAtUses(Instruction* inst) { auto scope_id_to_users_itr = scope_id_to_users_.find(inst->result_id()); if (scope_id_to_users_itr != scope_id_to_users_.end()) { scope_id_to_users_.erase(scope_id_to_users_itr); } auto inlinedat_id_to_users_itr = inlinedat_id_to_users_.find(inst->result_id()); if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) { inlinedat_id_to_users_.erase(inlinedat_id_to_users_itr); } } void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) { auto& users = scope_id_to_users_[inst->GetDebugScope().GetLexicalScope()]; users.insert(inst); } if (inst->GetDebugInlinedAt() != kNoInlinedAt) { auto& users = inlinedat_id_to_users_[inst->GetDebugInlinedAt()]; users.insert(inst); } if (!inst->IsOpenCL100DebugInstr()) return; RegisterDbgInst(inst); if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { assert(GetDebugFunction(inst->GetSingleWordOperand( kDebugFunctionOperandFunctionIndex)) == nullptr && "Two DebugFunction instruction exists for a single OpFunction."); RegisterDbgFunction(inst); } if (deref_operation_ == nullptr && inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation && inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) == OpenCLDebugInfo100Deref) { deref_operation_ = inst; } if (debug_info_none_inst_ == nullptr && inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { debug_info_none_inst_ = inst; } if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(inst)) { empty_debug_expr_inst_ = inst; } if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { uint32_t var_id = inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); RegisterDbgDeclare(var_id, inst); } if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(inst)) { RegisterDbgDeclare(var_id, inst); } } void DebugInfoManager::ConvertDebugGlobalToLocalVariable( Instruction* dbg_global_var, Instruction* local_var) { if (dbg_global_var->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable) { return; } assert(local_var->opcode() == SpvOpVariable || local_var->opcode() == SpvOpFunctionParameter); // Convert |dbg_global_var| to DebugLocalVariable dbg_global_var->SetInOperand(kExtInstInstructionInIdx, {OpenCLDebugInfo100DebugLocalVariable}); auto flags = dbg_global_var->GetSingleWordOperand( kDebugGlobalVariableOperandFlagsIndex); for (uint32_t i = dbg_global_var->NumInOperands() - 1; i >= kDebugLocalVariableOperandFlagsIndex; --i) { dbg_global_var->RemoveOperand(i); } dbg_global_var->SetOperand(kDebugLocalVariableOperandFlagsIndex, {flags}); context()->ForgetUses(dbg_global_var); context()->AnalyzeUses(dbg_global_var); // Create a DebugDeclare std::unique_ptr new_dbg_decl(new Instruction( context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), context()->TakeNextId(), { {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {context() ->get_feature_mgr() ->GetExtInstImportId_OpenCL100DebugInfo()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {static_cast(OpenCLDebugInfo100DebugDeclare)}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {dbg_global_var->result_id()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetEmptyDebugExpression()->result_id()}}, })); auto* added_dbg_decl = local_var->NextNode()->InsertBefore(std::move(new_dbg_decl)); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl); if (context()->AreAnalysesValid( IRContext::Analysis::kAnalysisInstrToBlockMapping)) { auto insert_blk = context()->get_instr_block(local_var); context()->set_instr_block(added_dbg_decl, insert_blk); } } void DebugInfoManager::AnalyzeDebugInsts(Module& module) { deref_operation_ = nullptr; debug_info_none_inst_ = nullptr; empty_debug_expr_inst_ = nullptr; module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); }); // Move |empty_debug_expr_inst_| to the beginning of the debug instruction // list. if (empty_debug_expr_inst_ != nullptr && empty_debug_expr_inst_->PreviousNode() != nullptr && empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { empty_debug_expr_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } // Move |debug_info_none_inst_| to the beginning of the debug instruction // list. if (debug_info_none_inst_ != nullptr && debug_info_none_inst_->PreviousNode() != nullptr && debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { debug_info_none_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } } void DebugInfoManager::ClearDebugInfo(Instruction* instr) { auto scope_id_to_users_itr = scope_id_to_users_.find(instr->GetDebugScope().GetLexicalScope()); if (scope_id_to_users_itr != scope_id_to_users_.end()) { scope_id_to_users_itr->second.erase(instr); } auto inlinedat_id_to_users_itr = inlinedat_id_to_users_.find(instr->GetDebugInlinedAt()); if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) { inlinedat_id_to_users_itr->second.erase(instr); } if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) { return; } id_to_dbg_inst_.erase(instr->result_id()); if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { auto fn_id = instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); fn_id_to_dbg_fn_.erase(fn_id); } if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { auto var_or_value_id = instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id); if (dbg_decl_itr != var_id_to_dbg_decl_.end()) { dbg_decl_itr->second.erase(instr); } } if (deref_operation_ == instr) { deref_operation_ = nullptr; for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); ++dbg_instr_itr) { if (instr != &*dbg_instr_itr && dbg_instr_itr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation && dbg_instr_itr->GetSingleWordOperand( kDebugOperationOperandOperationIndex) == OpenCLDebugInfo100Deref) { deref_operation_ = &*dbg_instr_itr; break; } } } if (debug_info_none_inst_ == instr) { debug_info_none_inst_ = nullptr; for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); ++dbg_instr_itr) { if (instr != &*dbg_instr_itr && dbg_instr_itr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { debug_info_none_inst_ = &*dbg_instr_itr; break; } } } if (empty_debug_expr_inst_ == instr) { empty_debug_expr_inst_ = nullptr; for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); ++dbg_instr_itr) { if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) { empty_debug_expr_inst_ = &*dbg_instr_itr; break; } } } } } // namespace analysis } // namespace opt } // namespace spvtools