17
17
#include " DwarfExpression.h"
18
18
#include " DwarfUnit.h"
19
19
#include " llvm/ADT/APInt.h"
20
+ #include " llvm/ADT/ScopeExit.h"
20
21
#include " llvm/ADT/Statistic.h"
21
22
#include " llvm/ADT/StringExtras.h"
22
23
#include " llvm/ADT/Twine.h"
@@ -170,6 +171,9 @@ static cl::opt<DwarfDebug::MinimizeAddrInV5> MinimizeAddrInV5Option(
170
171
" Stuff" )),
171
172
cl::init(DwarfDebug::MinimizeAddrInV5::Default));
172
173
174
+ static cl::opt<bool > KeyInstructionsAreStmts (" dwarf-use-key-instructions" ,
175
+ cl::Hidden, cl::init(false ));
176
+
173
177
static constexpr unsigned ULEB128PadSize = 4 ;
174
178
175
179
void DebugLocDwarfExpression::emitOp (uint8_t Op, const char *Comment) {
@@ -2075,6 +2079,10 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
2075
2079
unsigned LastAsmLine =
2076
2080
Asm->OutStreamer ->getContext ().getCurrentDwarfLoc ().getLine ();
2077
2081
2082
+ bool IsKey = false ;
2083
+ if (KeyInstructionsAreStmts && DL && DL.getLine ())
2084
+ IsKey = KeyInstructions.contains (MI);
2085
+
2078
2086
if (!DL && MI == PrologEndLoc) {
2079
2087
// In rare situations, we might want to place the end of the prologue
2080
2088
// somewhere that doesn't have a source location already. It should be in
@@ -2089,17 +2097,22 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
2089
2097
(!PrevInstBB ||
2090
2098
PrevInstBB->getSectionID () == MI->getParent ()->getSectionID ());
2091
2099
bool ForceIsStmt = ForceIsStmtInstrs.contains (MI);
2092
- if (DL == PrevInstLoc && PrevInstInSameSection && !ForceIsStmt ) {
2100
+ if (PrevInstInSameSection && !ForceIsStmt && DL. isSameSourceLocation (PrevInstLoc) ) {
2093
2101
// If we have an ongoing unspecified location, nothing to do here.
2094
2102
if (!DL)
2095
2103
return ;
2096
- // We have an explicit location, same as the previous location.
2097
- // But we might be coming back to it after a line 0 record.
2098
- if ((LastAsmLine == 0 && DL.getLine () != 0 ) || Flags) {
2099
- // Reinstate the source location but not marked as a statement.
2100
- RecordSourceLine (DL, Flags);
2104
+
2105
+ // Skip this if the instruction is Key, else we might accidentally miss an
2106
+ // is_stmt.
2107
+ if (!IsKey) {
2108
+ // We have an explicit location, same as the previous location.
2109
+ // But we might be coming back to it after a line 0 record.
2110
+ if ((LastAsmLine == 0 && DL.getLine () != 0 ) || Flags) {
2111
+ // Reinstate the source location but not marked as a statement.
2112
+ RecordSourceLine (DL, Flags);
2113
+ }
2114
+ return ;
2101
2115
}
2102
- return ;
2103
2116
}
2104
2117
2105
2118
if (!DL) {
@@ -2146,11 +2159,17 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
2146
2159
Flags |= DWARF2_FLAG_PROLOGUE_END | DWARF2_FLAG_IS_STMT;
2147
2160
PrologEndLoc = nullptr ;
2148
2161
}
2149
- // If the line changed, we call that a new statement; unless we went to
2150
- // line 0 and came back, in which case it is not a new statement.
2151
- unsigned OldLine = PrevInstLoc ? PrevInstLoc.getLine () : LastAsmLine;
2152
- if (DL.getLine () && (DL.getLine () != OldLine || ForceIsStmt))
2153
- Flags |= DWARF2_FLAG_IS_STMT;
2162
+
2163
+ if (KeyInstructionsAreStmts) {
2164
+ if (IsKey)
2165
+ Flags |= DWARF2_FLAG_IS_STMT;
2166
+ } else {
2167
+ // If the line changed, we call that a new statement; unless we went to
2168
+ // line 0 and came back, in which case it is not a new statement.
2169
+ unsigned OldLine = PrevInstLoc ? PrevInstLoc.getLine () : LastAsmLine;
2170
+ if (DL.getLine () && (DL.getLine () != OldLine || ForceIsStmt))
2171
+ Flags |= DWARF2_FLAG_IS_STMT;
2172
+ }
2154
2173
2155
2174
RecordSourceLine (DL, Flags);
2156
2175
@@ -2343,6 +2362,139 @@ DwarfDebug::emitInitialLocDirective(const MachineFunction &MF, unsigned CUID) {
2343
2362
return PrologEndLoc;
2344
2363
}
2345
2364
2365
+ void DwarfDebug::computeKeyInstructions (const MachineFunction *MF) {
2366
+ // New function - reset KeyInstructions.
2367
+ KeyInstructions.clear ();
2368
+
2369
+ // The current candidate is_stmt instructions for each source atom.
2370
+ // Map {(InlinedAt, Group): (Rank, Instructions)}.
2371
+ // NOTE: Anecdotally, for a large C++ blob, 99% of the instruction
2372
+ // SmallVectors contain 2 or fewer elements; use 2 inline elements.
2373
+ DenseMap<std::pair<DILocation *, uint32_t >,
2374
+ std::pair<uint16_t , SmallVector<const MachineInstr *, 2 >>>
2375
+ GroupCandidates;
2376
+
2377
+ // For each instruction:
2378
+ // * Skip insts without DebugLoc, AtomGroup or AtomRank, and line zeros.
2379
+ // * Check if insts in this group have been seen already in GroupCandidates.
2380
+ // * If this instr rank is equal, add this instruction to GroupCandidates.
2381
+ // Remove existing instructions from GroupCandidates if they have the
2382
+ // same parent.
2383
+ // * If this instr rank is higher (lower precedence), ignore it.
2384
+ // * If this instr rank is lower (higher precedence), erase existing
2385
+ // instructions from GroupCandidates and add this one.
2386
+ //
2387
+ // Then insert each GroupCandidates instruction into KeyInstructions.
2388
+
2389
+ for (auto &MBB : *MF) {
2390
+ // Rather than apply is_stmt directly to Key Instructions, we "float"
2391
+ // is_stmt up to the 1st instruction with the same line number in a
2392
+ // contiguous block. That instruction is called the "buoy". The
2393
+ // buoy gets reset if we encouner an instruction with an atom
2394
+ // group.
2395
+ const MachineInstr *Buoy = nullptr ;
2396
+ // The atom group number associated with Buoy which may be 0 if we haven't
2397
+ // encountered an atom group yet in this blob of instructions with the same
2398
+ // line number.
2399
+ uint64_t BuoyAtom = 0 ;
2400
+
2401
+ for (auto &MI : MBB) {
2402
+ if (MI.isMetaInstruction ())
2403
+ continue ;
2404
+
2405
+ if (!MI.getDebugLoc () || !MI.getDebugLoc ().getLine ())
2406
+ continue ;
2407
+
2408
+ // Reset the Buoy to this instruction if it has a different line number.
2409
+ if (!Buoy ||
2410
+ Buoy->getDebugLoc ().getLine () != MI.getDebugLoc ().getLine ()) {
2411
+ Buoy = &MI;
2412
+ BuoyAtom = 0 ; // Set later when we know which atom the buoy is used by.
2413
+ }
2414
+
2415
+ // Call instructions are handled specially - we always mark them as key
2416
+ // regardless of atom info.
2417
+ const auto &TII =
2418
+ *MI.getParent ()->getParent ()->getSubtarget ().getInstrInfo ();
2419
+ bool IsCallLike = MI.isCall () || TII.isTailCall (MI);
2420
+ if (IsCallLike) {
2421
+ assert (MI.getDebugLoc () && " Unexpectedly missing DL" );
2422
+
2423
+ // Calls are always key. Put the buoy (may not be the call) into
2424
+ // KeyInstructions directly rather than the candidate map to avoid it
2425
+ // being erased (and we may not have a group number for the call).
2426
+ KeyInstructions.insert (Buoy);
2427
+
2428
+ // Avoid floating any future is_stmts up to the call.
2429
+ Buoy = nullptr ;
2430
+ BuoyAtom = 0 ;
2431
+
2432
+ if (!MI.getDebugLoc ()->getAtomGroup () ||
2433
+ !MI.getDebugLoc ()->getAtomRank ())
2434
+ continue ;
2435
+ }
2436
+
2437
+ auto *InlinedAt = MI.getDebugLoc ()->getInlinedAt ();
2438
+ uint64_t Group = MI.getDebugLoc ()->getAtomGroup ();
2439
+ uint8_t Rank = MI.getDebugLoc ()->getAtomRank ();
2440
+ if (!Group || !Rank)
2441
+ continue ;
2442
+
2443
+ // Don't let is_stmts float past instructions from different source atoms.
2444
+ if (BuoyAtom && BuoyAtom != Group) {
2445
+ Buoy = &MI;
2446
+ BuoyAtom = Group;
2447
+ }
2448
+
2449
+ auto &[CandidateRank, CandidateInsts] =
2450
+ GroupCandidates[{InlinedAt, Group}];
2451
+
2452
+ // If CandidateRank is zero then CandidateInsts should be empty: there
2453
+ // are no other candidates for this group yet. If CandidateRank is nonzero
2454
+ // then CandidateInsts shouldn't be empty: we've got existing candidate
2455
+ // instructions.
2456
+ assert ((CandidateRank == 0 && CandidateInsts.empty ()) ||
2457
+ (CandidateRank != 0 && !CandidateInsts.empty ()));
2458
+
2459
+ assert (Rank && " expected nonzero rank" );
2460
+ // If we've seen other instructions in this group with higher precedence
2461
+ // (lower nonzero rank), don't add this one as a candidate.
2462
+ if (CandidateRank && CandidateRank < Rank)
2463
+ continue ;
2464
+
2465
+ // If we've seen other instructions in this group of the same rank,
2466
+ // discard any from this block (keeping the others). Else if we've
2467
+ // seen other instructions in this group of lower precedence (higher
2468
+ // rank), discard them all.
2469
+ if (CandidateRank == Rank)
2470
+ llvm::remove_if (CandidateInsts, [&MI](const MachineInstr *Candidate) {
2471
+ return MI.getParent () == Candidate->getParent ();
2472
+ });
2473
+ else if (CandidateRank > Rank)
2474
+ CandidateInsts.clear ();
2475
+
2476
+ if (Buoy) {
2477
+ // Add this candidate.
2478
+ CandidateInsts.push_back (Buoy);
2479
+ CandidateRank = Rank;
2480
+
2481
+ assert (!BuoyAtom || BuoyAtom == MI.getDebugLoc ()->getAtomGroup ());
2482
+ BuoyAtom = MI.getDebugLoc ()->getAtomGroup ();
2483
+ } else {
2484
+ // Don't add calls, because they've been dealt with already. This means
2485
+ // CandidateInsts might now be empty - handle that.
2486
+ assert (IsCallLike);
2487
+ if (CandidateInsts.empty ())
2488
+ CandidateRank = 0 ;
2489
+ }
2490
+ }
2491
+ }
2492
+
2493
+ for (const auto &[_, Insts] : GroupCandidates.values ())
2494
+ for (auto *I : Insts)
2495
+ KeyInstructions.insert (I);
2496
+ }
2497
+
2346
2498
// / For the function \p MF, finds the set of instructions which may represent a
2347
2499
// / change in line number from one or more of the preceding MBBs. Stores the
2348
2500
// / resulting set of instructions, which should have is_stmt set, in
@@ -2501,7 +2653,10 @@ void DwarfDebug::beginFunctionImpl(const MachineFunction *MF) {
2501
2653
PrologEndLoc = emitInitialLocDirective (
2502
2654
*MF, Asm->OutStreamer ->getContext ().getDwarfCompileUnitID ());
2503
2655
2504
- findForceIsStmtInstrs (MF);
2656
+ if (KeyInstructionsAreStmts)
2657
+ computeKeyInstructions (MF);
2658
+ else
2659
+ findForceIsStmtInstrs (MF);
2505
2660
}
2506
2661
2507
2662
unsigned
0 commit comments