[llvm] [llvm-dwarfdump][LineCov 3/3] Add IR analysis for variable coverage (PR #195342)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 1 04:31:42 PDT 2026
================
@@ -195,6 +215,124 @@ static const SmallVector<DWARFDie> getParentSubroutines(DWARFDie DIE) {
return Parents;
}
+static bool isInScope(MDNode *Scope, const DebugLoc &Loc) {
+ MDNode *Parent = Loc.getScope();
+ while (Parent != Scope) {
+ auto *S = dyn_cast_if_present<DIScope>(Parent);
+ if (!S)
+ return false;
+ Parent = S->getScope();
+ }
+ return true;
+}
+
+static bool isLoop(BasicBlock *Origin, BasicBlock *BB,
+ SmallPtrSet<BasicBlock *, 8> &Visited) {
+ for (auto *P : predecessors(BB)) {
+ if (P == Origin)
+ return true;
+ if (!Visited.count(P)) {
+ Visited.insert(P);
+ if (isLoop(Origin, P, Visited))
+ return true;
+ }
+ }
+ return false;
+}
+
+struct VarState {
+ DbgVariableRecord &DVR;
+ DenseSet<std::pair<StringRef, uint32_t>> Lines;
+ SmallPtrSet<BasicBlock *, 8> LiveOut;
+};
+
+/// Given an instruction that stores to a variable and its basic block,
+/// recursively searches its successor instructions/basic blocks and adds lines
+/// where the variable has a defined value to the variable's line set.
+static void getSuccessorLines(VarState &Var, BasicBlock *BB, Instruction *I) {
+ // Process the basic block if it contains the store instruction or the
+ // variable is defined in all of its predecessors, excluding any that are part
+ // of a loop that contains the current block.
+ bool ShouldProcess = I;
+ if (!ShouldProcess) {
+ ShouldProcess = true;
+ for (auto *P : predecessors(BB)) {
+ SmallPtrSet<BasicBlock *, 8> Visited;
+ if (!Var.LiveOut.count(P) && !isLoop(BB, P, Visited)) {
+ ShouldProcess = false;
+ break;
+ }
+ }
+ }
+ if (!ShouldProcess || Var.LiveOut.count(BB))
+ return;
+ Var.LiveOut.insert(BB);
+
+ // Add lines that are within the variable's scope, starting from the store
+ // instruction or the start of the block if this is a successor block.
+ auto *Next = I ? I : &BB->front();
+ auto *VarScope = Var.DVR.getVariable()->getScope();
+ do {
+ auto &Loc = Next->getDebugLoc();
+ DIScope *Scope;
+ if (Loc && isInScope(VarScope, Loc) && Loc.getLine() &&
+ (Scope = dyn_cast_if_present<DIScope>(Loc.getScope()))) {
+ Var.Lines.insert({Scope->getFilename(), Loc.getLine()});
+ }
+ } while ((Next = Next->getNextNode()));
+ for (auto *S : successors(BB))
+ getSuccessorLines(Var, S, nullptr);
+}
+
+/// Computes the defined lines of all variables in an IR module.
+static LineMap processModule(Module *Mod) {
+ LineMap Result;
+
+ for (auto &F : Mod->functions()) {
+ std::vector<VarState> Vars;
+ for (auto &BB : F) {
+ for (auto &I : BB) {
+ for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
+ if (DVR.isDbgDeclare()) {
+ // For #dbg_declare, don't treat the variable as live until we find
+ // a store to it.
+ Vars.push_back({DVR, {}, {}});
+ } else if (DVR.isDbgValue()) {
+ // For #dbg_value, the variable is live immediately from this point.
+ auto Var = find_if(Vars, [&](auto &Var) {
+ return Var.DVR.getVariable() == DVR.getVariable();
+ });
+ if (Var != Vars.end()) {
+ getSuccessorLines(*Var, &BB, &I);
+ } else {
+ Vars.push_back({DVR, {}, {}});
+ getSuccessorLines(Vars.back(), &BB, &I);
+ }
+ }
+ }
+ }
+ }
+
+ // Search for stores to any declared variables. For the purposes of this
+ // analysis, we consider any instruction that isn't a load and has the
+ // variable as an operand to potentially store to it.
----------------
unexpectedlydefined wrote:
I've added a new `isStoreToLocation` function that mostly replicates the check in `at::trackAssignments`, but also treats call-like instructions as stores if the location is passed as an argument to the call.
https://github.com/llvm/llvm-project/pull/195342
More information about the llvm-commits
mailing list