mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 13:20:05 +00:00
[spirv-opt] debug info preservation in ssa-rewrite (#3356)
Add OpenCL.DebugInfo.100 `DebugValue` instructions for store and phi instructions of local variables to provide the debugger with the updated values of local variables correctly.
This commit is contained in:
parent
2a1b8c0622
commit
d4b9f576eb
@ -24,7 +24,18 @@ 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 kDebugOperationOperandOperationIndex = 4;
|
||||
static const uint32_t kOpVariableOperandStorageClassIndex = 2;
|
||||
static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -36,7 +47,8 @@ void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) {
|
||||
assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugInlinedAt);
|
||||
if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) {
|
||||
dbg_inlined_at->AddOperand({SPV_OPERAND_TYPE_RESULT_ID, {inlined_operand}});
|
||||
dbg_inlined_at->AddOperand(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}});
|
||||
} else {
|
||||
dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex,
|
||||
{inlined_operand});
|
||||
@ -53,6 +65,12 @@ uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) {
|
||||
kDebugInlinedAtOperandInlinedIndex);
|
||||
}
|
||||
|
||||
bool IsEmptyDebugExpression(Instruction* instr) {
|
||||
return instr->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugExpression &&
|
||||
instr->NumOperands() == kDebugExpressOperandOperationIndex;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
|
||||
@ -83,6 +101,20 @@ void DebugInfoManager::RegisterDbgFunction(Instruction* inst) {
|
||||
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.push_back(dbg_declare);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
|
||||
const DebugScope& scope) {
|
||||
if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
|
||||
@ -139,10 +171,12 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
|
||||
// |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_RESULT_ID,
|
||||
{scope.GetInlinedAt()}});
|
||||
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;
|
||||
}
|
||||
@ -218,11 +252,11 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
|
||||
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
result_id,
|
||||
{
|
||||
{SPV_OPERAND_TYPE_RESULT_ID,
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{context()
|
||||
->get_feature_mgr()
|
||||
->GetExtInstImportId_OpenCL100DebugInfo()}},
|
||||
{SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
|
||||
{static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
|
||||
}));
|
||||
|
||||
@ -232,9 +266,38 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
|
||||
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;
|
||||
@ -252,12 +315,162 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
|
||||
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));
|
||||
}
|
||||
|
||||
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,
|
||||
uint32_t instr_scope_id) {
|
||||
if (instr_scope_id == kNoDebugScope) return false;
|
||||
|
||||
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.
|
||||
return IsAncestorOfScope(instr_scope_id, decl_scope_id);
|
||||
}
|
||||
|
||||
void DebugInfoManager::AddDebugValue(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 (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
|
||||
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
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_decl_or_val->GetSingleWordOperand(
|
||||
kDebugValueOperandLocalVariableIndex)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{GetEmptyDebugExpression()->result_id()}},
|
||||
}));
|
||||
|
||||
if (dbg_decl_or_val->NumOperands() >
|
||||
kDebugValueOperandExpressionIndex + 1) {
|
||||
assert(dbg_decl_or_val->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugValue);
|
||||
for (uint32_t i = kDebugValueOperandExpressionIndex + 1;
|
||||
i < dbg_decl_or_val->NumOperands(); ++i) {
|
||||
new_dbg_value->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{dbg_decl_or_val->GetSingleWordOperand(i)}});
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
Instruction* added_dbg_value =
|
||||
insert_before->InsertBefore(std::move(new_dbg_value));
|
||||
added_dbg_value->UpdateDebugInfo(scope_and_line);
|
||||
AnalyzeDebugInst(added_dbg_value);
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(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;
|
||||
}
|
||||
|
||||
void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
|
||||
if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
|
||||
return;
|
||||
@ -275,12 +488,37 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
|
||||
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
|
||||
debug_info_none_inst_ = dbg_inst;
|
||||
}
|
||||
|
||||
if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(dbg_inst)) {
|
||||
empty_debug_expr_inst_ = dbg_inst;
|
||||
}
|
||||
|
||||
if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
|
||||
uint32_t var_id =
|
||||
dbg_inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
|
||||
RegisterDbgDeclare(var_id, dbg_inst);
|
||||
}
|
||||
|
||||
if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(dbg_inst)) {
|
||||
RegisterDbgDeclare(var_id, dbg_inst);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
|
||||
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()->GetOpenCL100DebugOpcode() !=
|
||||
OpenCLDebugInfo100InstructionsMax) {
|
||||
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 &&
|
||||
@ -292,6 +530,50 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
|
||||
}
|
||||
}
|
||||
|
||||
void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
|
||||
if (instr == nullptr ||
|
||||
instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax) {
|
||||
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) {
|
||||
auto var_id =
|
||||
instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
|
||||
var_id_to_dbg_decl_.erase(var_id);
|
||||
}
|
||||
|
||||
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 (dbg_instr_itr->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugInfoNone) {
|
||||
debug_info_none_inst_ = &*dbg_instr_itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (IsEmptyDebugExpression(&*dbg_instr_itr)) {
|
||||
empty_debug_expr_inst_ = &*dbg_instr_itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace analysis
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/opt/module.h"
|
||||
@ -128,6 +129,15 @@ class DebugInfoManager {
|
||||
uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
|
||||
DebugInlinedAtContext* inlined_at_ctx);
|
||||
|
||||
// Generates a DebugValue instruction with value |value_id| for every local
|
||||
// variable that is in the scope of |scope_and_line| and whose memory is
|
||||
// |variable_id| and inserts it after the instruction |insert_pos|.
|
||||
void AddDebugValue(Instruction* scope_and_line, uint32_t variable_id,
|
||||
uint32_t value_id, Instruction* insert_pos);
|
||||
|
||||
// Erases |instr| from data structures of this class.
|
||||
void ClearDebugInfo(Instruction* instr);
|
||||
|
||||
private:
|
||||
IRContext* context() { return context_; }
|
||||
|
||||
@ -147,6 +157,30 @@ class DebugInfoManager {
|
||||
// in |inst| must not already be registered.
|
||||
void RegisterDbgFunction(Instruction* inst);
|
||||
|
||||
// Register the DebugDeclare instruction |dbg_declare| into
|
||||
// |var_id_to_dbg_decl_| using OpVariable id |var_id| as a key.
|
||||
void RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare);
|
||||
|
||||
// Returns a DebugExpression instruction without Operation operands.
|
||||
Instruction* GetEmptyDebugExpression();
|
||||
|
||||
// Returns the id of Value operand if |inst| is DebugValue who has Deref
|
||||
// operation and its Value operand is a result id of OpVariable with
|
||||
// Function storage class. Otherwise, returns 0.
|
||||
uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
|
||||
|
||||
// Returns true if a scope |ancestor| is |scope| or an ancestor scope
|
||||
// of |scope|.
|
||||
bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor);
|
||||
|
||||
// Returns true if the declaration of a local variable |dbg_declare|
|
||||
// is visible in the scope of an instruction |instr_scope_id|.
|
||||
bool IsDeclareVisibleToInstr(Instruction* dbg_declare,
|
||||
uint32_t instr_scope_id);
|
||||
|
||||
// Returns the parent scope of the scope |child_scope|.
|
||||
uint32_t GetParentScope(uint32_t child_scope);
|
||||
|
||||
IRContext* context_;
|
||||
|
||||
// Mapping from ids of OpenCL.DebugInfo.100 extension instructions
|
||||
@ -157,9 +191,18 @@ class DebugInfoManager {
|
||||
// operand is the function.
|
||||
std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
|
||||
|
||||
// Mapping from local variable ids to DebugDeclare instructions whose
|
||||
// operand is the local variable.
|
||||
std::unordered_map<uint32_t, std::vector<Instruction*>> var_id_to_dbg_decl_;
|
||||
|
||||
// DebugInfoNone instruction. We need only a single DebugInfoNone.
|
||||
// To reuse the existing one, we keep it using this member variable.
|
||||
Instruction* debug_info_none_inst_;
|
||||
|
||||
// DebugExpression instruction without Operation operands. We need only
|
||||
// a single DebugExpression without Operation operands. To reuse the
|
||||
// existing one, we keep it using this member variable.
|
||||
Instruction* empty_debug_expr_inst_;
|
||||
};
|
||||
|
||||
} // namespace analysis
|
||||
|
@ -97,8 +97,9 @@ void IRContext::InvalidateAnalysesExceptFor(
|
||||
}
|
||||
|
||||
void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
|
||||
// The ConstantManager contains Type pointers. If the TypeManager goes
|
||||
// away, the ConstantManager has to go away.
|
||||
// The ConstantManager and DebugInfoManager contain Type pointers. If the
|
||||
// TypeManager goes away, the ConstantManager and DebugInfoManager have to
|
||||
// go away.
|
||||
if (analyses_to_invalidate & kAnalysisTypes) {
|
||||
analyses_to_invalidate |= kAnalysisConstants;
|
||||
analyses_to_invalidate |= kAnalysisDebugInfo;
|
||||
@ -179,6 +180,9 @@ Instruction* IRContext::KillInst(Instruction* inst) {
|
||||
decoration_mgr_->RemoveDecoration(inst);
|
||||
}
|
||||
}
|
||||
if (AreAnalysesValid(kAnalysisDebugInfo)) {
|
||||
get_debug_info_mgr()->ClearDebugInfo(inst);
|
||||
}
|
||||
if (type_mgr_ && IsTypeInst(inst->opcode())) {
|
||||
type_mgr_->RemoveId(inst->result_id());
|
||||
}
|
||||
@ -218,6 +222,13 @@ bool IRContext::KillDef(uint32_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRContext::KillDebugDeclareInsts(Function* fn) {
|
||||
fn->ForEachInst([this](Instruction* inst) {
|
||||
if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare)
|
||||
KillInst(inst);
|
||||
});
|
||||
}
|
||||
|
||||
bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
|
||||
return ReplaceAllUsesWithPredicate(
|
||||
before, after, [](Instruction*, uint32_t) { return true; });
|
||||
@ -394,6 +405,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
|
||||
if (operand.words[0] == id) {
|
||||
operand.words[0] =
|
||||
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
|
||||
get_def_use_mgr()->AnalyzeInstUse(&*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -408,6 +420,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
|
||||
if (operand.words[0] == id) {
|
||||
operand.words[0] =
|
||||
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
|
||||
get_def_use_mgr()->AnalyzeInstUse(&*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -403,6 +403,9 @@ class IRContext {
|
||||
// instruction exists.
|
||||
Instruction* KillInst(Instruction* inst);
|
||||
|
||||
// Deletes DebugDeclare instructions in the given function |fn|.
|
||||
void KillDebugDeclareInsts(Function* fn);
|
||||
|
||||
// Returns true if all of the given analyses are valid.
|
||||
bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "OpenCLDebugInfo100.h"
|
||||
#include "source/cfa.h"
|
||||
#include "source/opt/basic_block.h"
|
||||
#include "source/opt/dominator_analysis.h"
|
||||
@ -225,6 +226,11 @@ MemPass::MemPass() {}
|
||||
|
||||
bool MemPass::HasOnlySupportedRefs(uint32_t varId) {
|
||||
return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
|
||||
auto dbg_op = user->GetOpenCL100DebugOpcode();
|
||||
if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
|
||||
dbg_op == OpenCLDebugInfo100DebugValue) {
|
||||
return true;
|
||||
}
|
||||
SpvOp op = user->opcode();
|
||||
if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
|
||||
!IsNonTypeDecorate(op)) {
|
||||
|
@ -71,6 +71,10 @@ class Pass {
|
||||
return context()->get_def_use_mgr();
|
||||
}
|
||||
|
||||
analysis::DebugInfoManager* get_debug_info_mgr() const {
|
||||
return context()->get_debug_info_mgr();
|
||||
}
|
||||
|
||||
analysis::DecorationManager* get_decoration_mgr() const {
|
||||
return context()->get_decoration_mgr();
|
||||
}
|
||||
|
@ -307,6 +307,7 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
|
||||
}
|
||||
if (pass_->IsTargetVar(var_id)) {
|
||||
WriteVariable(var_id, bb, val_id);
|
||||
pass_->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id, inst);
|
||||
|
||||
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
|
||||
std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
|
||||
@ -437,6 +438,8 @@ bool SSARewriter::ApplyReplacements() {
|
||||
|
||||
// Add Phi instructions from completed Phi candidates.
|
||||
std::vector<Instruction*> generated_phis;
|
||||
// Add DebugValue instructions for Phi instructions.
|
||||
std::vector<Instruction*> dbg_values_for_phis;
|
||||
for (const PhiCandidate* phi_candidate : phis_to_generate_) {
|
||||
#if SSA_REWRITE_DEBUGGING_LEVEL > 2
|
||||
std::cerr << "Phi candidate: " << phi_candidate->PrettyPrint(pass_->cfg())
|
||||
@ -447,9 +450,10 @@ bool SSARewriter::ApplyReplacements() {
|
||||
"Tried to instantiate a Phi instruction from an incomplete Phi "
|
||||
"candidate");
|
||||
|
||||
auto* local_var = pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id());
|
||||
|
||||
// Build the vector of operands for the new OpPhi instruction.
|
||||
uint32_t type_id = pass_->GetPointeeTypeId(
|
||||
pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id()));
|
||||
uint32_t type_id = pass_->GetPointeeTypeId(local_var);
|
||||
std::vector<Operand> phi_operands;
|
||||
uint32_t arg_ix = 0;
|
||||
std::unordered_map<uint32_t, uint32_t> already_seen;
|
||||
@ -479,11 +483,17 @@ bool SSARewriter::ApplyReplacements() {
|
||||
pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst);
|
||||
pass_->context()->set_instr_block(&*phi_inst, phi_candidate->bb());
|
||||
auto insert_it = phi_candidate->bb()->begin();
|
||||
insert_it.InsertBefore(std::move(phi_inst));
|
||||
insert_it = insert_it.InsertBefore(std::move(phi_inst));
|
||||
pass_->context()->get_decoration_mgr()->CloneDecorations(
|
||||
phi_candidate->var_id(), phi_candidate->result_id(),
|
||||
{SpvDecorationRelaxedPrecision});
|
||||
|
||||
// Add DebugValue for the new OpPhi instruction.
|
||||
insert_it->SetDebugScope(local_var->GetDebugScope());
|
||||
pass_->get_debug_info_mgr()->AddDebugValue(
|
||||
&*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
|
||||
&*insert_it);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@ -604,6 +614,8 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
|
||||
<< fp->PrettyPrint(0) << "\n";
|
||||
#endif
|
||||
|
||||
if (modified) pass_->context()->KillDebugDeclareInsts(fp);
|
||||
|
||||
return modified ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
@ -39,8 +39,7 @@ namespace opt {
|
||||
// (https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6)
|
||||
class SSARewriter {
|
||||
public:
|
||||
SSARewriter(MemPass* pass)
|
||||
: pass_(pass), first_phi_id_(pass_->get_module()->IdBound()) {}
|
||||
SSARewriter(MemPass* pass) : pass_(pass) {}
|
||||
|
||||
// Rewrites SSA-target variables in function |fp| into SSA. This is the
|
||||
// entry point for the SSA rewrite algorithm. SSA-target variables are
|
||||
@ -287,10 +286,6 @@ class SSARewriter {
|
||||
|
||||
// Memory pass requesting the SSA rewriter.
|
||||
MemPass* pass_;
|
||||
|
||||
// ID of the first Phi created by the SSA rewriter. During rewriting, any
|
||||
// ID bigger than this corresponds to a Phi candidate.
|
||||
uint32_t first_phi_id_;
|
||||
};
|
||||
|
||||
class SSARewritePass : public MemPass {
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user