diff options
author | Jeremy Morse <jeremy.morse@sony.com> | 2024-11-12 15:09:40 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-12 15:09:40 +0000 |
commit | bf483ddb42065405e345393e022dc72357ec5a3a (patch) | |
tree | 4b559e5decc186d1689c25d942552c5d7491a071 /llvm/lib | |
parent | eea8b44aaa3464f52dea1d56ca47e0519b08fd36 (diff) | |
download | llvm-bf483ddb42065405e345393e022dc72357ec5a3a.zip llvm-bf483ddb42065405e345393e022dc72357ec5a3a.tar.gz llvm-bf483ddb42065405e345393e022dc72357ec5a3a.tar.bz2 |
[DWARF] Emit a worst-case prologue_end flag for pathological inputs (#107849)
prologue_end usually indicates where the end of the function-initialization
lies, and is where debuggers usually choose to put the initial breakpoint
for a function. Our current algorithm piggy-backs it on the first available
source-location: which doesn't necessarily have anything to do with the
start of the function.
To avoid this in heavily-optimised code that lacks many useful source
locations, pick a worst-case "if all else fails" prologue_end location, of
the first instruction that appears to do meaningful computation. It'll be
given the function-scope line number, which should run-on from the start of
the function anyway. This means if your code is completely inverted by the
optimiser, you can at least put a breakpoint at the _start_ like you
expect, even if it's difficult to then step through.
This patch also attempts to preserve some good behaviour we have without
optimisations -- at O0, if the prologue immediately falls into a loop body
without any computation happening, then prologue_end lands at the start of
that loop. This is desirable; but does mean we need to do more work to
detect and support those situations.
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 144 |
1 files changed, 120 insertions, 24 deletions
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 88ed3f5..7ccd0bc 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -2062,6 +2062,16 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) { unsigned LastAsmLine = Asm->OutStreamer->getContext().getCurrentDwarfLoc().getLine(); + if (!DL && MI == PrologEndLoc) { + // In rare situations, we might want to place the end of the prologue + // somewhere that doesn't have a source location already. It should be in + // the entry block. + assert(MI->getParent() == &*MI->getMF()->begin()); + recordSourceLine(SP->getScopeLine(), 0, SP, + DWARF2_FLAG_PROLOGUE_END | DWARF2_FLAG_IS_STMT); + return; + } + bool PrevInstInSameSection = (!PrevInstBB || PrevInstBB->getSectionID() == MI->getParent()->getSectionID()); @@ -2137,32 +2147,109 @@ static std::pair<const MachineInstr *, bool> findPrologueEndLoc(const MachineFunction *MF) { // First known non-DBG_VALUE and non-frame setup location marks // the beginning of the function body. - const MachineInstr *LineZeroLoc = nullptr; + const auto &TII = *MF->getSubtarget().getInstrInfo(); + const MachineInstr *NonTrivialInst = nullptr; const Function &F = MF->getFunction(); // Some instructions may be inserted into prologue after this function. Must // keep prologue for these cases. bool IsEmptyPrologue = !(F.hasPrologueData() || F.getMetadata(LLVMContext::MD_func_sanitize)); - for (const auto &MBB : *MF) { - for (const auto &MI : MBB) { - if (!MI.isMetaInstruction()) { - if (!MI.getFlag(MachineInstr::FrameSetup) && MI.getDebugLoc()) { - // Scan forward to try to find a non-zero line number. The - // prologue_end marks the first breakpoint in the function after the - // frame setup, and a compiler-generated line 0 location is not a - // meaningful breakpoint. If none is found, return the first - // location after the frame setup. - if (MI.getDebugLoc().getLine()) - return std::make_pair(&MI, IsEmptyPrologue); - - LineZeroLoc = &MI; - } - IsEmptyPrologue = false; - } + + // Helper lambda to examine each instruction and potentially return it + // as the prologue_end point. + auto ExamineInst = [&](const MachineInstr &MI) + -> std::optional<std::pair<const MachineInstr *, bool>> { + // Is this instruction trivial data shuffling or frame-setup? + bool isCopy = (TII.isCopyInstr(MI) ? true : false); + bool isTrivRemat = TII.isTriviallyReMaterializable(MI); + bool isFrameSetup = MI.getFlag(MachineInstr::FrameSetup); + + if (!isFrameSetup && MI.getDebugLoc()) { + // Scan forward to try to find a non-zero line number. The + // prologue_end marks the first breakpoint in the function after the + // frame setup, and a compiler-generated line 0 location is not a + // meaningful breakpoint. If none is found, return the first + // location after the frame setup. + if (MI.getDebugLoc().getLine()) + return std::make_pair(&MI, IsEmptyPrologue); + } + + // Keep track of the first "non-trivial" instruction seen, i.e. anything + // that doesn't involve shuffling data around or is a frame-setup. + if (!isCopy && !isTrivRemat && !isFrameSetup && !NonTrivialInst) + NonTrivialInst = &MI; + + IsEmptyPrologue = false; + return std::nullopt; + }; + + // Examine all the instructions at the start of the function. This doesn't + // necessarily mean just the entry block: unoptimised code can fall-through + // into an initial loop, and it makes sense to put the initial breakpoint on + // the first instruction of such a loop. However, if we pass branches, we're + // better off synthesising an early prologue_end. + auto CurBlock = MF->begin(); + auto CurInst = CurBlock->begin(); + while (true) { + // Skip empty blocks, in rare cases the entry can be empty. + if (CurInst == CurBlock->end()) { + ++CurBlock; + CurInst = CurBlock->begin(); + continue; } + + // Check whether this non-meta instruction a good position for prologue_end. + if (!CurInst->isMetaInstruction()) { + auto FoundInst = ExamineInst(*CurInst); + if (FoundInst) + return *FoundInst; + } + + // Try to continue searching, but use a backup-location if substantive + // computation is happening. + auto NextInst = std::next(CurInst); + if (NextInst != CurInst->getParent()->end()) { + // Continue examining the current block. + CurInst = NextInst; + continue; + } + + // We've reached the end of the block. Did we just look at a terminator? + if (CurInst->isTerminator()) { + // Some kind of "real" control flow is occurring. At the very least + // we would have to start exploring the CFG, a good signal that the + // prologue is over. + break; + } + + // If we've already fallen through into a loop, don't fall through + // further, use a backup-location. + if (CurBlock->pred_size() > 1) + break; + + // Fall-through from entry to the next block. This is common at -O0 when + // there's no initialisation in the function. Bail if we're also at the + // end of the function. + if (++CurBlock == MF->end()) + break; + CurInst = CurBlock->begin(); + } + + // We couldn't find any source-location, suggesting all meaningful information + // got optimised away. Set the prologue_end to be the first non-trivial + // instruction, which will get the scope line number. This is better than + // nothing. + // Only do this in the entry block, as we'll be giving it the scope line for + // the function. Return IsEmptyPrologue==true if we've picked the first + // instruction. + if (NonTrivialInst && NonTrivialInst->getParent() == &*MF->begin()) { + IsEmptyPrologue = NonTrivialInst == &*MF->begin()->begin(); + return std::make_pair(NonTrivialInst, IsEmptyPrologue); } - return std::make_pair(LineZeroLoc, IsEmptyPrologue); + + // If the entry path is empty, just don't have a prologue_end at all. + return std::make_pair(nullptr, IsEmptyPrologue); } /// Register a source line with debug info. Returns the unique label that was @@ -2194,12 +2281,21 @@ DwarfDebug::emitInitialLocDirective(const MachineFunction &MF, unsigned CUID) { bool IsEmptyPrologue = PrologEnd.second; // If the prolog is empty, no need to generate scope line for the proc. - if (IsEmptyPrologue) - // In degenerate cases, we can have functions with no source locations - // at all. These want a scope line, to avoid a totally empty function. - // Thus, only skip scope line if there's location to place prologue_end. - if (PrologEndLoc) - return PrologEndLoc; + if (IsEmptyPrologue) { + // If there's nowhere to put a prologue_end flag, emit a scope line in case + // there are simply no source locations anywhere in the function. + if (PrologEndLoc) { + // Avoid trying to assign prologue_end to a line-zero location. + // Instructions with no DebugLoc at all are fine, they'll be given the + // scope line nuumber. + const DebugLoc &DL = PrologEndLoc->getDebugLoc(); + if (!DL || DL->getLine() != 0) + return PrologEndLoc; + + // Later, don't place the prologue_end flag on this line-zero location. + PrologEndLoc = nullptr; + } + } // Ensure the compile unit is created if the function is called before // beginFunction(). |