SPIRV-Tools/source/opt/debug_info_manager.cpp
Jaebaek Seo fd3948e161
Add DebugValue for function param regardless of scope (#3923)
`DebugInfoManager::AddDebugValueIfVarDeclIsVisible()` adds
OpenCL.DebugInfo.100 DebugValue from DebugDeclare only when the
DebugDeclare is visible to the give scope. It helps us correctly
handle a reference variable e.g.,

{ // scope #1.
  int foo = /* init */;
  { // scope #2.
    int& bar = foo;
    ...

in the above code, we must not propagate DebugValue of `int& bar` for
store instructions in the scope #1 because it is alive only in
the scope #2.

We have an exception: If the given DebugDeclare is used for a function
parameter, `DebugInfoManager::AddDebugValueIfVarDeclIsVisible()` has
to always add DebugValue instruction regardless
of the scope. It is because the initializer (store instruction) for
the function parameter can be out of the function parameter's scope
(the function) in particular when the function was inlined.

Without this change, the function parameter value information always
disappears whenever we run the function inlining pass and
`DebugInfoManager::AddDebugValueIfVarDeclIsVisible()`.
2020-10-16 10:18:41 -04:00

831 lines
33 KiB
C++

// 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 <cassert>
#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 kDebugValueOperandLocalVariableIndex = 4;
static const uint32_t kDebugValueOperandExpressionIndex = 6;
static const uint32_t kDebugValueOperandIndexesIndex = 7;
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;
static const uint32_t kDebugLocalVariableOperandArgNumberIndex = 11;
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<Instruction> 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<uint32_t>(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<Instruction> 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<uint32_t>(OpenCLDebugInfo100DebugOperation)}},
{SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,
{static_cast<uint32_t>(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<Instruction> 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<Instruction> 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<uint32_t>(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<Instruction> 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<uint32_t>(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<Instruction> 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();
}
void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
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);
}
var_id_to_dbg_decl_.erase(dbg_decl_itr);
}
}
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;
}
Instruction* DebugInfoManager::GetDebugLocalVariableFromDeclare(
Instruction* dbg_declare) {
assert(dbg_declare);
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());
return dbg_local_var_itr->second;
}
bool DebugInfoManager::IsFunctionParameter(Instruction* dbg_local_var) const {
// If a DebugLocalVariable has ArgNumber operand, it is a function parameter.
return dbg_local_var->NumOperands() >
kDebugLocalVariableOperandArgNumberIndex;
}
bool DebugInfoManager::IsLocalVariableVisibleToInstr(Instruction* dbg_local_var,
uint32_t instr_scope_id) {
if (instr_scope_id == kNoDebugScope) return false;
uint32_t decl_scope_id = dbg_local_var->GetSingleWordOperand(
kDebugLocalVariableOperandParentIndex);
// If the scope of DebugDeclare is an ancestor scope of the instruction's
// scope, the local variable is visible to the instruction.
return IsAncestorOfScope(instr_scope_id, decl_scope_id);
}
Instruction* DebugInfoManager::AddDebugValueWithIndex(
uint32_t dbg_local_var_id, uint32_t value_id, uint32_t expr_id,
uint32_t index_id, Instruction* insert_before) {
uint32_t result_id = context()->TakeNextId();
if (!result_id) return nullptr;
std::unique_ptr<Instruction> new_dbg_value(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<uint32_t>(OpenCLDebugInfo100DebugValue)}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {dbg_local_var_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{expr_id == 0 ? GetEmptyDebugExpression()->result_id() : expr_id}},
}));
if (index_id) {
new_dbg_value->AddOperand(
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {index_id}});
}
Instruction* added_dbg_value =
insert_before->InsertBefore(std::move(new_dbg_value));
AnalyzeDebugInst(added_dbg_value);
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
if (context()->AreAnalysesValid(
IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
auto insert_blk = context()->get_instr_block(insert_before);
context()->set_instr_block(added_dbg_value, insert_blk);
}
return added_dbg_value;
}
void DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
Instruction* insert_pos) {
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
// If it declares a function parameter, the store instruction for the
// function parameter can exist out of the function parameter's scope
// because of the function inlining. We always add DebugValue for a
// function parameter next to the DebugDeclare regardless of the scope.
auto* dbg_local_var = GetDebugLocalVariableFromDeclare(dbg_decl_or_val);
bool is_function_param = IsFunctionParameter(dbg_local_var);
if (!is_function_param &&
!IsLocalVariableVisibleToInstr(dbg_local_var, instr_scope_id))
continue;
// Avoid inserting the new DebugValue between OpPhi or OpVariable
// instructions.
Instruction* insert_before = is_function_param ? dbg_decl_or_val->NextNode()
: insert_pos->NextNode();
while (insert_before->opcode() == SpvOpPhi ||
insert_before->opcode() == SpvOpVariable) {
insert_before = insert_before->NextNode();
}
uint32_t index_id = 0;
if (dbg_decl_or_val->NumOperands() > kDebugValueOperandIndexesIndex) {
index_id =
dbg_decl_or_val->GetSingleWordOperand(kDebugValueOperandIndexesIndex);
}
Instruction* added_dbg_value =
AddDebugValueWithIndex(dbg_decl_or_val->GetSingleWordOperand(
kDebugValueOperandLocalVariableIndex),
value_id, 0, index_id, insert_before);
assert(added_dbg_value != nullptr);
added_dbg_value->UpdateDebugInfoFrom(is_function_param ? dbg_decl_or_val
: scope_and_line);
AnalyzeDebugInst(added_dbg_value);
}
}
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<bool(Instruction*)>& 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<Instruction> 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<uint32_t>(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