This is an archive of the discontinued LLVM Phabricator instance.

[codeview] Implement FPO data assembler directives
ClosedPublic

Authored by rnk on Oct 10 2017, 6:19 PM.

Details

Summary

This adds a set of new directives that describe 32-bit x86 prologues.
The directives are limited and do not expose the full complexity of
codeview FPO data. They are merely a convenience for the compiler to
generate more readable assembly so we don't need to generate tons of
labels in CodeGen. If our prologue emission changes in the future, we
can change the set of available directives to suit our needs. These are
modelled after the .seh_ directives, which use a different format that
interacts with exception handling.

The directives are:

.cv_fpo_proc _foo
.cv_fpo_pushreg ebp/ebx/etc
.cv_fpo_setframe ebp/esi/etc
.cv_fpo_stackalloc 200
.cv_fpo_endprologue
.cv_fpo_endproc
.cv_fpo_data _foo

I tried to follow the implementation of ARM EHABI CFI directives by
sinking most directives out of MCStreamer and into X86TargetStreamer.
This helps avoid polluting non-X86 code with WinCOFF specific logic.

I used cdb to confirm that this can show locals in parent CSRs in a few
cases, most importantly the one where we use ESI as a frame pointer,
i.e. the one in http://crbug.com/756153#c28

Once we have cdb integration in debuginfo-tests, we can add integration
tests there.

Diff Detail

Repository
rL LLVM

Event Timeline

rnk created this revision.Oct 10 2017, 6:19 PM
majnemer edited edge metadata.Oct 10 2017, 8:05 PM

Cool! I've always wondered if we'd end up with an implementation of this :)

Should we have a shrink-wrapping test?

llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
246 ↗(On Diff #118520)

It'd be cool to get this to work, if only to make sure that we understand the format properly.

263–266 ↗(On Diff #118520)

Shouldn't X86::EAX be $eax, etc?

315 ↗(On Diff #118520)

How would you extend the assembler directive to support this?

316 ↗(On Diff #118520)

Does MSVC stick anything interesting in here? What's it for?

llvm/test/MC/COFF/cv-fpo-setframe.s
48 ↗(On Diff #118520)

How would this work with this FPO format?

I'd also be interested in seeing a stack frame with spills and stack realignment.

llvm/lib/Target/X86/X86FrameLowering.cpp
1127 ↗(On Diff #118520)

I think this would be more clear as NeedsWinFPO.

rnk marked 4 inline comments as done.Oct 11 2017, 9:46 AM
rnk added inline comments.
llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
263–266 ↗(On Diff #118520)

BTW, breakpad provides some other FPO data references:
https://chromium.googlesource.com/breakpad/breakpad/+/master/src/processor/stackwalker_x86.cc

I guess I'll put all the GPRs in here, but leave the CV regnum fallback. Realistically only these registers and the other CSRs (ESI, EDI, and EBX) will appear in these postfix programs.

315 ↗(On Diff #118520)

I added a number to .cv_proc for this. Might as well do it now.

316 ↗(On Diff #118520)

I've only seen zeros so far. I think you'd have to screw around with dynamic allocas to make it do something.

llvm/test/MC/COFF/cv-fpo-setframe.s
48 ↗(On Diff #118520)

The FrameData records act like nested scopes, where inner ones override the outer ones. They seem to assume the epilogue is at the end of the function, and the PC ranges look like nested intervals like this:

FrameData {
  RvaStart: 0x0
  CodeSize: 0x3C
FrameData {
  RvaStart: 0x1
  CodeSize: 0x3A
FrameData {
  RvaStart: 0x2
  CodeSize: 0x38
FrameData {
  RvaStart: 0x3
  CodeSize: 0x36
FrameData {
  RvaStart: 0x4
  CodeSize: 0x34

The start increases by one each time as it steps over each push, but the size decreases by two, which uncovers the corresponding pop.

Obviously, this is problematic in the face of LLVM's tail duplication and mid-function epilogues, so I doubt it'll ever get done.

rnk updated this revision to Diff 118633.Oct 11 2017, 9:47 AM
rnk marked 2 inline comments as done.
  • Implement ParamsSize
  • Print symbolic register names
rnk added a comment.Oct 11 2017, 9:55 AM

I'd also be interested in seeing a stack frame with spills and stack realignment.

I'll do that, but it's not very interesting because we basically use a normal EBP-based prologue before AND-ing the stack pointer and optionally copying it to ESI. As far as the unwinder is concerned, it's pretty boring.

It'd also be nice to have a test showing how it interacts with stack cookies.

I think our implementation of these happens in LLVM IR, so it isn't very interesting, but maybe worth adding.

Should we have a shrink-wrapping test?

This is definitely worth testing, but it should mostly work since it basically splits the prologue block after this stuff is inserted and the label ranges should just work out. Let's find out.

In D38776#894832, @rnk wrote:

I'd also be interested in seeing a stack frame with spills and stack realignment.

I'll do that, but it's not very interesting because we basically use a normal EBP-based prologue before AND-ing the stack pointer and optionally copying it to ESI. As far as the unwinder is concerned, it's pretty boring.

I guess I remembered the MSVC prologue sequence.

It'd also be nice to have a test showing how it interacts with stack cookies.

I think our implementation of these happens in LLVM IR, so it isn't very interesting, but maybe worth adding.

True.

Should we have a shrink-wrapping test?

This is definitely worth testing, but it should mostly work since it basically splits the prologue block after this stuff is inserted and the label ranges should just work out. Let's find out.

Awesome.

majnemer added inline comments.Oct 11 2017, 10:55 AM
llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
264 ↗(On Diff #118633)

Comment needs to be updated now that you added more symbolic names.

330 ↗(On Diff #118633)

I'd maybe include a comment saying that MSVC seems to always set this to 0.

rnk updated this revision to Diff 118645.Oct 11 2017, 11:03 AM
  • add ssp, realign, vla, and shrink wrapping test cases
rnk marked an inline comment as done.Oct 11 2017, 11:05 AM
rnk added inline comments.
llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
264 ↗(On Diff #118633)

I did update it, it now documents that we differ from MSVC here.

rnk updated this revision to Diff 118646.Oct 11 2017, 11:06 AM
  • Comment MaxStackSize more
majnemer accepted this revision.Oct 11 2017, 12:32 PM

LGTM

llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
264 ↗(On Diff #118633)

Misread it, sorry :(

307–309 ↗(On Diff #118646)

Are these sorted by register? If not, you could get more string sharing if you sorted first.

This revision is now accepted and ready to land.Oct 11 2017, 12:32 PM
hans accepted this revision.Oct 11 2017, 2:05 PM

looks amazing to me

llvm/lib/Target/X86/MCTargetDesc/X86TargetStreamer.h
9 ↗(On Diff #118646)

ultra nit: drop the // it's cleaner

rnk marked an inline comment as done.Oct 11 2017, 2:19 PM
rnk added inline comments.
llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
307–309 ↗(On Diff #118646)

The strings should be well-shared because we typically allocate CSRs in the same order in every function. I like not sorting them because then the previous FrameFunc is usually a prefix of the next FrameFunc, which you can see in the test cases here.

This revision was automatically updated to reflect the committed changes.