Skip to content

Commit 1a0f284

Browse files
[NFC] Extract DWARFCFIProgram into separate files (llvm#139326)
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 code for them into its own files, setting them up to be evaluated from outside debug frames themselves. 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.
1 parent 071e55b commit 1a0f284

File tree

5 files changed

+605
-538
lines changed

5 files changed

+605
-538
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//===- DWARFCFIProgram.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_DWARFCFIPROGRAM_H
10+
#define LLVM_DEBUGINFO_DWARF_DWARFCFIPROGRAM_H
11+
12+
#include "llvm/ADT/ArrayRef.h"
13+
#include "llvm/ADT/SmallString.h"
14+
#include "llvm/ADT/iterator.h"
15+
#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
16+
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
17+
#include "llvm/Support/Error.h"
18+
#include "llvm/TargetParser/Triple.h"
19+
#include <map>
20+
#include <memory>
21+
#include <vector>
22+
23+
namespace llvm {
24+
25+
namespace dwarf {
26+
/// Represent a sequence of Call Frame Information instructions that, when read
27+
/// in order, construct a table mapping PC to frame state. This can also be
28+
/// referred to as "CFI rules" in DWARF literature to avoid confusion with
29+
/// computer programs in the broader sense, and in this context each instruction
30+
/// would be a rule to establish the mapping. Refer to pg. 172 in the DWARF5
31+
/// manual, "6.4.1 Structure of Call Frame Information".
32+
class CFIProgram {
33+
public:
34+
static constexpr size_t MaxOperands = 3;
35+
typedef SmallVector<uint64_t, MaxOperands> Operands;
36+
37+
/// An instruction consists of a DWARF CFI opcode and an optional sequence of
38+
/// operands. If it refers to an expression, then this expression has its own
39+
/// sequence of operations and operands handled separately by DWARFExpression.
40+
struct Instruction {
41+
Instruction(uint8_t Opcode) : Opcode(Opcode) {}
42+
43+
uint8_t Opcode;
44+
Operands Ops;
45+
// Associated DWARF expression in case this instruction refers to one
46+
std::optional<DWARFExpression> Expression;
47+
48+
Expected<uint64_t> getOperandAsUnsigned(const CFIProgram &CFIP,
49+
uint32_t OperandIdx) const;
50+
51+
Expected<int64_t> getOperandAsSigned(const CFIProgram &CFIP,
52+
uint32_t OperandIdx) const;
53+
};
54+
55+
using InstrList = std::vector<Instruction>;
56+
using iterator = InstrList::iterator;
57+
using const_iterator = InstrList::const_iterator;
58+
59+
iterator begin() { return Instructions.begin(); }
60+
const_iterator begin() const { return Instructions.begin(); }
61+
iterator end() { return Instructions.end(); }
62+
const_iterator end() const { return Instructions.end(); }
63+
64+
unsigned size() const { return (unsigned)Instructions.size(); }
65+
bool empty() const { return Instructions.empty(); }
66+
uint64_t codeAlign() const { return CodeAlignmentFactor; }
67+
int64_t dataAlign() const { return DataAlignmentFactor; }
68+
Triple::ArchType triple() const { return Arch; }
69+
70+
CFIProgram(uint64_t CodeAlignmentFactor, int64_t DataAlignmentFactor,
71+
Triple::ArchType Arch)
72+
: CodeAlignmentFactor(CodeAlignmentFactor),
73+
DataAlignmentFactor(DataAlignmentFactor), Arch(Arch) {}
74+
75+
/// Parse and store a sequence of CFI instructions from Data,
76+
/// starting at *Offset and ending at EndOffset. *Offset is updated
77+
/// to EndOffset upon successful parsing, or indicates the offset
78+
/// where a problem occurred in case an error is returned.
79+
Error parse(DWARFDataExtractor Data, uint64_t *Offset, uint64_t EndOffset);
80+
81+
void dump(raw_ostream &OS, DIDumpOptions DumpOpts, unsigned IndentLevel,
82+
std::optional<uint64_t> InitialLocation) const;
83+
84+
void addInstruction(const Instruction &I) { Instructions.push_back(I); }
85+
86+
/// Get a DWARF CFI call frame string for the given DW_CFA opcode.
87+
StringRef callFrameString(unsigned Opcode) const;
88+
89+
private:
90+
std::vector<Instruction> Instructions;
91+
const uint64_t CodeAlignmentFactor;
92+
const int64_t DataAlignmentFactor;
93+
Triple::ArchType Arch;
94+
95+
/// Convenience method to add a new instruction with the given opcode.
96+
void addInstruction(uint8_t Opcode) {
97+
Instructions.push_back(Instruction(Opcode));
98+
}
99+
100+
/// Add a new single-operand instruction.
101+
void addInstruction(uint8_t Opcode, uint64_t Operand1) {
102+
Instructions.push_back(Instruction(Opcode));
103+
Instructions.back().Ops.push_back(Operand1);
104+
}
105+
106+
/// Add a new instruction that has two operands.
107+
void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2) {
108+
Instructions.push_back(Instruction(Opcode));
109+
Instructions.back().Ops.push_back(Operand1);
110+
Instructions.back().Ops.push_back(Operand2);
111+
}
112+
113+
/// Add a new instruction that has three operands.
114+
void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2,
115+
uint64_t Operand3) {
116+
Instructions.push_back(Instruction(Opcode));
117+
Instructions.back().Ops.push_back(Operand1);
118+
Instructions.back().Ops.push_back(Operand2);
119+
Instructions.back().Ops.push_back(Operand3);
120+
}
121+
122+
/// Types of operands to CFI instructions
123+
/// In DWARF, this type is implicitly tied to a CFI instruction opcode and
124+
/// thus this type doesn't need to be explicitly written to the file (this is
125+
/// not a DWARF encoding). The relationship of instrs to operand types can
126+
/// be obtained from getOperandTypes() and is only used to simplify
127+
/// instruction printing.
128+
enum OperandType {
129+
OT_Unset,
130+
OT_None,
131+
OT_Address,
132+
OT_Offset,
133+
OT_FactoredCodeOffset,
134+
OT_SignedFactDataOffset,
135+
OT_UnsignedFactDataOffset,
136+
OT_Register,
137+
OT_AddressSpace,
138+
OT_Expression
139+
};
140+
141+
/// Get the OperandType as a "const char *".
142+
static const char *operandTypeString(OperandType OT);
143+
144+
/// Retrieve the array describing the types of operands according to the enum
145+
/// above. This is indexed by opcode.
146+
static ArrayRef<OperandType[MaxOperands]> getOperandTypes();
147+
148+
/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand.
149+
void printOperand(raw_ostream &OS, DIDumpOptions DumpOpts,
150+
const Instruction &Instr, unsigned OperandIdx,
151+
uint64_t Operand, std::optional<uint64_t> &Address) const;
152+
};
153+
154+
} // end namespace dwarf
155+
156+
} // end namespace llvm
157+
158+
#endif // LLVM_DEBUGINFO_DWARF_DWARFCFIPROGRAM_H

llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h

Lines changed: 1 addition & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "llvm/ADT/ArrayRef.h"
1313
#include "llvm/ADT/SmallString.h"
1414
#include "llvm/ADT/iterator.h"
15+
#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
1516
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
1617
#include "llvm/Support/Error.h"
1718
#include "llvm/TargetParser/Triple.h"
@@ -309,7 +310,6 @@ class UnwindRow {
309310

310311
raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
311312

312-
class CFIProgram;
313313
class CIE;
314314
class FDE;
315315

@@ -398,135 +398,6 @@ class UnwindTable {
398398

399399
raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
400400

401-
/// Represent a sequence of Call Frame Information instructions that, when read
402-
/// in order, construct a table mapping PC to frame state. This can also be
403-
/// referred to as "CFI rules" in DWARF literature to avoid confusion with
404-
/// computer programs in the broader sense, and in this context each instruction
405-
/// would be a rule to establish the mapping. Refer to pg. 172 in the DWARF5
406-
/// manual, "6.4.1 Structure of Call Frame Information".
407-
class CFIProgram {
408-
public:
409-
static constexpr size_t MaxOperands = 3;
410-
typedef SmallVector<uint64_t, MaxOperands> Operands;
411-
412-
/// An instruction consists of a DWARF CFI opcode and an optional sequence of
413-
/// operands. If it refers to an expression, then this expression has its own
414-
/// sequence of operations and operands handled separately by DWARFExpression.
415-
struct Instruction {
416-
Instruction(uint8_t Opcode) : Opcode(Opcode) {}
417-
418-
uint8_t Opcode;
419-
Operands Ops;
420-
// Associated DWARF expression in case this instruction refers to one
421-
std::optional<DWARFExpression> Expression;
422-
423-
Expected<uint64_t> getOperandAsUnsigned(const CFIProgram &CFIP,
424-
uint32_t OperandIdx) const;
425-
426-
Expected<int64_t> getOperandAsSigned(const CFIProgram &CFIP,
427-
uint32_t OperandIdx) const;
428-
};
429-
430-
using InstrList = std::vector<Instruction>;
431-
using iterator = InstrList::iterator;
432-
using const_iterator = InstrList::const_iterator;
433-
434-
iterator begin() { return Instructions.begin(); }
435-
const_iterator begin() const { return Instructions.begin(); }
436-
iterator end() { return Instructions.end(); }
437-
const_iterator end() const { return Instructions.end(); }
438-
439-
unsigned size() const { return (unsigned)Instructions.size(); }
440-
bool empty() const { return Instructions.empty(); }
441-
uint64_t codeAlign() const { return CodeAlignmentFactor; }
442-
int64_t dataAlign() const { return DataAlignmentFactor; }
443-
Triple::ArchType triple() const { return Arch; }
444-
445-
CFIProgram(uint64_t CodeAlignmentFactor, int64_t DataAlignmentFactor,
446-
Triple::ArchType Arch)
447-
: CodeAlignmentFactor(CodeAlignmentFactor),
448-
DataAlignmentFactor(DataAlignmentFactor),
449-
Arch(Arch) {}
450-
451-
/// Parse and store a sequence of CFI instructions from Data,
452-
/// starting at *Offset and ending at EndOffset. *Offset is updated
453-
/// to EndOffset upon successful parsing, or indicates the offset
454-
/// where a problem occurred in case an error is returned.
455-
Error parse(DWARFDataExtractor Data, uint64_t *Offset, uint64_t EndOffset);
456-
457-
void dump(raw_ostream &OS, DIDumpOptions DumpOpts, unsigned IndentLevel,
458-
std::optional<uint64_t> InitialLocation) const;
459-
460-
void addInstruction(const Instruction &I) { Instructions.push_back(I); }
461-
462-
/// Get a DWARF CFI call frame string for the given DW_CFA opcode.
463-
StringRef callFrameString(unsigned Opcode) const;
464-
465-
private:
466-
std::vector<Instruction> Instructions;
467-
const uint64_t CodeAlignmentFactor;
468-
const int64_t DataAlignmentFactor;
469-
Triple::ArchType Arch;
470-
471-
/// Convenience method to add a new instruction with the given opcode.
472-
void addInstruction(uint8_t Opcode) {
473-
Instructions.push_back(Instruction(Opcode));
474-
}
475-
476-
/// Add a new single-operand instruction.
477-
void addInstruction(uint8_t Opcode, uint64_t Operand1) {
478-
Instructions.push_back(Instruction(Opcode));
479-
Instructions.back().Ops.push_back(Operand1);
480-
}
481-
482-
/// Add a new instruction that has two operands.
483-
void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2) {
484-
Instructions.push_back(Instruction(Opcode));
485-
Instructions.back().Ops.push_back(Operand1);
486-
Instructions.back().Ops.push_back(Operand2);
487-
}
488-
489-
/// Add a new instruction that has three operands.
490-
void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2,
491-
uint64_t Operand3) {
492-
Instructions.push_back(Instruction(Opcode));
493-
Instructions.back().Ops.push_back(Operand1);
494-
Instructions.back().Ops.push_back(Operand2);
495-
Instructions.back().Ops.push_back(Operand3);
496-
}
497-
498-
/// Types of operands to CFI instructions
499-
/// In DWARF, this type is implicitly tied to a CFI instruction opcode and
500-
/// thus this type doesn't need to be explicitly written to the file (this is
501-
/// not a DWARF encoding). The relationship of instrs to operand types can
502-
/// be obtained from getOperandTypes() and is only used to simplify
503-
/// instruction printing.
504-
enum OperandType {
505-
OT_Unset,
506-
OT_None,
507-
OT_Address,
508-
OT_Offset,
509-
OT_FactoredCodeOffset,
510-
OT_SignedFactDataOffset,
511-
OT_UnsignedFactDataOffset,
512-
OT_Register,
513-
OT_AddressSpace,
514-
OT_Expression
515-
};
516-
517-
/// Get the OperandType as a "const char *".
518-
static const char *operandTypeString(OperandType OT);
519-
520-
/// Retrieve the array describing the types of operands according to the enum
521-
/// above. This is indexed by opcode.
522-
static ArrayRef<OperandType[MaxOperands]> getOperandTypes();
523-
524-
/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand.
525-
void printOperand(raw_ostream &OS, DIDumpOptions DumpOpts,
526-
const Instruction &Instr, unsigned OperandIdx,
527-
uint64_t Operand, std::optional<uint64_t> &Address) const;
528-
};
529-
530401
/// An entry in either debug_frame or eh_frame. This entry can be a CIE or an
531402
/// FDE.
532403
class FrameEntry {

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+
DWARFCFIProgram.cpp
56
DWARFCompileUnit.cpp
67
DWARFContext.cpp
78
DWARFDataExtractor.cpp

0 commit comments

Comments
 (0)