This is an archive of the discontinued LLVM Phabricator instance.

[DWARF v5] emit DWARF v5 range lists (no support for fission yet)
ClosedPublic

Authored by wolfgangp on Jul 11 2018, 6:38 PM.

Details

Summary

This is a first cut at generating DWARF v5 range lists into the .debug_rnglists section. We are generating the range lists table specified by the standard, using the range list entry kinds DW_RLE_offset_pair and DW_RLE_start_length.

We do not support split DWARF yet, because as far as I can tell it requires support for .debug_addr to be able to refer back to locations in the executable.
Also, we don't yet use DW_FORM_rnglistx, so there is no offset table yet.

One of the modified test cases is fission-ranges.ll, which is a bit misleading as we don't support fission yet, but it already had a nice usable module, which avoids duplication.

Diff Detail

Repository
rL LLVM

Event Timeline

wolfgangp created this revision.Jul 11 2018, 6:38 PM
aprantl accepted this revision.Jul 11 2018, 11:22 PM

Thanks, I think this looks good!

lib/CodeGen/AsmPrinter/DwarfDebug.cpp
651 ↗(On Diff #155100)

perhaps add a getSkeletonOrInfoHolder() accessor?

lib/MC/MCObjectFileInfo.cpp
271 ↗(On Diff #155100)

Looks like that fits exactly into the 16 available characters :-)

This revision is now accepted and ready to land.Jul 11 2018, 11:22 PM

Thanks for the quick review.

lib/CodeGen/AsmPrinter/DwarfDebug.cpp
651 ↗(On Diff #155100)

That would require a few more changes in unrelated places (this stanza is used a few times elsewhere). I'll do that in a separate commit.

This revision was automatically updated to reflect the committed changes.
dblaikie added inline comments.
llvm/trunk/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
424

It looks like this would end up adding a range list base address to the CU for every range emitted - wouldn't it?

Yeah, looks like it does:

DW_AT_rnglists_base       (0x0000000c)
DW_AT_low_pc      (0x0000000000000000)
DW_AT_ranges      (0x0000002b
   [0x0000000000000000, 0x0000000000000033)
   [0x0000000000000040, 0x0000000000000046)
   [0x0000000000000000, 0x0000000000000006))
DW_AT_rnglists_base       (0x0000000c)

using this input:

bool x;
void f1() {
  f1();
  if (bool b = x) {
    f1();
    f1();
  }
}
__attribute__((section(".text.foo"))) void f2() { }
void f3() { }

Compile with dwarf5 to LLVM IR, reorder the 3 calls to f1 (inserting the first call between the second and third) to cause the scope of 'b' to have a hole in the middle (for that first call to f1) & so to be described by a range list rather than high/low pc.

Looks like the range list base attribute should be added in the same place DW_AT_GNU_ranges_base is set, I think?

llvm/trunk/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
2016–2021

I suspect this section doesn't make sense for DWARFv5 style range lists - there would never be a need to reset the base address to zero, because the forms themselves encode whether they are base address relative or not.

(in the old style range lists, you'd have to reset the base address to zero because the address pairs didn't describe whether they were absolute or relative - so setting it to zero would make the following address pairs absolute (relative to zero). It'd look more like "<base address> <relative pair> <relative pair> <base address 0> <abs pair> <abs pair>" in the new form it's "base address, relative pair, absolute pair, relative pair, etc" - absolute and relative pairs can be interleaved and each is self-descriptive about its absolute or relativeness)

Maybe there are some missing test cases too? Though they should be/probably can be covered by the original testing for UseDwarfRangesBaseAddressSpecifier - but so you might be able to reuse the test input for that ( test/DebugInfo/X86/range_reloc.ll ), but use DWARFv5 instead of UseDwarfRangesBaseAddressSpecfier.

2060–2068

Is it the best tradeoff to produce one shared range list, rather than a separate range list per CU? (I ask/think about that here, because then you wouldn't need a scan through all the CUs to see if any are non-empty)

Looks like the tradeoff is whether the bytes spent in the header are saved by reduced bytes in the debug_info section of being able to use smaller indexes, encoded with shorter encodings. I would imagine in practice for builds with multiple CUs (optimized/LTO situations) there are many ranges and the tradeoff tips in favor of using multiple separate range list lists - one for each CU?

2092–2095

To be honest, I'm not sure why the standard added this offset table - it seems to add extra bytes without any savings I can see (shorter encodings in the debug_info section - using indexes instead of offsets, but it doesn't save relocations or bytes overall, unless I'm missing something?). Meh. (@probinson - any ideas on what the purpose of this indirection is?)

2109–2110

I might rephrase this as "into the .debug_ranges section or, in DWARFv5 the .debug_rnglists section" - the current phrasing sounds like there are two different kinds of things "address ranges" and "rangelists" - to me they're the same thing, encoded differently (depending on the section).

Perhaps others have a different opinion on how this reads, etc?

2115–2123

Looks like the precondition that there are some ranges is already tested here - so no need to check it in emitDebugRnglists? the check there could be another assert, perhaps?

2130–2144

Might be worth splitting this out into a separate function so it's symmetric with emitDebugRnglists?

2222

Missing oxford comma between 'DW_AT_ranges_base' and 'or'. Though it also reads a little ambiguously - in English 'or' can often be read as 'exclusive or' (why some people put 'and/or').

Maybe "This DIE can have any/all of the following attributes: x, y, and z"? No big deal, just some thoughts.

llvm/trunk/test/DebugInfo/X86/rnglists-nobase.ll
11–19

This example might obscure what's really going on by having implicit functions from the global static initializer - it may be more illustrative to provide a more explicit example using section attributes to show which functions are in which sections, etc.

The existence of 3 functions wouldn't itself create ranges (if they were all in a single .text section they'd be covered by a single high/low pc), it's the fact that the static initializer for 'glob' creates functions in a .text.startup section. And that those two static initializer functions aren't emitted together - one, then 'foo', then the other (splitting them up - otherwise there'd be only two ranges, instead of 3)

__attribute__((section(".text.foo"))) void f1() {}
__attribute__((section(".text.bar"))) void f2() {}
__attribute__((section(".text.foo"))) void f3() {}

and I'd expect the optimal range list (in the absence of a debug_addr section) for this source to be:

DW_RLE_base_address (.text.foo+0)
DW_RLE_offset_pair (f1 begin/end relative to .text.foo+0)
DW_RLE_offset_pair (f3 begin/end relative to .text.foo+0)
DW_RLE_start_length (f2 begin, f2 length)

(aside: shorter encodings might be achievable (though maybe not sufficiently worthwhile/only a small improvement) if RLE_start_length implicitly set the base address, and if offset_pair was offset_length instead - but at least there's room to improve this over versions (there's versioning in the header, there's space in the RLE_* enum space, etc))

I think the reason this is missing the base_address+offset_pair opportunity is because the code is predicated on UseDwarfRangesBaseAddressSpecifier - which defaults to off, because I found some DWARF consumers couldn't handle it, I think. But that property isn't relevant to DWARFv5 - so the implementation should be modified to do the same thing no matter the value of 'UseDwarfRangesBaseAddressSpecifier'.

wolfgangp added inline comments.Jul 16 2018, 11:59 AM
llvm/trunk/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
424

Ah, thanks for finding this. I guess I missed that duplicates are not eliminated when attributes are added to a DIE. AT_rnglists_base should only be added if there is any use of the AT_ranges attribute and it seemed simpler to do this here but it's just driven by the number of ranges in the unit anyway. I'll fix this in a separate review since this one is already closed.

llvm/trunk/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
2060–2068

If we use DW_FORM_rnglistx with DW_AT_ranges, we would definitely save space in .debug_info when the number of ranges gets large, which is where it matters most. Perhaps I will add a FIXME comment effectively saying that when we support DW_FORM_rnglistx (along with the offset table), we'll switch to one rnglist table per CU.

2092–2095

My understanding is that DW_AT_ranges has to either use FORM_sec_offset (which needs to be relocated) or FORM_rnglistx, which is an index into the offset table. So with the offset table and FORM_rnglistx we'd be saving the relocations in .debug_info.

2115–2123

I thought the check here was only used in case the -no-dwarf-ranges-section option was on, so we still need the check for the normal case.

2130–2144

Ok, sounds good.

2222

Seems like this comment needs to be reworked for DWARF 5 anyway with some information on what is DWARF 5 specific and what's not. It also should go into the header, no? Let me take a stab at that in another review.

llvm/trunk/test/DebugInfo/X86/rnglists-nobase.ll
11–19

I'll rework the example in a separate review.

probinson added inline comments.Jul 16 2018, 12:37 PM
llvm/trunk/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
2092–2095

Right, the benefit is eliminating one relocation per DW_AT_ranges (all collapsed into a single relocation per CU for DW_AT_rnglists_base). Overall it would be a slight size increase in the linked file, as each reference would cost an extra byte or two total. Reducing the linker's effort (and what, 24 bytes in the .o per relocation?) seemed like the right tradeoff.

2109–2110

Maybe just "Emit address ranges into the .debug_ranges or .debug_rnglists section." Someday there will be a DWARF 6 and mostly likely it will use the same layout as v5.

2222

Replace all the commas with semicolons, then it reads correctly (the 'or' here is an exclusive or).

dblaikie added inline comments.Jul 16 2018, 12:42 PM
llvm/trunk/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
2092–2095

Right, this certainly saves relocations - but it's not the only way that could've saved relocations.

Rather than introducing FORM_rnglistx, it could've been specified that a DW_AT_ranges value with a data (not section-relative) form is considered as a byte offset relative to the DW_AT_ranges_base value. That way there would be no need for relocations, and no need for the extra indirection table I think... unless I'm missing something.

(@probinson - any ideas why this is the way it is, with the indirection table?)

2092–2095

Right - I get the desire to remove a reloc per range list reference. But I don't understand why that solution created the indirection table in debug_rnglist to go from range list indexes to range list offsets? What problem did that indirection solve compared to having range list offsets (relative to DW_AT_ranges_base or whatnot) directly in the DW_AT_ranges attributes?

2115–2123

Ah, right - good point.

Might still be nice to refactor to have this somewhat complicated expression in one place rather than two. Could skip the old-school pre-v5 ranges emission (even though it's a no-op) when the list is empty? Dunno - no big deal either way, just a thought.

probinson added inline comments.Jul 17 2018, 10:25 AM
llvm/trunk/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
2092–2095

OK I understand the question now.
If you want to encode the range list offset directly into DW_AT_ranges, then you must build the encoded range-list table incrementally so you know what offset to use for DW_AT_ranges. If instead you have an index in DW_AT_ranges, you can allocate a slot in the offset table immediately and build the encoded range-list table later, and fill in the offset table when you do that. This gives the producer more flexibility.

Now, if you as a producer are in fact building the encoded range-list table incrementally already, then your suggestion makes sense, and we can say that (for example) a constant form such as DW_FORM_data means the value is directly an offset from ranges_base. This saves relocations and also the offset table in .debug_rnglists. File it as a proposal on dwarfstd.org if it seems like a worthwhile benefit.

dblaikie added inline comments.Jul 17 2018, 10:32 AM
llvm/trunk/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
2092–2095

Thanks for explaining/your perspective there, Paul!

Yeah - for LLVM at least, we use assembler expressions computing label differences in many places to compute these sort of region-relative offsets, so it doesn't require incremental emission, as such... (sort of does, sort of doesn't - underlying assembly emission logic already has to handle these sort of temporarily unresolved expressions (use them, for example, for high_pc constants (end of function minus start of function), actually, maybe that's the only place we use it currently))

Once this is all implemented, perhaps we can see how much the indirection table takes up - but, yeah, I doubt it'll be a pressing issue/top of anyone's list. Just seemed like a strange inefficiency to me. I wonder if any implementation would actually need it/couldn't use the label difference approach (guess it could be a tradeoff even if they could use it - maybe it'd be slower or more memory intensive to use it, even if it did result in marginally smaller output)

probinson added inline comments.Jul 17 2018, 10:53 AM
llvm/trunk/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
2092–2095

Even after all this time I still don't intuitively think of compilers as effectively emitting assembler first, and that it can do useful stuff like that. Right, we can emit a difference expression to a uleb directive and it all works out.

It's quite possible we just had "index into table" on the brain when we were doing the new encoding for range/location lists, what with debug_str_offsets and debug_addr already in place. but the rationale I described above is what occurred to me, even if compilers can actually be smarter than that. :-)

probinson added inline comments.Jul 17 2018, 12:19 PM
llvm/trunk/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
2016–2021

I suspect this section doesn't make sense for DWARFv5 style range lists - there would never be a need to reset the base address to zero, because the forms themselves encode whether they are base address relative or not.

While I don't dispute any of this, is it okay if we defer this work until after location lists are in? We really want to have a syntactically conforming DWARF 5 before LLVM 7.0 branches, which is just a couple of weeks away. I think what Wolfgang has done meets that criterion (conforming) even if it isn't taking best advantage of the v5 possibilities.

dblaikie added inline comments.Jul 18 2018, 11:10 AM
llvm/trunk/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
2016–2021

Didn't seem like too much work/worth waiting - so I've fixed it (& improved the testing and assembly text (adding helpful comments)) in r337411