Skip to content

Commit 628274d

Browse files
[NFC] Extract Printing portions of DWARFCFIProgram to new files (#143762)
CFIPrograms' most common uses are within debug frames, but it is not their only use. For example, some assembly writers encode them by hand into .cfi_escape directives. This PR extracts printing code for them into its own files, which avoids the need for the main class to depend on DWARFUnit, sections, and similar. One in a series of NFC DebugInfo/DWARF refactoring changes to layer it more cleanly, so that binary CFI parsing can be used from low-level code, (such as byte strings created via .cfi_escape) without circular dependencies. The final goal is to make a more limited dwarf library usable from lower-level code. More information can be found at https://discourse.llvm.org/t/rfc-debuginfo-dwarf-refactor-into-to-lower-and-higher-level-libraries/86665
1 parent a981134 commit 628274d

File tree

7 files changed

+184
-133
lines changed

7 files changed

+184
-133
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===- DWARFCFIPrinter.h ----------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_DEBUGINFO_DWARF_DWARFCFIPRINTER_H
10+
#define LLVM_DEBUGINFO_DWARF_DWARFCFIPRINTER_H
11+
12+
#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
13+
14+
namespace llvm {
15+
16+
struct DIDumpOptions;
17+
18+
namespace dwarf {
19+
20+
void printCFIProgram(const CFIProgram &P, raw_ostream &OS,
21+
const DIDumpOptions &DumpOpts, unsigned IndentLevel,
22+
std::optional<uint64_t> Address);
23+
24+
} // end namespace dwarf
25+
26+
} // end namespace llvm
27+
28+
#endif // LLVM_DEBUGINFO_DWARF_DWARFCFIPRINTER_H

llvm/include/llvm/DebugInfo/DWARF/DWARFCFIProgram.h

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
namespace llvm {
2525

2626
namespace dwarf {
27+
2728
/// Represent a sequence of Call Frame Information instructions that, when read
2829
/// in order, construct a table mapping PC to frame state. This can also be
2930
/// referred to as "CFI rules" in DWARF literature to avoid confusion with
@@ -80,15 +81,37 @@ class CFIProgram {
8081
LLVM_ABI Error parse(DWARFDataExtractor Data, uint64_t *Offset,
8182
uint64_t EndOffset);
8283

83-
LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
84-
unsigned IndentLevel,
85-
std::optional<uint64_t> InitialLocation) const;
86-
8784
void addInstruction(const Instruction &I) { Instructions.push_back(I); }
8885

8986
/// Get a DWARF CFI call frame string for the given DW_CFA opcode.
9087
LLVM_ABI StringRef callFrameString(unsigned Opcode) const;
9188

89+
/// Types of operands to CFI instructions
90+
/// In DWARF, this type is implicitly tied to a CFI instruction opcode and
91+
/// thus this type doesn't need to be explicitly written to the file (this is
92+
/// not a DWARF encoding). The relationship of instrs to operand types can
93+
/// be obtained from getOperandTypes() and is only used to simplify
94+
/// instruction printing and error messages.
95+
enum OperandType {
96+
OT_Unset,
97+
OT_None,
98+
OT_Address,
99+
OT_Offset,
100+
OT_FactoredCodeOffset,
101+
OT_SignedFactDataOffset,
102+
OT_UnsignedFactDataOffset,
103+
OT_Register,
104+
OT_AddressSpace,
105+
OT_Expression
106+
};
107+
108+
/// Get the OperandType as a "const char *".
109+
static const char *operandTypeString(OperandType OT);
110+
111+
/// Retrieve the array describing the types of operands according to the enum
112+
/// above. This is indexed by opcode.
113+
static ArrayRef<OperandType[MaxOperands]> getOperandTypes();
114+
92115
private:
93116
std::vector<Instruction> Instructions;
94117
const uint64_t CodeAlignmentFactor;
@@ -121,37 +144,6 @@ class CFIProgram {
121144
Instructions.back().Ops.push_back(Operand2);
122145
Instructions.back().Ops.push_back(Operand3);
123146
}
124-
125-
/// Types of operands to CFI instructions
126-
/// In DWARF, this type is implicitly tied to a CFI instruction opcode and
127-
/// thus this type doesn't need to be explicitly written to the file (this is
128-
/// not a DWARF encoding). The relationship of instrs to operand types can
129-
/// be obtained from getOperandTypes() and is only used to simplify
130-
/// instruction printing.
131-
enum OperandType {
132-
OT_Unset,
133-
OT_None,
134-
OT_Address,
135-
OT_Offset,
136-
OT_FactoredCodeOffset,
137-
OT_SignedFactDataOffset,
138-
OT_UnsignedFactDataOffset,
139-
OT_Register,
140-
OT_AddressSpace,
141-
OT_Expression
142-
};
143-
144-
/// Get the OperandType as a "const char *".
145-
static const char *operandTypeString(OperandType OT);
146-
147-
/// Retrieve the array describing the types of operands according to the enum
148-
/// above. This is indexed by opcode.
149-
static ArrayRef<OperandType[MaxOperands]> getOperandTypes();
150-
151-
/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand.
152-
void printOperand(raw_ostream &OS, DIDumpOptions DumpOpts,
153-
const Instruction &Instr, unsigned OperandIdx,
154-
uint64_t Operand, std::optional<uint64_t> &Address) const;
155147
};
156148

157149
} // end namespace dwarf

llvm/lib/DebugInfo/DWARF/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ add_llvm_component_library(LLVMDebugInfoDWARF
22
DWARFAbbreviationDeclaration.cpp
33
DWARFAddressRange.cpp
44
DWARFAcceleratorTable.cpp
5+
DWARFCFIPrinter.cpp
56
DWARFCFIProgram.cpp
67
DWARFCompileUnit.cpp
78
DWARFContext.cpp
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//===- DWARFCFIPrinter.cpp - Print the cfi-portions of .debug_frame -------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h"
10+
#include "llvm/DebugInfo/DIContext.h"
11+
#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
12+
#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
13+
#include "llvm/Support/Compiler.h"
14+
#include "llvm/Support/DataExtractor.h"
15+
#include "llvm/Support/Errc.h"
16+
#include "llvm/Support/ErrorHandling.h"
17+
#include "llvm/Support/Format.h"
18+
#include "llvm/Support/raw_ostream.h"
19+
#include <cassert>
20+
#include <cinttypes>
21+
#include <cstdint>
22+
#include <optional>
23+
24+
using namespace llvm;
25+
using namespace dwarf;
26+
27+
static void printRegister(raw_ostream &OS, const DIDumpOptions &DumpOpts,
28+
unsigned RegNum) {
29+
if (DumpOpts.GetNameForDWARFReg) {
30+
auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH);
31+
if (!RegName.empty()) {
32+
OS << RegName;
33+
return;
34+
}
35+
}
36+
OS << "reg" << RegNum;
37+
}
38+
39+
/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand.
40+
static void printOperand(raw_ostream &OS, const DIDumpOptions &DumpOpts,
41+
const CFIProgram &P,
42+
const CFIProgram::Instruction &Instr,
43+
unsigned OperandIdx, uint64_t Operand,
44+
std::optional<uint64_t> &Address) {
45+
assert(OperandIdx < CFIProgram::MaxOperands);
46+
uint8_t Opcode = Instr.Opcode;
47+
CFIProgram::OperandType Type = P.getOperandTypes()[Opcode][OperandIdx];
48+
49+
switch (Type) {
50+
case CFIProgram::OT_Unset: {
51+
OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to";
52+
auto OpcodeName = P.callFrameString(Opcode);
53+
if (!OpcodeName.empty())
54+
OS << " " << OpcodeName;
55+
else
56+
OS << format(" Opcode %x", Opcode);
57+
break;
58+
}
59+
case CFIProgram::OT_None:
60+
break;
61+
case CFIProgram::OT_Address:
62+
OS << format(" %" PRIx64, Operand);
63+
Address = Operand;
64+
break;
65+
case CFIProgram::OT_Offset:
66+
// The offsets are all encoded in a unsigned form, but in practice
67+
// consumers use them signed. It's most certainly legacy due to
68+
// the lack of signed variants in the first Dwarf standards.
69+
OS << format(" %+" PRId64, int64_t(Operand));
70+
break;
71+
case CFIProgram::OT_FactoredCodeOffset: // Always Unsigned
72+
if (P.codeAlign())
73+
OS << format(" %" PRId64, Operand * P.codeAlign());
74+
else
75+
OS << format(" %" PRId64 "*code_alignment_factor", Operand);
76+
if (Address && P.codeAlign()) {
77+
*Address += Operand * P.codeAlign();
78+
OS << format(" to 0x%" PRIx64, *Address);
79+
}
80+
break;
81+
case CFIProgram::OT_SignedFactDataOffset:
82+
if (P.dataAlign())
83+
OS << format(" %" PRId64, int64_t(Operand) * P.dataAlign());
84+
else
85+
OS << format(" %" PRId64 "*data_alignment_factor", int64_t(Operand));
86+
break;
87+
case CFIProgram::OT_UnsignedFactDataOffset:
88+
if (P.dataAlign())
89+
OS << format(" %" PRId64, Operand * P.dataAlign());
90+
else
91+
OS << format(" %" PRId64 "*data_alignment_factor", Operand);
92+
break;
93+
case CFIProgram::OT_Register:
94+
OS << ' ';
95+
printRegister(OS, DumpOpts, Operand);
96+
break;
97+
case CFIProgram::OT_AddressSpace:
98+
OS << format(" in addrspace%" PRId64, Operand);
99+
break;
100+
case CFIProgram::OT_Expression:
101+
assert(Instr.Expression && "missing DWARFExpression object");
102+
OS << " ";
103+
DWARFExpressionPrinter::print(&Instr.Expression.value(), OS, DumpOpts,
104+
nullptr);
105+
break;
106+
}
107+
}
108+
109+
void llvm::dwarf::printCFIProgram(const CFIProgram &P, raw_ostream &OS,
110+
const DIDumpOptions &DumpOpts,
111+
unsigned IndentLevel,
112+
std::optional<uint64_t> Address) {
113+
for (const auto &Instr : P) {
114+
uint8_t Opcode = Instr.Opcode;
115+
OS.indent(2 * IndentLevel);
116+
OS << P.callFrameString(Opcode) << ":";
117+
for (size_t i = 0; i < Instr.Ops.size(); ++i)
118+
printOperand(OS, DumpOpts, P, Instr, i, Instr.Ops[i], Address);
119+
OS << '\n';
120+
}
121+
}

llvm/lib/DebugInfo/DWARF/DWARFCFIProgram.cpp

Lines changed: 0 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,6 @@
2323
using namespace llvm;
2424
using namespace dwarf;
2525

26-
static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts,
27-
unsigned RegNum) {
28-
if (DumpOpts.GetNameForDWARFReg) {
29-
auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH);
30-
if (!RegName.empty()) {
31-
OS << RegName;
32-
return;
33-
}
34-
}
35-
OS << "reg" << RegNum;
36-
}
37-
3826
// See DWARF standard v3, section 7.23
3927
const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0;
4028
const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f;
@@ -361,85 +349,3 @@ CFIProgram::getOperandTypes() {
361349

362350
return ArrayRef<OperandType[MaxOperands]>(&OpTypes[0], DW_CFA_restore + 1);
363351
}
364-
365-
/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand.
366-
void CFIProgram::printOperand(raw_ostream &OS, DIDumpOptions DumpOpts,
367-
const Instruction &Instr, unsigned OperandIdx,
368-
uint64_t Operand,
369-
std::optional<uint64_t> &Address) const {
370-
assert(OperandIdx < MaxOperands);
371-
uint8_t Opcode = Instr.Opcode;
372-
OperandType Type = getOperandTypes()[Opcode][OperandIdx];
373-
374-
switch (Type) {
375-
case OT_Unset: {
376-
OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to";
377-
auto OpcodeName = callFrameString(Opcode);
378-
if (!OpcodeName.empty())
379-
OS << " " << OpcodeName;
380-
else
381-
OS << format(" Opcode %x", Opcode);
382-
break;
383-
}
384-
case OT_None:
385-
break;
386-
case OT_Address:
387-
OS << format(" %" PRIx64, Operand);
388-
Address = Operand;
389-
break;
390-
case OT_Offset:
391-
// The offsets are all encoded in a unsigned form, but in practice
392-
// consumers use them signed. It's most certainly legacy due to
393-
// the lack of signed variants in the first Dwarf standards.
394-
OS << format(" %+" PRId64, int64_t(Operand));
395-
break;
396-
case OT_FactoredCodeOffset: // Always Unsigned
397-
if (CodeAlignmentFactor)
398-
OS << format(" %" PRId64, Operand * CodeAlignmentFactor);
399-
else
400-
OS << format(" %" PRId64 "*code_alignment_factor", Operand);
401-
if (Address && CodeAlignmentFactor) {
402-
*Address += Operand * CodeAlignmentFactor;
403-
OS << format(" to 0x%" PRIx64, *Address);
404-
}
405-
break;
406-
case OT_SignedFactDataOffset:
407-
if (DataAlignmentFactor)
408-
OS << format(" %" PRId64, int64_t(Operand) * DataAlignmentFactor);
409-
else
410-
OS << format(" %" PRId64 "*data_alignment_factor", int64_t(Operand));
411-
break;
412-
case OT_UnsignedFactDataOffset:
413-
if (DataAlignmentFactor)
414-
OS << format(" %" PRId64, Operand * DataAlignmentFactor);
415-
else
416-
OS << format(" %" PRId64 "*data_alignment_factor", Operand);
417-
break;
418-
case OT_Register:
419-
OS << ' ';
420-
printRegister(OS, DumpOpts, Operand);
421-
break;
422-
case OT_AddressSpace:
423-
OS << format(" in addrspace%" PRId64, Operand);
424-
break;
425-
case OT_Expression:
426-
assert(Instr.Expression && "missing DWARFExpression object");
427-
OS << " ";
428-
DWARFExpressionPrinter::print(&Instr.Expression.value(), OS, DumpOpts,
429-
nullptr);
430-
break;
431-
}
432-
}
433-
434-
void CFIProgram::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
435-
unsigned IndentLevel,
436-
std::optional<uint64_t> Address) const {
437-
for (const auto &Instr : Instructions) {
438-
uint8_t Opcode = Instr.Opcode;
439-
OS.indent(2 * IndentLevel);
440-
OS << callFrameString(Opcode) << ":";
441-
for (unsigned i = 0; i < Instr.Ops.size(); ++i)
442-
printOperand(OS, DumpOpts, Instr, i, Instr.Ops[i], Address);
443-
OS << '\n';
444-
}
445-
}

llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "llvm/ADT/StringRef.h"
1313
#include "llvm/BinaryFormat/Dwarf.h"
1414
#include "llvm/DebugInfo/DIContext.h"
15+
#include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h"
1516
#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
1617
#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
1718
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
@@ -602,7 +603,8 @@ void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
602603
OS << "\n";
603604
}
604605
OS << "\n";
605-
CFIs.dump(OS, DumpOpts, /*IndentLevel=*/1, /*InitialLocation=*/{});
606+
printCFIProgram(CFIs, OS, DumpOpts, /*IndentLevel=*/1,
607+
/*InitialLocation=*/{});
606608
OS << "\n";
607609

608610
if (Expected<UnwindTable> RowsOrErr = UnwindTable::create(this))
@@ -630,7 +632,7 @@ void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
630632
OS << " Format: " << FormatString(IsDWARF64) << "\n";
631633
if (LSDAAddress)
632634
OS << format(" LSDA Address: %016" PRIx64 "\n", *LSDAAddress);
633-
CFIs.dump(OS, DumpOpts, /*IndentLevel=*/1, InitialLocation);
635+
printCFIProgram(CFIs, OS, DumpOpts, /*IndentLevel=*/1, InitialLocation);
634636
OS << "\n";
635637

636638
if (Expected<UnwindTable> RowsOrErr = UnwindTable::create(this))

llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "llvm-readobj.h"
1313
#include "llvm/ADT/STLExtras.h"
1414
#include "llvm/BinaryFormat/Dwarf.h"
15+
#include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h"
1516
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
1617
#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
1718
#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"
@@ -228,8 +229,8 @@ void PrinterContext<ELFT>::printEHFrame(const Elf_Shdr *EHFrameShdr) const {
228229
W.indent();
229230
auto DumpOpts = DIDumpOptions();
230231
DumpOpts.IsEH = true;
231-
Entry.cfis().dump(W.getOStream(), DumpOpts, W.getIndentLevel(),
232-
InitialLocation);
232+
printCFIProgram(Entry.cfis(), W.getOStream(), DumpOpts, W.getIndentLevel(),
233+
InitialLocation);
233234
W.unindent();
234235
W.unindent();
235236
W.getOStream() << "\n";

0 commit comments

Comments
 (0)