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