Skip to content

Commit b3da1b9

Browse files
Merge pull request #9463 from rastogishubham/DroppedVarStatsStable
Add a pass to collect dropped variable statistics (llvm#102233)
2 parents a3a4448 + 9f1821e commit b3da1b9

File tree

5 files changed

+738
-3
lines changed

5 files changed

+738
-3
lines changed

llvm/include/llvm/Passes/StandardInstrumentations.h

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/ADT/StringSet.h"
2222
#include "llvm/CodeGen/MachineBasicBlock.h"
2323
#include "llvm/IR/BasicBlock.h"
24+
#include "llvm/IR/DebugInfoMetadata.h"
2425
#include "llvm/IR/OptBisect.h"
2526
#include "llvm/IR/PassTimingInfo.h"
2627
#include "llvm/IR/ValueHandle.h"
@@ -578,6 +579,83 @@ class PrintCrashIRInstrumentation {
578579
static void SignalHandler(void *);
579580
};
580581

582+
/// A class to collect and print dropped debug information variable statistics.
583+
/// After every LLVM IR pass is run, it will print how many #dbg_values were
584+
/// dropped due to that pass.
585+
class DroppedVariableStats {
586+
public:
587+
DroppedVariableStats(bool DroppedVarStatsEnabled) {
588+
if (DroppedVarStatsEnabled)
589+
llvm::outs()
590+
<< "Pass Level, Pass Name, Num of Dropped Variables, Func or "
591+
"Module Name\n";
592+
};
593+
// We intend this to be unique per-compilation, thus no copies.
594+
DroppedVariableStats(const DroppedVariableStats &) = delete;
595+
void operator=(const DroppedVariableStats &) = delete;
596+
597+
void registerCallbacks(PassInstrumentationCallbacks &PIC);
598+
void runBeforePass(StringRef PassID, Any IR);
599+
void runAfterPass(StringRef PassID, Any IR, const PreservedAnalyses &PA);
600+
void runAfterPassInvalidated(StringRef PassID, const PreservedAnalyses &PA);
601+
bool getPassDroppedVariables() { return PassDroppedVariables; }
602+
603+
private:
604+
bool PassDroppedVariables = false;
605+
/// A unique key that represents a #dbg_value.
606+
using VarID =
607+
std::tuple<const DIScope *, const DIScope *, const DILocalVariable *>;
608+
609+
struct DebugVariables {
610+
/// DenseSet of VarIDs before an optimization pass has run.
611+
DenseSet<VarID> DebugVariablesBefore;
612+
/// DenseSet of VarIDs after an optimization pass has run.
613+
DenseSet<VarID> DebugVariablesAfter;
614+
};
615+
616+
/// A stack of a DenseMap, that maps DebugVariables for every pass to an
617+
/// llvm::Function. A stack is used because an optimization pass can call
618+
/// other passes.
619+
SmallVector<DenseMap<const Function *, DebugVariables>> DebugVariablesStack;
620+
621+
/// A DenseSet tracking whether a scope was visited before.
622+
DenseSet<const DIScope *> VisitedScope;
623+
/// A stack of DenseMaps, which map the name of an llvm::Function to a
624+
/// DenseMap of VarIDs and their inlinedAt locations before an optimization
625+
/// pass has run.
626+
SmallVector<DenseMap<StringRef, DenseMap<VarID, DILocation *>>> InlinedAts;
627+
628+
/// Iterate over all Functions in a Module and report any dropped debug
629+
/// information. Will call calculateDroppedVarStatsOnFunction on every
630+
/// Function.
631+
void calculateDroppedVarStatsOnModule(const Module *M, StringRef PassID,
632+
std::string FuncOrModName,
633+
std::string PassLevel);
634+
/// Iterate over all Instructions in a Function and report any dropped debug
635+
/// information.
636+
void calculateDroppedVarStatsOnFunction(const Function *F, StringRef PassID,
637+
std::string FuncOrModName,
638+
std::string PassLevel);
639+
/// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or
640+
/// after a pass has run to facilitate dropped variable calculation for an
641+
/// llvm::Function.
642+
void runOnFunction(const Function *F, bool Before);
643+
/// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or
644+
/// after a pass has run to facilitate dropped variable calculation for an
645+
/// llvm::Module. Calls runOnFunction on every Function in the Module.
646+
void runOnModule(const Module *M, bool Before);
647+
/// Remove a dropped #dbg_value VarID from all Sets in the
648+
/// DroppedVariablesBefore stack.
649+
void removeVarFromAllSets(VarID Var, const Function *F);
650+
/// Return true if \p Scope is the same as \p DbgValScope or a child scope of
651+
/// \p DbgValScope, return false otherwise.
652+
bool isScopeChildOfOrEqualTo(DIScope *Scope, const DIScope *DbgValScope);
653+
/// Return true if \p InlinedAt is the same as \p DbgValInlinedAt or part of
654+
/// the InlinedAt chain, return false otherwise.
655+
bool isInlinedAtChildOfOrEqualTo(const DILocation *InlinedAt,
656+
const DILocation *DbgValInlinedAt);
657+
};
658+
581659
/// This class provides an interface to register all the standard pass
582660
/// instrumentations and manages their state (if any).
583661
class StandardInstrumentations {
@@ -595,6 +673,7 @@ class StandardInstrumentations {
595673
PrintCrashIRInstrumentation PrintCrashIR;
596674
IRChangedTester ChangeTester;
597675
VerifyInstrumentation Verify;
676+
DroppedVariableStats DroppedStats;
598677

599678
bool VerifyEach;
600679

llvm/lib/Passes/StandardInstrumentations.cpp

Lines changed: 187 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include "llvm/CodeGen/MachineVerifier.h"
2525
#include "llvm/IR/Constants.h"
2626
#include "llvm/IR/Function.h"
27+
#include "llvm/IR/InstIterator.h"
28+
#include "llvm/IR/IntrinsicInst.h"
2729
#include "llvm/IR/Module.h"
2830
#include "llvm/IR/PassInstrumentation.h"
2931
#include "llvm/IR/PassManager.h"
@@ -138,6 +140,11 @@ static cl::opt<std::string> IRDumpDirectory(
138140
"files in this directory rather than written to stderr"),
139141
cl::Hidden, cl::value_desc("filename"));
140142

143+
static cl::opt<bool>
144+
DroppedVarStats("dropped-variable-stats", cl::Hidden,
145+
cl::desc("Dump dropped debug variables stats"),
146+
cl::init(false));
147+
141148
template <typename IRUnitT> static const IRUnitT *unwrapIR(Any IR) {
142149
const IRUnitT **IRPtr = llvm::any_cast<const IRUnitT *>(&IR);
143150
return IRPtr ? *IRPtr : nullptr;
@@ -2443,16 +2450,16 @@ void DotCfgChangeReporter::registerCallbacks(
24432450
StandardInstrumentations::StandardInstrumentations(
24442451
LLVMContext &Context, bool DebugLogging, bool VerifyEach,
24452452
PrintPassOptions PrintPassOpts)
2446-
: PrintPass(DebugLogging, PrintPassOpts),
2447-
OptNone(DebugLogging),
2453+
: PrintPass(DebugLogging, PrintPassOpts), OptNone(DebugLogging),
24482454
OptPassGate(Context),
24492455
PrintChangedIR(PrintChanged == ChangePrinter::Verbose),
24502456
PrintChangedDiff(PrintChanged == ChangePrinter::DiffVerbose ||
24512457
PrintChanged == ChangePrinter::ColourDiffVerbose,
24522458
PrintChanged == ChangePrinter::ColourDiffVerbose ||
24532459
PrintChanged == ChangePrinter::ColourDiffQuiet),
24542460
WebsiteChangeReporter(PrintChanged == ChangePrinter::DotCfgVerbose),
2455-
Verify(DebugLogging), VerifyEach(VerifyEach) {}
2461+
Verify(DebugLogging), DroppedStats(DroppedVarStats),
2462+
VerifyEach(VerifyEach) {}
24562463

24572464
PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter =
24582465
nullptr;
@@ -2512,6 +2519,182 @@ void PrintCrashIRInstrumentation::registerCallbacks(
25122519
});
25132520
}
25142521

2522+
void DroppedVariableStats::registerCallbacks(
2523+
PassInstrumentationCallbacks &PIC) {
2524+
if (!DroppedVarStats)
2525+
return;
2526+
2527+
PIC.registerBeforeNonSkippedPassCallback(
2528+
[this](StringRef P, Any IR) { return this->runBeforePass(P, IR); });
2529+
PIC.registerAfterPassCallback(
2530+
[this](StringRef P, Any IR, const PreservedAnalyses &PA) {
2531+
return this->runAfterPass(P, IR, PA);
2532+
});
2533+
PIC.registerAfterPassInvalidatedCallback(
2534+
[this](StringRef P, const PreservedAnalyses &PA) {
2535+
return this->runAfterPassInvalidated(P, PA);
2536+
});
2537+
}
2538+
2539+
void DroppedVariableStats::runBeforePass(StringRef PassID, Any IR) {
2540+
DebugVariablesStack.push_back({DenseMap<const Function *, DebugVariables>()});
2541+
InlinedAts.push_back({DenseMap<StringRef, DenseMap<VarID, DILocation *>>()});
2542+
if (auto *M = unwrapIR<Module>(IR))
2543+
return this->runOnModule(M, true);
2544+
if (auto *F = unwrapIR<Function>(IR))
2545+
return this->runOnFunction(F, true);
2546+
return;
2547+
}
2548+
2549+
void DroppedVariableStats::runOnFunction(const Function *F, bool Before) {
2550+
auto &DebugVariables = DebugVariablesStack.back()[F];
2551+
auto &VarIDSet = (Before ? DebugVariables.DebugVariablesBefore
2552+
: DebugVariables.DebugVariablesAfter);
2553+
auto &InlinedAtsMap = InlinedAts.back();
2554+
auto FuncName = F->getName();
2555+
if (Before)
2556+
InlinedAtsMap.try_emplace(FuncName, DenseMap<VarID, DILocation *>());
2557+
VarIDSet = DenseSet<VarID>();
2558+
for (const auto &I : instructions(F)) {
2559+
for (DbgRecord &DR : I.getDbgRecordRange()) {
2560+
if (auto *Dbg = dyn_cast<DbgVariableRecord>(&DR)) {
2561+
auto *DbgVar = Dbg->getVariable();
2562+
auto DbgLoc = DR.getDebugLoc();
2563+
VarID Key{DbgVar->getScope(), DbgLoc->getInlinedAtScope(), DbgVar};
2564+
VarIDSet.insert(Key);
2565+
if (Before)
2566+
InlinedAtsMap[FuncName].try_emplace(Key, DbgLoc.getInlinedAt());
2567+
}
2568+
}
2569+
}
2570+
}
2571+
2572+
void DroppedVariableStats::runOnModule(const Module *M, bool Before) {
2573+
for (auto &F : *M)
2574+
runOnFunction(&F, Before);
2575+
}
2576+
2577+
void DroppedVariableStats::removeVarFromAllSets(VarID Var, const Function *F) {
2578+
// Do not remove Var from the last element, it will be popped from the stack.
2579+
for (auto &DebugVariablesMap : llvm::drop_end(DebugVariablesStack))
2580+
DebugVariablesMap[F].DebugVariablesBefore.erase(Var);
2581+
}
2582+
2583+
void DroppedVariableStats::calculateDroppedVarStatsOnModule(
2584+
const Module *M, StringRef PassID, std::string FuncOrModName,
2585+
std::string PassLevel) {
2586+
for (auto &F : *M) {
2587+
calculateDroppedVarStatsOnFunction(&F, PassID, FuncOrModName, PassLevel);
2588+
}
2589+
}
2590+
2591+
void DroppedVariableStats::calculateDroppedVarStatsOnFunction(
2592+
const Function *F, StringRef PassID, std::string FuncOrModName,
2593+
std::string PassLevel) {
2594+
unsigned DroppedCount = 0;
2595+
StringRef FuncName = F->getName();
2596+
DebugVariables &DbgVariables = DebugVariablesStack.back()[F];
2597+
DenseSet<VarID> &DebugVariablesBeforeSet = DbgVariables.DebugVariablesBefore;
2598+
DenseSet<VarID> &DebugVariablesAfterSet = DbgVariables.DebugVariablesAfter;
2599+
DenseMap<VarID, DILocation *> &InlinedAtsMap = InlinedAts.back()[FuncName];
2600+
// Find an Instruction that shares the same scope as the dropped #dbg_value or
2601+
// has a scope that is the child of the scope of the #dbg_value, and has an
2602+
// inlinedAt equal to the inlinedAt of the #dbg_value or it's inlinedAt chain
2603+
// contains the inlinedAt of the #dbg_value, if such an Instruction is found,
2604+
// debug information is dropped.
2605+
for (VarID Var : DebugVariablesBeforeSet) {
2606+
if (DebugVariablesAfterSet.contains(Var))
2607+
continue;
2608+
const DIScope *DbgValScope = std::get<0>(Var);
2609+
for (const auto &I : instructions(F)) {
2610+
auto *DbgLoc = I.getDebugLoc().get();
2611+
if (!DbgLoc)
2612+
continue;
2613+
2614+
auto *Scope = DbgLoc->getScope();
2615+
if (isScopeChildOfOrEqualTo(Scope, DbgValScope)) {
2616+
if (isInlinedAtChildOfOrEqualTo(DbgLoc->getInlinedAt(),
2617+
InlinedAtsMap[Var])) {
2618+
// Found another instruction in the variable's scope, so there exists
2619+
// a break point at which the variable could be observed. Count it as
2620+
// dropped.
2621+
DroppedCount++;
2622+
break;
2623+
}
2624+
}
2625+
}
2626+
removeVarFromAllSets(Var, F);
2627+
}
2628+
if (DroppedCount > 0) {
2629+
llvm::outs() << PassLevel << ", " << PassID << ", " << DroppedCount << ", "
2630+
<< FuncOrModName << "\n";
2631+
PassDroppedVariables = true;
2632+
} else
2633+
PassDroppedVariables = false;
2634+
}
2635+
2636+
void DroppedVariableStats::runAfterPassInvalidated(
2637+
StringRef PassID, const PreservedAnalyses &PA) {
2638+
DebugVariablesStack.pop_back();
2639+
InlinedAts.pop_back();
2640+
}
2641+
2642+
void DroppedVariableStats::runAfterPass(StringRef PassID, Any IR,
2643+
const PreservedAnalyses &PA) {
2644+
std::string PassLevel;
2645+
std::string FuncOrModName;
2646+
if (auto *M = unwrapIR<Module>(IR)) {
2647+
this->runOnModule(M, false);
2648+
PassLevel = "Module";
2649+
FuncOrModName = M->getName();
2650+
calculateDroppedVarStatsOnModule(M, PassID, FuncOrModName, PassLevel);
2651+
} else if (auto *F = unwrapIR<Function>(IR)) {
2652+
this->runOnFunction(F, false);
2653+
PassLevel = "Function";
2654+
FuncOrModName = F->getName();
2655+
calculateDroppedVarStatsOnFunction(F, PassID, FuncOrModName, PassLevel);
2656+
}
2657+
2658+
DebugVariablesStack.pop_back();
2659+
InlinedAts.pop_back();
2660+
return;
2661+
}
2662+
2663+
bool DroppedVariableStats::isScopeChildOfOrEqualTo(DIScope *Scope,
2664+
const DIScope *DbgValScope) {
2665+
while (Scope != nullptr) {
2666+
if (VisitedScope.find(Scope) == VisitedScope.end()) {
2667+
VisitedScope.insert(Scope);
2668+
if (Scope == DbgValScope) {
2669+
VisitedScope.clear();
2670+
return true;
2671+
}
2672+
Scope = Scope->getScope();
2673+
} else {
2674+
VisitedScope.clear();
2675+
return false;
2676+
}
2677+
}
2678+
return false;
2679+
}
2680+
2681+
bool DroppedVariableStats::isInlinedAtChildOfOrEqualTo(
2682+
const DILocation *InlinedAt, const DILocation *DbgValInlinedAt) {
2683+
if (DbgValInlinedAt == InlinedAt)
2684+
return true;
2685+
if (!DbgValInlinedAt)
2686+
return false;
2687+
if (!InlinedAt)
2688+
return false;
2689+
auto *IA = InlinedAt;
2690+
while (IA) {
2691+
if (IA == DbgValInlinedAt)
2692+
return true;
2693+
IA = IA->getInlinedAt();
2694+
}
2695+
return false;
2696+
}
2697+
25152698
void StandardInstrumentations::registerCallbacks(
25162699
PassInstrumentationCallbacks &PIC, ModuleAnalysisManager *MAM) {
25172700
PrintIR.registerCallbacks(PIC);
@@ -2527,6 +2710,7 @@ void StandardInstrumentations::registerCallbacks(
25272710
WebsiteChangeReporter.registerCallbacks(PIC);
25282711
ChangeTester.registerCallbacks(PIC);
25292712
PrintCrashIR.registerCallbacks(PIC);
2713+
DroppedStats.registerCallbacks(PIC);
25302714
if (MAM)
25312715
PreservedCFGChecker.registerCallbacks(PIC, *MAM);
25322716

llvm/test/Other/dropped-var-stats.ll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
; RUN: opt -dropped-variable-stats %s -passes='verify' -S | FileCheck %s --check-prefix=NOT-DROPPED
2+
; NOT-DROPPED: Pass Level, Pass Name, Num of Dropped Variables, Func or Module Name
3+
; NOT-DROPPED-NOT: Function, ADCEPass, 1, _Z3bari
4+
5+
; ModuleID = '/tmp/dropped.cpp'
6+
define noundef range(i32 -2147483646, -2147483648) i32 @_Z3bari(i32 noundef %y) local_unnamed_addr #1 !dbg !19 {
7+
#dbg_value(i32 %y, !15, !DIExpression(), !23)
8+
%add = add nsw i32 %y, 2,!dbg !25
9+
ret i32 %add,!dbg !26
10+
}
11+
!llvm.module.flags = !{ !3, !7}
12+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 20.0.0git ([email protected]:llvm/llvm-project.git 7fc8398aaad65c4c29f1511c374d07308e667af5)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
13+
!1 = !DIFile(filename: "/tmp/dropped.cpp", directory: "/Users/shubham/Development/llvm-project")
14+
!3 = !{i32 2, !"Debug Info Version", i32 3}
15+
!7 = !{i32 7, !"frame-pointer", i32 1}
16+
!9 = distinct !DISubprogram( unit: !0, retainedNodes: !14)
17+
!13 = !DIBasicType()
18+
!14 = !{}
19+
!15 = !DILocalVariable( scope: !9, type: !13)
20+
!19 = distinct !DISubprogram( unit: !0, retainedNodes: !20)
21+
!20 = !{}
22+
!23 = !DILocation( scope: !9, inlinedAt: !24)
23+
!24 = distinct !DILocation( scope: !19)
24+
!25 = !DILocation( scope: !19)
25+
!26 = !DILocation( scope: !19)

llvm/unittests/IR/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ add_llvm_unittest(IRTests
4242
ShuffleVectorInstTest.cpp
4343
StructuralHashTest.cpp
4444
TimePassesTest.cpp
45+
DroppedVariableStatsTest.cpp
4546
TypesTest.cpp
4647
UseTest.cpp
4748
UserTest.cpp

0 commit comments

Comments
 (0)