Skip to content

Commit

Permalink
Correctly replace debug lexical scope of instruction (KhronosGroup#3718)
Browse files Browse the repository at this point in the history
When we update OpenCL.DebugInfo.100 lexical scopes e.g., DebugFunction,
we have to replace DebugScope of each instruction that uses the lexical
scope correctly.
  • Loading branch information
jaebaek authored Aug 31, 2020
1 parent f428aa3 commit 8a0ebd4
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 52 deletions.
90 changes: 72 additions & 18 deletions source/opt/debug_info_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ void DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
value_id, 0, index_id, insert_before);
assert(added_dbg_value != nullptr);
added_dbg_value->UpdateDebugInfoFrom(scope_and_line);
AnalyzeDebugInst(added_dbg_value);
}
}

Expand Down Expand Up @@ -567,42 +568,84 @@ bool DebugInfoManager::IsDebugDeclare(Instruction* instr) {
GetVariableIdOfDebugValueUsedForDeclare(instr) != 0;
}

void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
if (!dbg_inst->IsOpenCL100DebugInstr()) return;
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);
}

RegisterDbgInst(dbg_inst);
if (!inst->IsOpenCL100DebugInstr()) return;

if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
assert(GetDebugFunction(dbg_inst->GetSingleWordOperand(
RegisterDbgInst(inst);

if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
assert(GetDebugFunction(inst->GetSingleWordOperand(
kDebugFunctionOperandFunctionIndex)) == nullptr &&
"Two DebugFunction instruction exists for a single OpFunction.");
RegisterDbgFunction(dbg_inst);
RegisterDbgFunction(inst);
}

if (deref_operation_ == nullptr &&
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
dbg_inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) ==
inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) ==
OpenCLDebugInfo100Deref) {
deref_operation_ = dbg_inst;
deref_operation_ = inst;
}

if (debug_info_none_inst_ == nullptr &&
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
debug_info_none_inst_ = dbg_inst;
inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
debug_info_none_inst_ = inst;
}

if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(dbg_inst)) {
empty_debug_expr_inst_ = dbg_inst;
if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(inst)) {
empty_debug_expr_inst_ = inst;
}

if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
uint32_t var_id =
dbg_inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
RegisterDbgDeclare(var_id, dbg_inst);
inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
RegisterDbgDeclare(var_id, inst);
}

if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(dbg_inst)) {
RegisterDbgDeclare(var_id, dbg_inst);
if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(inst)) {
RegisterDbgDeclare(var_id, inst);
}
}

Expand Down Expand Up @@ -682,6 +725,17 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
}

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;
}
Expand Down
18 changes: 18 additions & 0 deletions source/opt/debug_info_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ class DebugInfoManager {
// Returns true if |instr| is a debug declaration instruction.
bool IsDebugDeclare(Instruction* instr);

// Replace all uses of |before| id that is an operand of a DebugScope with
// |after| id if those uses (instruction) return true for |predicate|.
void ReplaceAllUsesInDebugScopeWithPredicate(
uint32_t before, uint32_t after,
const std::function<bool(Instruction*)>& predicate);

// Removes uses of DebugScope |inst| from |scope_id_to_users_| or uses of
// DebugInlinedAt |inst| from |inlinedat_id_to_users_|.
void ClearDebugScopeAndInlinedAtUses(Instruction* inst);

private:
IRContext* context() { return context_; }

Expand Down Expand Up @@ -228,6 +238,14 @@ class DebugInfoManager {
std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
var_id_to_dbg_decl_;

// Mapping from DebugScope ids to users.
std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
scope_id_to_users_;

// Mapping from DebugInlinedAt ids to users.
std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
inlinedat_id_to_users_;

// DebugOperation whose OpCode is OpenCLDebugInfo100Deref.
Instruction* deref_operation_;

Expand Down
34 changes: 34 additions & 0 deletions source/opt/instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,40 @@ uint32_t Instruction::GetTypeComponent(uint32_t element) const {
return subtype;
}

void Instruction::UpdateLexicalScope(uint32_t scope) {
dbg_scope_.SetLexicalScope(scope);
for (auto& i : dbg_line_insts_) {
i.dbg_scope_.SetLexicalScope(scope);
}
if (!IsDebugLineInst(opcode()) &&
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
}
}

void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
dbg_scope_.SetInlinedAt(new_inlined_at);
for (auto& i : dbg_line_insts_) {
i.dbg_scope_.SetInlinedAt(new_inlined_at);
}
if (!IsDebugLineInst(opcode()) &&
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
}
}

void Instruction::UpdateDebugInfoFrom(const Instruction* from) {
if (from == nullptr) return;
clear_dbg_line_insts();
if (!from->dbg_line_insts().empty())
dbg_line_insts().push_back(from->dbg_line_insts()[0]);
SetDebugScope(from->GetDebugScope());
if (!IsDebugLineInst(opcode()) &&
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
}
}

Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& inst) {
inst.get()->InsertBefore(this);
return inst.release();
Expand Down
28 changes: 3 additions & 25 deletions source/opt/instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,14 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
inline void SetDebugScope(const DebugScope& scope);
inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
// Updates DebugInlinedAt of DebugScope and OpLine.
inline void UpdateDebugInlinedAt(uint32_t new_inlined_at);
void UpdateDebugInlinedAt(uint32_t new_inlined_at);
inline uint32_t GetDebugInlinedAt() const {
return dbg_scope_.GetInlinedAt();
}
// Updates lexical scope of DebugScope and OpLine.
inline void UpdateLexicalScope(uint32_t scope);
void UpdateLexicalScope(uint32_t scope);
// Updates OpLine and DebugScope based on the information of |from|.
inline void UpdateDebugInfoFrom(const Instruction* from);
void UpdateDebugInfoFrom(const Instruction* from);
// Remove the |index|-th operand
void RemoveOperand(uint32_t index) {
operands_.erase(operands_.begin() + index);
Expand Down Expand Up @@ -670,28 +670,6 @@ inline void Instruction::SetDebugScope(const DebugScope& scope) {
}
}

inline void Instruction::UpdateLexicalScope(uint32_t scope) {
dbg_scope_.SetLexicalScope(scope);
for (auto& i : dbg_line_insts_) {
i.dbg_scope_.SetLexicalScope(scope);
}
}

inline void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
dbg_scope_.SetInlinedAt(new_inlined_at);
for (auto& i : dbg_line_insts_) {
i.dbg_scope_.SetInlinedAt(new_inlined_at);
}
}

