Skip to content

Commit fc20332

Browse files
Add a pass to collect dropped variable statistics
Add an instrumentation pass to llvm to collect dropped debug information variable statistics for every Function-level and Module-level IR pass.
1 parent 1fb1a5d commit fc20332

File tree

3 files changed

+201
-3
lines changed

3 files changed

+201
-3
lines changed

llvm/include/llvm/Passes/StandardInstrumentations.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,44 @@ class DotCfgChangeReporter : public ChangeReporter<IRDataT<DCData>> {
559559
std::unique_ptr<raw_fd_ostream> HTML;
560560
};
561561

562+
class DroppedVariableStats {
563+
public:
564+
DroppedVariableStats(bool DroppedVarStatsEnabled) {
565+
if (DroppedVarStatsEnabled)
566+
llvm::outs()
567+
<< "Pass Level, Pass Name, Num of Dropped Variables, Func or "
568+
"Module Name\n";
569+
};
570+
// We intend this to be unique per-compilation, thus no copies.
571+
DroppedVariableStats(const DroppedVariableStats &) = delete;
572+
void operator=(const DroppedVariableStats &) = delete;
573+
574+
void registerCallbacks(PassInstrumentationCallbacks &PIC);
575+
576+
private:
577+
using VarID = std::tuple<const DILocalScope *, const DILocalScope *,
578+
const DILocalVariable *>;
579+
580+
SmallVector<llvm::DenseSet<VarID>> DebugVariablesBefore;
581+
SmallVector<llvm::DenseSet<VarID>> DebugVariablesAfter;
582+
583+
DenseMap<const DISubprogram *, const llvm::Function *>
584+
SubprogramToFunctionMap;
585+
586+
// Implementation of pass instrumentation callbacks.
587+
void runBeforePass(StringRef PassID, Any IR);
588+
void runAfterPass(StringRef PassID, Any IR, const PreservedAnalyses &PA);
589+
590+
void runOnFunction(const Function *F, bool Before);
591+
void runOnModule(const Module *M, bool Before);
592+
593+
void removeVarFromAllSets(VarID Var);
594+
595+
// Populates a DenseMap<const DISubprogram*, const Function*>
596+
// SubprogramToFunctionMap every time it is called
597+
void makeDISubprogramToFunctionMap(const llvm::Module *M);
598+
};
599+
562600
// Print IR on crash.
563601
class PrintCrashIRInstrumentation {
564602
public:
@@ -595,6 +633,7 @@ class StandardInstrumentations {
595633
PrintCrashIRInstrumentation PrintCrashIR;
596634
IRChangedTester ChangeTester;
597635
VerifyInstrumentation Verify;
636+
DroppedVariableStats DroppedStats;
598637

599638
bool VerifyEach;
600639

llvm/lib/Passes/StandardInstrumentations.cpp

Lines changed: 132 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "llvm/Demangle/Demangle.h"
2626
#include "llvm/IR/Constants.h"
2727
#include "llvm/IR/Function.h"
28+
#include "llvm/IR/InstIterator.h"
29+
#include "llvm/IR/IntrinsicInst.h"
2830
#include "llvm/IR/Module.h"
2931
#include "llvm/IR/PassInstrumentation.h"
3032
#include "llvm/IR/PassManager.h"
@@ -139,6 +141,11 @@ static cl::opt<std::string> IRDumpDirectory(
139141
"files in this directory rather than written to stderr"),
140142
cl::Hidden, cl::value_desc("filename"));
141143

144+
static cl::opt<bool>
145+
DroppedVarStats("dropped-variable-stats", cl::Hidden,
146+
cl::desc("Dump dropped debug variables stats"),
147+
cl::init(false));
148+
142149
template <typename IRUnitT> static const IRUnitT *unwrapIR(Any IR) {
143150
const IRUnitT **IRPtr = llvm::any_cast<const IRUnitT *>(&IR);
144151
return IRPtr ? *IRPtr : nullptr;
@@ -2441,19 +2448,140 @@ void DotCfgChangeReporter::registerCallbacks(
24412448
}
24422449
}
24432450

2451+
void DroppedVariableStats::registerCallbacks(
2452+
PassInstrumentationCallbacks &PIC) {
2453+
if (!DroppedVarStats)
2454+
return;
2455+
2456+
PIC.registerBeforeNonSkippedPassCallback(
2457+
[this](StringRef P, Any IR) { return this->runBeforePass(P, IR); });
2458+
PIC.registerAfterPassCallback(
2459+
[this](StringRef P, Any IR, const PreservedAnalyses &PA) {
2460+
return this->runAfterPass(P, IR, PA);
2461+
});
2462+
}
2463+
2464+
void DroppedVariableStats::runBeforePass(StringRef PassID, Any IR) {
2465+
DebugVariablesBefore.push_back(llvm::DenseSet<VarID>());
2466+
DebugVariablesAfter.push_back(llvm::DenseSet<VarID>());
2467+
if (auto *M = unwrapIR<Module>(IR))
2468+
return this->runOnModule(M, true);
2469+
if (auto *F = unwrapIR<Function>(IR))
2470+
return this->runOnFunction(F, true);
2471+
return;
2472+
}
2473+
2474+
void DroppedVariableStats::runOnFunction(const Function *F, bool Before) {
2475+
auto &VarIDs = (Before ? DebugVariablesBefore : DebugVariablesAfter).back();
2476+
for (const auto &I : instructions(F)) {
2477+
for (DbgRecord &DR : I.getDbgRecordRange()) {
2478+
if (auto *Dbg = dyn_cast<DbgVariableRecord>(&DR)) {
2479+
auto *DbgVar = Dbg->getVariable();
2480+
auto DbgLoc = DR.getDebugLoc();
2481+
VarID Key{cast<DILocalScope>(DbgVar->getScope()),
2482+
DbgLoc->getInlinedAtScope(), DbgVar};
2483+
VarIDs.insert(Key);
2484+
}
2485+
}
2486+
}
2487+
}
2488+
2489+
void DroppedVariableStats::runOnModule(const Module *M, bool Before) {
2490+
for (auto &F : *M)
2491+
runOnFunction(&F, Before);
2492+
}
2493+
2494+
void DroppedVariableStats::removeVarFromAllSets(VarID Var) {
2495+
// Do not remove Var from the last element, it will be popped from the stack
2496+
// anyway.
2497+
for (auto &Elem : llvm::drop_end(DebugVariablesBefore))
2498+
Elem.erase(Var);
2499+
}
2500+
2501+
void DroppedVariableStats::makeDISubprogramToFunctionMap(
2502+
const llvm::Module *M) {
2503+
for (auto &F : *M) {
2504+
if (auto *SP = cast_or_null<const DISubprogram>(F.getSubprogram()))
2505+
SubprogramToFunctionMap[SP] = &F;
2506+
}
2507+
}
2508+
2509+
void DroppedVariableStats::runAfterPass(StringRef PassID, Any IR,
2510+
const PreservedAnalyses &PA) {
2511+
unsigned DroppedCount = 0;
2512+
std::string PassLevel;
2513+
std::string FuncOrModName;
2514+
if (auto *M = unwrapIR<Module>(IR)) {
2515+
this->runOnModule(M, false);
2516+
PassLevel = "Module";
2517+
FuncOrModName = M->getName();
2518+
makeDISubprogramToFunctionMap(M);
2519+
} else if (auto *F = unwrapIR<Function>(IR)) {
2520+
this->runOnFunction(F, false);
2521+
PassLevel = "Function";
2522+
FuncOrModName = F->getName();
2523+
makeDISubprogramToFunctionMap(F->getParent());
2524+
} else {
2525+
return;
2526+
}
2527+
for (auto Var : DebugVariablesBefore.back()) {
2528+
if (!DebugVariablesAfter.back().contains(Var)) {
2529+
// Ensure that a variable that doesn't exist in the DebugVariablesAfter
2530+
// map is actually dropped from the pass and not removed due to the
2531+
// Function being removed due to code elimination. Do this by checking if
2532+
// the InlinedAt for a debug variable is in the SubprogramToFunctionMap,
2533+
// only if it exists, find a real instruction in the function obtained by
2534+
// the SubprogramToFunctionMap that has the same scope as the debug
2535+
// variable, if such an instruction exists, the debug variable has been
2536+
// dropped.
2537+
// TODO: Improve the dropped variable statistics by not just checking if
2538+
// the scope of an Insturction matches the scope of the debug variable but
2539+
// if any child scope matches it too.
2540+
const DISubprogram *InlinedAt = nullptr;
2541+
if (auto *LS = dyn_cast<const DILexicalBlock>(std::get<1>(Var)))
2542+
InlinedAt = LS->getSubprogram();
2543+
else if (auto *DSP = dyn_cast<const DISubprogram>(std::get<1>(Var)))
2544+
InlinedAt = DSP;
2545+
if (InlinedAt) {
2546+
auto It = SubprogramToFunctionMap.find(InlinedAt);
2547+
if (It != SubprogramToFunctionMap.end() && !It->second->empty()) {
2548+
for (const auto &I : instructions(It->second)) {
2549+
auto *DbgLoc = I.getDebugLoc().get();
2550+
if (DbgLoc) {
2551+
auto *Scope = cast<DILocalScope>(DbgLoc->getScope());
2552+
if (Scope == It->first) {
2553+
DroppedCount++;
2554+
break;
2555+
}
2556+
}
2557+
}
2558+
}
2559+
}
2560+
removeVarFromAllSets(Var);
2561+
}
2562+
}
2563+
if (DroppedCount > 0)
2564+
llvm::outs() << PassLevel << ", " << PassID << ", " << DroppedCount << ", "
2565+
<< FuncOrModName << "\n";
2566+
DebugVariablesBefore.pop_back();
2567+
DebugVariablesAfter.pop_back();
2568+
SubprogramToFunctionMap.clear();
2569+
return;
2570+
}
2571+
24442572
StandardInstrumentations::StandardInstrumentations(
24452573
LLVMContext &Context, bool DebugLogging, bool VerifyEach,
24462574
PrintPassOptions PrintPassOpts)
2447-
: PrintPass(DebugLogging, PrintPassOpts),
2448-
OptNone(DebugLogging),
2575+
: PrintPass(DebugLogging, PrintPassOpts), OptNone(DebugLogging),
24492576
OptPassGate(Context),
24502577
PrintChangedIR(PrintChanged == ChangePrinter::Verbose),
24512578
PrintChangedDiff(PrintChanged == ChangePrinter::DiffVerbose ||
24522579
PrintChanged == ChangePrinter::ColourDiffVerbose,
24532580
PrintChanged == ChangePrinter::ColourDiffVerbose ||
24542581
PrintChanged == ChangePrinter::ColourDiffQuiet),
24552582
WebsiteChangeReporter(PrintChanged == ChangePrinter::DotCfgVerbose),
2456-
Verify(DebugLogging), VerifyEach(VerifyEach) {}
2583+
Verify(DebugLogging), DroppedStats(DroppedVarStats),
2584+
VerifyEach(VerifyEach) {}
24572585

24582586
PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter =
24592587
nullptr;
@@ -2528,6 +2656,7 @@ void StandardInstrumentations::registerCallbacks(
25282656
WebsiteChangeReporter.registerCallbacks(PIC);
25292657
ChangeTester.registerCallbacks(PIC);
25302658
PrintCrashIR.registerCallbacks(PIC);
2659+
DroppedStats.registerCallbacks(PIC);
25312660
if (MAM)
25322661
PreservedCFGChecker.registerCallbacks(PIC, *MAM);
25332662

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

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

0 commit comments

Comments
 (0)