Correctly replace debug lexical scope of instruction (#3718)

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.
This commit is contained in:
Jaebaek Seo 2020-08-31 10:05:38 -04:00 committed by GitHub
parent f428aa39ca
commit 8a0ebd40f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 206 additions and 52 deletions

View File

@ -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);
}
}
@ -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);
}
}
RegisterDbgInst(dbg_inst);
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);
}
}
if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
assert(GetDebugFunction(dbg_inst->GetSingleWordOperand(
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(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);
}
}
@ -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;
}

View File

@ -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_; }
@ -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_;

View File

@ -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();

View File

@ -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);
@ -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,

View File

@ -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())) {
@ -247,15 +248,20 @@ 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.");
@ -263,7 +269,7 @@ bool IRContext::ReplaceAllUsesWithPredicate(
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);
}
});
@ -301,7 +307,6 @@ bool IRContext::ReplaceAllUsesWithPredicate(
}
AnalyzeUses(user);
}
return true;
}

View File

@ -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.

View File

@ -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)) {
@ -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;

View File

@ -1011,6 +1011,71 @@ OpFunctionEnd)";
dbg_decl1->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
}
TEST_F(IRContextTest, DebugInstructionReplaceDebugScopeAndDebugInlinedAt) {
const std::string text = R"(
OpCapability Shader
OpCapability Linkage
%1 = OpExtInstImport "OpenCL.DebugInfo.100"
OpMemoryModel Logical GLSL450
%2 = OpString "test"
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeFloat 32
%6 = OpTypePointer Function %5
%7 = OpConstant %5 0
%8 = OpTypeInt 32 0
%9 = OpConstant %8 32
%10 = OpExtInst %3 %1 DebugExpression
%11 = OpExtInst %3 %1 DebugSource %2
%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
%15 = OpExtInst %3 %1 DebugInfoNone
%16 = OpExtInst %3 %1 DebugFunction %2 %13 %11 10 10 %12 %2 FlagIsProtected|FlagIsPrivate 0 %15
%25 = OpExtInst %3 %1 DebugInlinedAt 0 %14
%26 = OpExtInst %3 %1 DebugInlinedAt 2 %14
%17 = OpFunction %3 None %4
%18 = OpLabel
%19 = OpExtInst %3 %1 DebugScope %14
%20 = OpVariable %6 Function
OpBranch %21
%21 = OpLabel
%24 = OpExtInst %3 %1 DebugScope %16
%22 = OpPhi %5 %7 %18
OpBranch %23
%23 = OpLabel
%27 = OpExtInst %3 %1 DebugScope %16 %25
OpLine %2 0 0
%28 = OpFAdd %5 %7 %7
OpStore %20 %28
OpReturn
OpFunctionEnd)";
std::unique_ptr<IRContext> ctx =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
NoopPassPreservesAll pass(Pass::Status::SuccessWithChange);
pass.Run(ctx.get());
EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
auto* inst0 = ctx->get_def_use_mgr()->GetDef(20);
auto* inst1 = ctx->get_def_use_mgr()->GetDef(22);
auto* inst2 = ctx->get_def_use_mgr()->GetDef(28);
EXPECT_EQ(inst0->GetDebugScope().GetLexicalScope(), 14);
EXPECT_EQ(inst1->GetDebugScope().GetLexicalScope(), 16);
EXPECT_EQ(inst2->GetDebugScope().GetLexicalScope(), 16);
EXPECT_EQ(inst2->GetDebugInlinedAt(), 25);
EXPECT_TRUE(ctx->ReplaceAllUsesWith(14, 12));
EXPECT_TRUE(ctx->ReplaceAllUsesWith(16, 14));
EXPECT_TRUE(ctx->ReplaceAllUsesWith(25, 26));
EXPECT_EQ(inst0->GetDebugScope().GetLexicalScope(), 12);
EXPECT_EQ(inst1->GetDebugScope().GetLexicalScope(), 14);
EXPECT_EQ(inst2->GetDebugScope().GetLexicalScope(), 14);
EXPECT_EQ(inst2->GetDebugInlinedAt(), 26);
}
TEST_F(IRContextTest, AddDebugValueAfterReplaceUse) {
const std::string text = R"(
OpCapability Shader