|
25 | 25 | #include "llvm/Demangle/Demangle.h"
|
26 | 26 | #include "llvm/IR/Constants.h"
|
27 | 27 | #include "llvm/IR/Function.h"
|
| 28 | +#include "llvm/IR/InstIterator.h" |
| 29 | +#include "llvm/IR/IntrinsicInst.h" |
28 | 30 | #include "llvm/IR/Module.h"
|
29 | 31 | #include "llvm/IR/PassInstrumentation.h"
|
30 | 32 | #include "llvm/IR/PassManager.h"
|
@@ -139,6 +141,11 @@ static cl::opt<std::string> IRDumpDirectory(
|
139 | 141 | "files in this directory rather than written to stderr"),
|
140 | 142 | cl::Hidden, cl::value_desc("filename"));
|
141 | 143 |
|
| 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 | + |
142 | 149 | template <typename IRUnitT> static const IRUnitT *unwrapIR(Any IR) {
|
143 | 150 | const IRUnitT **IRPtr = llvm::any_cast<const IRUnitT *>(&IR);
|
144 | 151 | return IRPtr ? *IRPtr : nullptr;
|
@@ -2441,19 +2448,140 @@ void DotCfgChangeReporter::registerCallbacks(
|
2441 | 2448 | }
|
2442 | 2449 | }
|
2443 | 2450 |
|
| 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 | + |
2444 | 2572 | StandardInstrumentations::StandardInstrumentations(
|
2445 | 2573 | LLVMContext &Context, bool DebugLogging, bool VerifyEach,
|
2446 | 2574 | PrintPassOptions PrintPassOpts)
|
2447 |
| - : PrintPass(DebugLogging, PrintPassOpts), |
2448 |
| - OptNone(DebugLogging), |
| 2575 | + : PrintPass(DebugLogging, PrintPassOpts), OptNone(DebugLogging), |
2449 | 2576 | OptPassGate(Context),
|
2450 | 2577 | PrintChangedIR(PrintChanged == ChangePrinter::Verbose),
|
2451 | 2578 | PrintChangedDiff(PrintChanged == ChangePrinter::DiffVerbose ||
|
2452 | 2579 | PrintChanged == ChangePrinter::ColourDiffVerbose,
|
2453 | 2580 | PrintChanged == ChangePrinter::ColourDiffVerbose ||
|
2454 | 2581 | PrintChanged == ChangePrinter::ColourDiffQuiet),
|
2455 | 2582 | WebsiteChangeReporter(PrintChanged == ChangePrinter::DotCfgVerbose),
|
2456 |
| - Verify(DebugLogging), VerifyEach(VerifyEach) {} |
| 2583 | + Verify(DebugLogging), DroppedStats(DroppedVarStats), |
| 2584 | + VerifyEach(VerifyEach) {} |
2457 | 2585 |
|
2458 | 2586 | PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter =
|
2459 | 2587 | nullptr;
|
@@ -2528,6 +2656,7 @@ void StandardInstrumentations::registerCallbacks(
|
2528 | 2656 | WebsiteChangeReporter.registerCallbacks(PIC);
|
2529 | 2657 | ChangeTester.registerCallbacks(PIC);
|
2530 | 2658 | PrintCrashIR.registerCallbacks(PIC);
|
| 2659 | + DroppedStats.registerCallbacks(PIC); |
2531 | 2660 | if (MAM)
|
2532 | 2661 | PreservedCFGChecker.registerCallbacks(PIC, *MAM);
|
2533 | 2662 |
|
|
0 commit comments