This is an archive of the discontinued LLVM Phabricator instance.

Emit line numbers in CodeView for trailing (after `ret`) blocks from inlined functions
ClosedPublic

Authored by dpaoliello on Aug 30 2023, 2:03 PM.

Details

Summary

Issue Details:
When building up line information for CodeView debug info, LLVM attempts to gather the "range" of instructions within a function as these are printed together in a single record. If there is an inlined function, then those lines are attributed to the original function to enable generating S_INLINESITE records. However, this thus requires there to be instructions from the inlining function after the inlined function otherwise the instruction range would not include the inlined function.

Fix Details:
Include any inlined functions when finding the extent of a function in getFunctionLineEntries

Diff Detail

Event Timeline

dpaoliello created this revision.Aug 30 2023, 2:03 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 30 2023, 2:03 PM
Herald added a subscriber: hiraditya. · View Herald Transcript
dpaoliello requested review of this revision.Aug 30 2023, 2:03 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 30 2023, 2:03 PM
dpaoliello updated this revision to Diff 555415.Sep 1 2023, 9:32 AM

Fix lld test

rnk added a comment.Sep 1 2023, 10:14 AM

Thanks for the patch, this is gnarly code. At the time I was probably thinking that flat vectors are good for performance, and that we'll have tons of line table entries, but as the inline location handling evolved, it's gotten quite complicated.

llvm/lib/MC/MCCodeView.cpp
277–278

It would be LLVM-style to early return FilteredLines on map lookup failure and reduce nesting.

I also think this code is reimplementing getLineExtent, we could just call that, and then use names like Lo/Hi or L/R for the extent instead of I->second.first/second.

Then, I propose you increase the line extent using the same code from encodeinlinelinetable:

// Include all child inline call sites in our .cv_loc extent.
MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(Frag.SiteFuncId);
for (auto &KV : SiteInfo->InlinedAtMap) {
  unsigned ChildId = KV.first;
  auto Extent = getLineExtent(ChildId);
  LocBegin = std::min(LocBegin, Extent.first);
  LocEnd = std::max(LocEnd, Extent.second);
}
281

This seems like it only handles one level of inlining. If this cv_loc is for an inline call site two levels deep, you aren't going to update the extent of the parent non-inline function to include this cv_loc entry.

I think this code in the inline line table emitter is meant to expand the range of cv_loc entries that we consider for inline line tables:
https://github.com/llvm/llvm-project/blob/main/llvm/lib/MC/MCCodeView.cpp#L468

Could we solve the problem by adding similar logic to CodeViewContext::getFunctionLineEntries before it's main loop?

llvm/test/DebugInfo/COFF/trailing-inlined-function.ll
1 ↗(On Diff #555415)

Can you make this a .s test? The fix is in the way MC handles .cv_loc directives, so it should reduce the scope of the test.

Does rustc have an equivalent of Clang -g1 / -gmlt, to only produce source locations, no variable locations? That has the potential to reduce variable debug info and make the test smaller.

It still makes sense to me to use llvm-readobj, since that keeps LLD and llvm-symbolizer out of the test.

dpaoliello updated this revision to Diff 555516.Sep 1 2023, 3:23 PM
dpaoliello edited the summary of this revision. (Show Details)

PR feedback: now looking for lines for both current func and inlinees in getFunctionLineEntries instead of adding to both in addLineEntry. Switched to .s test.

dpaoliello marked 3 inline comments as done.Sep 1 2023, 3:28 PM
dpaoliello added inline comments.
llvm/lib/MC/MCCodeView.cpp
277–278

Yep, nice, checking for inline extents in getFunctionLineEntries has the same effect.

281

As suggested, checking for inline extents in getFunctionLineEntries works and handles multiple levels of inlining.

llvm/test/DebugInfo/COFF/trailing-inlined-function.ll
1 ↗(On Diff #555415)

Done: switched to .s and reduced debug info leve.

dpaoliello updated this revision to Diff 555518.Sep 1 2023, 3:29 PM
dpaoliello marked 3 inline comments as done.

Reduced debug info in test file

rnk accepted this revision.Sep 1 2023, 3:45 PM

lgtm, thanks!

lld/test/COFF/symbolizer-line-numbers.s
65–66

Right, this now reflects the call site, which was at column 10 instead of the return statement start at col 3.

llvm/lib/MC/MCCodeView.cpp
539

I see more duplicated logic here, but that refactoring would be way out of scope for this change. It's been years, I think there was some vague intention to have getFunctionLineEntries usable for both inline and non-inline line tables, but I really cannot recall.

llvm/test/DebugInfo/COFF/trailing-inlined-function.s
144

I see you already deleted the unneeded variable records (.cv_defrange*), so don't worry about this, but for next time, if you emit verbose assembly (default if you pass through llc), you get codeview comments, see them on godbolt:
https://gcc.godbolt.org/z/cjca5jPTE

You can see the S_LOCAL records and others, it makes it easier to hack up the assembly.

This revision is now accepted and ready to land.Sep 1 2023, 3:45 PM
dpaoliello updated this revision to Diff 555534.Sep 1 2023, 5:09 PM

Fix test failures