DebugInfo: Add a DWARF FORM extension for addrx+offset references to reduce relocations

This is an alternative to the use of complex DWARF expressions for
addresses - shaving off a few extra bytes of expression overhead.
This commit is contained in:
David Blaikie 2021-01-27 18:09:31 -08:00
parent d6be277347
commit 4318028cd2
11 changed files with 137 additions and 28 deletions

View File

@ -494,6 +494,8 @@ HANDLE_DW_FORM(0x1f02, GNU_str_index, 0, GNU)
// Alternate debug sections proposal (output of "dwz" tool).
HANDLE_DW_FORM(0x1f20, GNU_ref_alt, 0, GNU)
HANDLE_DW_FORM(0x1f21, GNU_strp_alt, 0, GNU)
// LLVM addr+offset extension
HANDLE_DW_FORM(0x2001, LLVM_addrx_offset, 0, LLVM)
// DWARF Expression operators.
HANDLE_DW_OP(0x03, addr, 2, DWARF)

View File

@ -345,6 +345,22 @@ public:
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// A BaseTypeRef DIE.
class DIEAddrOffset {
DIEInteger Addr;
DIEDelta Offset;
public:
explicit DIEAddrOffset(uint64_t Idx, const MCSymbol *Hi, const MCSymbol *Lo)
: Addr(Idx), Offset(Hi, Lo) {}
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
unsigned SizeOf(const AsmPrinter *AP, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// A debug information entry value. Some of these roughly correlate
/// to DWARF attribute classes.
@ -368,9 +384,10 @@ private:
///
/// All values that aren't standard layout (or are larger than 8 bytes)
/// should be stored by reference instead of by value.
using ValTy = AlignedCharArrayUnion<DIEInteger, DIEString, DIEExpr, DIELabel,
DIEDelta *, DIEEntry, DIEBlock *,
DIELoc *, DIELocList, DIEBaseTypeRef *>;
using ValTy =
AlignedCharArrayUnion<DIEInteger, DIEString, DIEExpr, DIELabel,
DIEDelta *, DIEEntry, DIEBlock *, DIELoc *,
DIELocList, DIEBaseTypeRef *, DIEAddrOffset *>;
static_assert(sizeof(ValTy) <= sizeof(uint64_t) ||
sizeof(ValTy) <= sizeof(void *),

View File

@ -41,6 +41,7 @@ HANDLE_DIEVALUE_LARGE(Block)
HANDLE_DIEVALUE_LARGE(Loc)
HANDLE_DIEVALUE_SMALL(LocList)
HANDLE_DIEVALUE_LARGE(InlineString)
HANDLE_DIEVALUE_LARGE(AddrOffset)
#undef HANDLE_DIEVALUE
#undef HANDLE_DIEVALUE_SMALL

View File

@ -855,3 +855,27 @@ void DIELocList::emitValue(const AsmPrinter *AP, dwarf::Form Form) const {
LLVM_DUMP_METHOD
void DIELocList::print(raw_ostream &O) const { O << "LocList: " << Index; }
//===----------------------------------------------------------------------===//
// DIEAddrOffset Implementation
//===----------------------------------------------------------------------===//
unsigned DIEAddrOffset::SizeOf(const AsmPrinter *AP, dwarf::Form Form) const {
return Addr.SizeOf(AP, dwarf::DW_FORM_addrx) +
Offset.SizeOf(AP, dwarf::DW_FORM_data4);
}
/// EmitValue - Emit label value.
///
void DIEAddrOffset::emitValue(const AsmPrinter *AP, dwarf::Form Form) const {
Addr.emitValue(AP, dwarf::DW_FORM_addrx);
Offset.emitValue(AP, dwarf::DW_FORM_data4);
}
LLVM_DUMP_METHOD
void DIEAddrOffset::print(raw_ostream &O) const {
O << "AddrOffset: ";
Addr.print(O);
O << " + ";
Offset.print(O);
}

View File

@ -319,6 +319,7 @@ void DIEHash::hashAttribute(const DIEValue &Value, dwarf::Tag Tag) {
case DIEValue::isLabel:
case DIEValue::isBaseTypeRef:
case DIEValue::isDelta:
case DIEValue::isAddrOffset:
llvm_unreachable("Add support for additional value types.");
}
}

View File

@ -16,6 +16,7 @@
#include "llvm/ADT/None.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/CodeGen/MachineFunction.h"
@ -73,16 +74,20 @@ void DwarfCompileUnit::addLabelAddress(DIE &Die, dwarf::Attribute Attribute,
if (Label)
DD->addArangeLabel(SymbolCU(this, Label));
if (Label->isInSection() || !DD->useAddrOffsetExpressions()) {
const MCSymbol *Base = DD->getSectionLabel(&Label->getSection());
if (Base == Label || !DD->useAddrOffsetExpressions()) {
unsigned idx = DD->getAddressPool().getIndex(Label);
Die.addValue(DIEValueAllocator, Attribute,
DD->getDwarfVersion() >= 5 ? dwarf::DW_FORM_addrx
: dwarf::DW_FORM_GNU_addr_index,
DIEInteger(idx));
return;
}
bool UseAddrOffsetFormOrExpressions =
DD->useAddrOffsetForm() || DD->useAddrOffsetExpressions();
const MCSymbol *Base = nullptr;
if (Label->isInSection() && UseAddrOffsetFormOrExpressions)
Base = DD->getSectionLabel(&Label->getSection());
if (!Base || Base == Label) {
unsigned idx = DD->getAddressPool().getIndex(Label);
Die.addValue(DIEValueAllocator, Attribute,
DD->getDwarfVersion() >= 5 ? dwarf::DW_FORM_addrx
: dwarf::DW_FORM_GNU_addr_index,
DIEInteger(idx));
return;
}
// Could be extended to work with DWARFv4 Split DWARF if that's important for
@ -90,9 +95,14 @@ void DwarfCompileUnit::addLabelAddress(DIE &Die, dwarf::Attribute Attribute,
assert(DD->getDwarfVersion() >= 5 &&
"Addr+offset expressions are only valuable when using debug_addr (to "
"reduce relocations) available in DWARFv5 or higher");
auto *Loc = new (DIEValueAllocator) DIEBlock();
addPoolOpAddress(*Loc, Label);
addBlock(Die, Attribute, dwarf::DW_FORM_exprloc, Loc);
if (DD->useAddrOffsetExpressions()) {
auto *Loc = new (DIEValueAllocator) DIEBlock();
addPoolOpAddress(*Loc, Label);
addBlock(Die, Attribute, dwarf::DW_FORM_exprloc, Loc);
} else
Die.addValue(
DIEValueAllocator, Attribute, dwarf::DW_FORM_LLVM_addrx_offset,
new DIEAddrOffset(DD->getAddressPool().getIndex(Base), Label, Base));
}
void DwarfCompileUnit::addLocalLabelAddress(DIE &Die,

View File

@ -164,6 +164,9 @@ static cl::opt<DwarfDebug::MinimizeAddrInV5> MinimizeAddrInV5Option(
"Expressions",
"Use exprloc addrx+offset expressions for any "
"address with a prior base address"),
clEnumValN(DwarfDebug::MinimizeAddrInV5::Form, "Form",
"Use addrx+offset extension form for any address "
"with a prior base address"),
clEnumValN(DwarfDebug::MinimizeAddrInV5::Disabled, "Disabled",
"Stuff")),
cl::init(DwarfDebug::MinimizeAddrInV5::Default));

View File

@ -384,6 +384,7 @@ public:
Disabled,
Ranges,
Expressions,
Form,
};
private:
@ -713,6 +714,12 @@ public:
return MinimizeAddr == MinimizeAddrInV5::Expressions;
}
// Returns whether addrx+offset LLVM extension form should be used to reduce
// debug_addr size.
bool useAddrOffsetForm() const {
return MinimizeAddr == MinimizeAddrInV5::Form;
}
/// Returns whether to use sections as labels rather than temp symbols.
bool useSectionsAsReferences() const {
return UseSectionsAsReferences;

View File

@ -316,8 +316,11 @@ unsigned DwarfTypeUnit::getOrCreateSourceID(const DIFile *File) {
}
void DwarfUnit::addPoolOpAddress(DIEValueList &Die, const MCSymbol *Label) {
bool UseAddrOffsetFormOrExpressions =
DD->useAddrOffsetForm() || DD->useAddrOffsetExpressions();
const MCSymbol *Base = nullptr;
if (Label->isInSection() && DD->useAddrOffsetExpressions())
if (Label->isInSection() && UseAddrOffsetFormOrExpressions)
Base = DD->getSectionLabel(&Label->getSection());
uint32_t Index = DD->getAddressPool().getIndex(Base ? Base : Label);

View File

@ -74,7 +74,7 @@ static const DWARFFormValue::FormClass DWARF5FormClasses[] = {
DWARFFormValue::FC_Address, // 0x2a DW_FORM_addrx2
DWARFFormValue::FC_Address, // 0x2b DW_FORM_addrx3
DWARFFormValue::FC_Address, // 0x2c DW_FORM_addrx4
DWARFFormValue::FC_Address, // 0x2001 DW_FORM_addrx_offset
};
DWARFFormValue DWARFFormValue::createFromSValue(dwarf::Form F, int64_t V) {
@ -191,6 +191,11 @@ bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData,
DebugInfoData.getULEB128(OffsetPtr);
return true;
case DW_FORM_LLVM_addrx_offset:
DebugInfoData.getULEB128(OffsetPtr);
*OffsetPtr += 4;
return true;
case DW_FORM_indirect:
Indirect = true;
Form = static_cast<dwarf::Form>(DebugInfoData.getULEB128(OffsetPtr));
@ -217,6 +222,8 @@ bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const {
case DW_FORM_GNU_str_index:
case DW_FORM_GNU_strp_alt:
return (FC == FC_String);
case DW_FORM_LLVM_addrx_offset:
return (FC == FC_Address);
default:
break;
}
@ -322,6 +329,10 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data,
case DW_FORM_strx:
Value.uval = Data.getULEB128(OffsetPtr, &Err);
break;
case DW_FORM_LLVM_addrx_offset:
Value.uval = Data.getULEB128(OffsetPtr, &Err) << 32;
Value.uval = Data.getU32(OffsetPtr, &Err);
break;
case DW_FORM_string:
Value.cstr = Data.getCStr(OffsetPtr, &Err);
break;
@ -417,6 +428,23 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
OS << "<unresolved>";
break;
}
case DW_FORM_LLVM_addrx_offset: {
if (U == nullptr) {
OS << "<invalid dwarf unit>";
break;
}
uint32_t Index = UValue >> 32;
uint32_t Offset = UValue & 0xffffffff;
Optional<object::SectionedAddress> A = U->getAddrOffsetSectionItem(Index);
if (!A || DumpOpts.Verbose)
AddrOS << format("indexed (%8.8x) + 0x%x address = ", Index, Offset);
if (A) {
A->Address += Offset;
dumpSectionedAddress(AddrOS, DumpOpts, *A);
} else
OS << "<unresolved>";
break;
}
case DW_FORM_flag_present:
OS << "true";
break;
@ -636,13 +664,17 @@ Optional<object::SectionedAddress>
DWARFFormValue::getAsSectionedAddress() const {
if (!isFormClass(FC_Address))
return None;
if (Form == DW_FORM_GNU_addr_index || Form == DW_FORM_addrx) {
uint32_t Index = Value.uval;
bool AddrOffset = Form == dwarf::DW_FORM_LLVM_addrx_offset;
if (Form == DW_FORM_GNU_addr_index || Form == DW_FORM_addrx || AddrOffset) {
uint32_t Index = AddrOffset ? (Value.uval >> 32) : Value.uval;
if (!U)
return None;
Optional<object::SectionedAddress> SA = U->getAddrOffsetSectionItem(Index);
if (!SA)
return None;
if (AddrOffset)
SA->Address += (Value.uval & 0xffffffff);
return SA;
}
return {{Value.uval, Value.SectionIndex}};

View File

@ -3,12 +3,17 @@
; RUN: | FileCheck --check-prefix=CHECK --check-prefix=RNG \
; RUN: --implicit-check-not=DW_TAG --implicit-check-not=NULL --implicit-check-not=_pc %s
; RUN: llc -O0 %s -mtriple=x86_64-unknown-linux-gnu -filetype=obj -o - -minimize-addr-in-v5=Expressions \
; RUN: | llvm-dwarfdump -debug-info -debug-addr -debug-rnglists -v - \
; RUN: | FileCheck --check-prefix=CHECK --check-prefix=EXPR \
; RUN: | FileCheck --check-prefix=CHECK --check-prefix=EXPRORFORM --check-prefix=EXPR\
; RUN: --implicit-check-not=DW_TAG --implicit-check-not=NULL --implicit-check-not=_pc %s
; RUN: llc -O0 %s -mtriple=x86_64-unknown-linux-gnu -filetype=obj -o - -minimize-addr-in-v5=Form \
; RUN: | llvm-dwarfdump -debug-info -debug-addr -debug-rnglists -v - \
; RUN: | FileCheck --check-prefix=CHECK --check-prefix=EXPRORFORM --check-prefix=FORM \
; RUN: --implicit-check-not=DW_TAG --implicit-check-not=NULL --implicit-check-not=_pc %s
; Generated from the following source. f4 is used to put a hole in the CU
; ranges while keeping f2 and f4 in the same section (as opposed to
; -ffunction-sections, which would produce CU ranges, but each function would
@ -44,7 +49,7 @@
; CHECK: DW_TAG_compile_unit
; CHECK: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
; RNG: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x2) rangelist = [[CU_RANGE:.*]]
; EXPR: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = [[CU_RANGE:.*]]
; EXPRORFORM: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = [[CU_RANGE:.*]]
; CHECK: DW_TAG_subprogram
; CHECK: DW_AT_name {{.*}} "f2"
; CHECK: DW_TAG_subprogram
@ -52,25 +57,29 @@
; CHECK: DW_AT_high_pc [DW_FORM_data4] (0x00000010)
; CHECK: DW_TAG_inlined_subroutine
; EXPR: DW_AT_low_pc [DW_FORM_exprloc] (DW_OP_addrx 0x0, DW_OP_const4u 0x9, DW_OP_plus)
; EXPR: DW_AT_high_pc [DW_FORM_data4] (0x00000005)
; FORM: DW_AT_low_pc [DW_FORM_LLVM_addrx_offset] (indexed (00000000) + 0x9 address = 0x0000000000000009 ".text")
; EXPRORFORM: DW_AT_high_pc [DW_FORM_data4] (0x00000005)
; RNG: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = [[INL_RANGE:.*]]
; CHECK: DW_TAG_call_site
; RNG: DW_AT_call_return_pc [DW_FORM_addrx] (indexed (00000001) address = 0x0000000000000009 ".text")
; EXPR: DW_AT_call_return_pc [DW_FORM_exprloc] (DW_OP_addrx 0x0, DW_OP_const4u 0x9, DW_OP_plus)
; FORM: DW_AT_call_return_pc [DW_FORM_LLVM_addrx_offset] (indexed (00000000) + 0x9 address = 0x0000000000000009 ".text")
; CHECK: DW_TAG_call_site
; RNG: DW_AT_call_return_pc [DW_FORM_addrx] (indexed (00000002) address = 0x000000000000000e ".text")
; EXPR: DW_AT_call_return_pc [DW_FORM_exprloc] (DW_OP_addrx 0x0, DW_OP_const4u 0xe, DW_OP_plus)
; FORM: DW_AT_call_return_pc [DW_FORM_LLVM_addrx_offset] (indexed (00000000) + 0xe address = 0x000000000000000e ".text")
; CHECK: NULL
; CHECK: DW_TAG_subprogram
; CHECK: DW_AT_name {{.*}} "f1"
; CHECK: DW_TAG_subprogram
; EXPR: DW_AT_low_pc [DW_FORM_exprloc] (DW_OP_addrx 0x0, DW_OP_const4u 0x20, DW_OP_plus)
; EXPR: DW_AT_high_pc [DW_FORM_data4] (0x00000006)
; FORM: DW_AT_low_pc [DW_FORM_LLVM_addrx_offset] (indexed (00000000) + 0x20 address = 0x0000000000000020 ".text")
; EXPRORFORM: DW_AT_high_pc [DW_FORM_data4] (0x00000006)
; RNG: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = [[F5_RANGE:.*]]
; CHECK: DW_TAG_subprogram
; CHECK: DW_AT_low_pc [DW_FORM_addrx] (indexed (
; RNG-SAME: 00000003
; EXPR-SAME: 00000001
; EXPRORFORM-SAME: 00000001
; CHECK: ) address = 0x0000000000000000 ".other")
; CHECK: DW_AT_high_pc [DW_FORM_data4] (0x00000006)
; CHECK: NULL
@ -86,7 +95,7 @@
; CHECK-LABEL: .debug_rnglists contents:
; RNG: 0x00000000: range list header: {{.*}}, offset_entry_count = 0x00000003
; EXPR: 0x00000000: range list header: {{.*}}, offset_entry_count = 0x00000001
; EXPRORFORM: 0x00000000: range list header: {{.*}}, offset_entry_count = 0x00000001
; CHECK: ranges:
; RNG-NEXT: [[INL_RANGE]]: [DW_RLE_base_addressx]: 0x0000000000000000
; RNG-NEXT: [DW_RLE_offset_pair ]
@ -100,7 +109,7 @@
; CHECK-NEXT: [DW_RLE_offset_pair ]
; CHECK-NEXT: [DW_RLE_offset_pair ]
; RNG-NEXT: [DW_RLE_startx_length]: 0x0000000000000003
; EXPR-NEXT: [DW_RLE_startx_length]: 0x0000000000000001
; EXPRORFORM-NEXT: [DW_RLE_startx_length]: 0x0000000000000001
; CHECK-NEXT: [DW_RLE_end_of_list ]
; Function Attrs: noinline optnone uwtable mustprogress