inline void Instruction::UpdateDebugInfoFrom(const Instruction* from) {
if (from == nullptr) return;
clear_dbg_line_insts();
if (!from->dbg_line_insts().empty())
dbg_line_insts().push_back(from->dbg_line_insts()[0]);
SetDebugScope(from->GetDebugScope());
}

inline void Instruction::SetResultType(uint32_t ty_id) {
// TODO(dsinclair): Allow setting a type id if there wasn't one
// previously. Need to make room in the operands_ array to place the result,
Expand Down
15 changes: 10 additions & 5 deletions source/opt/ir_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ Instruction* IRContext::KillInst(Instruction* inst) {
}
}
if (AreAnalysesValid(kAnalysisDebugInfo)) {
get_debug_info_mgr()->ClearDebugScopeAndInlinedAtUses(inst);
get_debug_info_mgr()->ClearDebugInfo(inst);
}
if (type_mgr_ && IsTypeInst(inst->opcode())) {
Expand Down Expand Up @@ -247,23 +248,28 @@ bool IRContext::KillDef(uint32_t id) {
}

bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
return ReplaceAllUsesWithPredicate(
before, after, [](Instruction*, uint32_t) { return true; });
return ReplaceAllUsesWithPredicate(before, after,
[](Instruction*) { return true; });
}

bool IRContext::ReplaceAllUsesWithPredicate(
uint32_t before, uint32_t after,
const std::function<bool(Instruction*, uint32_t)>& predicate) {
const std::function<bool(Instruction*)>& predicate) {
if (before == after) return false;

if (AreAnalysesValid(kAnalysisDebugInfo)) {
get_debug_info_mgr()->ReplaceAllUsesInDebugScopeWithPredicate(before, after,
predicate);
}

// Ensure that |after| has been registered as def.
assert(get_def_use_mgr()->GetDef(after) &&
"'after' is not a registered def.");

std::vector<std::pair<Instruction*, uint32_t>> uses_to_update;
get_def_use_mgr()->ForEachUse(
before, [&predicate, &uses_to_update](Instruction* user, uint32_t index) {
if (predicate(user, index)) {
if (predicate(user)) {
uses_to_update.emplace_back(user, index);
}
});
Expand Down Expand Up @@ -301,7 +307,6 @@ bool IRContext::ReplaceAllUsesWithPredicate(
}
AnalyzeUses(user);
}

return true;
}

Expand Down
4 changes: 2 additions & 2 deletions source/opt/ir_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,13 +418,13 @@ class IRContext {
bool ReplaceAllUsesWith(uint32_t before, uint32_t after);

// Replace all uses of |before| id with |after| id if those uses
// (instruction, operand pair) return true for |predicate|. Returns true if
// (instruction) return true for |predicate|. Returns true if
// any replacement happens. This method does not kill the definition of the
// |before| id. If |after| is the same as |before|, does nothing and return
// false.
bool ReplaceAllUsesWithPredicate(
uint32_t before, uint32_t after,
const std::function<bool(Instruction*, uint32_t)>& predicate);
const std::function<bool(Instruction*)>& predicate);

// Returns true if all of the analyses that are suppose to be valid are
// actually valid.
Expand Down
4 changes: 2 additions & 2 deletions source/opt/simplification_pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ bool SimplificationPass::SimplifyFunction(Function* function) {
if (inst->opcode() == SpvOpCopyObject) {
context()->ReplaceAllUsesWithPredicate(
inst->result_id(), inst->GetSingleWordInOperand(0),
[](Instruction* user, uint32_t) {
[](Instruction* user) {
const auto opcode = user->opcode();
if (!spvOpcodeIsDebug(opcode) &&
!spvOpcodeIsDecoration(opcode)) {
Expand Down Expand Up @@ -137,7 +137,7 @@ bool SimplificationPass::SimplifyFunction(Function* function) {
if (inst->opcode() == SpvOpCopyObject) {
context()->ReplaceAllUsesWithPredicate(
inst->result_id(), inst->GetSingleWordInOperand(0),
[](Instruction* user, uint32_t) {
[](Instruction* user) {
const auto opcode = user->opcode();
if (!spvOpcodeIsDebug(opcode) && !spvOpcodeIsDecoration(opcode)) {
return true;
Expand Down
Loading

0 comments on commit 8a0ebd4

Please sign in to comment.