mirror of
https://gitee.com/openharmony/third_party_spirv-tools
synced 2024-11-23 15:30:36 +00:00
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:
parent
f428aa39ca
commit
8a0ebd40f8
@ -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;
|
||||
}
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user