[llvm] [NFC] Separate UnwindTable from DebugFrame into a different type (PR #142521)
AmirHossein PashaeeHir via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 17 14:33:57 PDT 2025
https://github.com/amsen20 updated https://github.com/llvm/llvm-project/pull/142521
>From 055492de6590cfad05704997de59dd4429bc0a32 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Mon, 2 Jun 2025 22:31:07 +0000
Subject: [PATCH 1/6] Remove UnwindTable dependency on CIE, and FDE
(transitively on llvm/Object)
For creating new UnwindTable, two static methods was implemented inside it, to create an instance of it from a CIE or FDE.
This static methods are moved out of the class as a library functions.
---
.../llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 65 ++++++++++---------
llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp | 14 ++--
.../DebugInfo/DWARF/DWARFDebugFrameTest.cpp | 56 ++++++++--------
3 files changed, 71 insertions(+), 64 deletions(-)
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index 051ea6e11e351..d48d3b628a9c8 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -311,9 +311,6 @@ class UnwindRow {
LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
-class CIE;
-class FDE;
-
/// A class that contains all UnwindRow objects for an FDE or a single unwind
/// row for a CIE. To unwind an address the rows, which are sorted by start
/// address, can be searched to find the UnwindRow with the lowest starting
@@ -334,6 +331,12 @@ class UnwindTable {
assert(Index < size());
return Rows[Index];
}
+ void insertRow(const UnwindRow &Row) { Rows.push_back(Row); }
+
+ /// Set the last address that this unwinding table refers to.
+ ///
+ /// This is used when this table is created based on a FDE.
+ void setEndAddress(uint64_t Addr) { EndAddress = Addr; }
/// Dump the UnwindTable to the stream.
///
@@ -352,32 +355,6 @@ class UnwindTable {
LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
unsigned IndentLevel = 0) const;
- /// Create an UnwindTable from a Common Information Entry (CIE).
- ///
- /// \param Cie The Common Information Entry to extract the table from. The
- /// CFIProgram is retrieved from the \a Cie object and used to create the
- /// UnwindTable.
- ///
- /// \returns An error if the DWARF Call Frame Information opcodes have state
- /// machine errors, or a valid UnwindTable otherwise.
- LLVM_ABI static Expected<UnwindTable> create(const CIE *Cie);
-
- /// Create an UnwindTable from a Frame Descriptor Entry (FDE).
- ///
- /// \param Fde The Frame Descriptor Entry to extract the table from. The
- /// CFIProgram is retrieved from the \a Fde object and used to create the
- /// UnwindTable.
- ///
- /// \returns An error if the DWARF Call Frame Information opcodes have state
- /// machine errors, or a valid UnwindTable otherwise.
- LLVM_ABI static Expected<UnwindTable> create(const FDE *Fde);
-
-private:
- RowContainer Rows;
- /// The end address when data is extracted from a FDE. This value will be
- /// invalid when a UnwindTable is extracted from a CIE.
- std::optional<uint64_t> EndAddress;
-
/// Parse the information in the CFIProgram and update the CurrRow object
/// that the state machine describes.
///
@@ -395,10 +372,40 @@ class UnwindTable {
/// DW_CFA_restore and DW_CFA_restore_extended opcodes.
Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
const RegisterLocations *InitialLocs);
+
+private:
+ RowContainer Rows;
+ /// The end address when data is extracted from a FDE. This value will be
+ /// invalid when a UnwindTable is extracted from a CIE.
+ std::optional<uint64_t> EndAddress;
};
LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
+class CIE;
+
+/// Create an UnwindTable from a Common Information Entry (CIE).
+///
+/// \param Cie The Common Information Entry to extract the table from. The
+/// CFIProgram is retrieved from the \a Cie object and used to create the
+/// UnwindTable.
+///
+/// \returns An error if the DWARF Call Frame Information opcodes have state
+/// machine errors, or a valid UnwindTable otherwise.
+Expected<UnwindTable> createUnwindTable(const CIE *Cie);
+
+class FDE;
+
+/// Create an UnwindTable from a Frame Descriptor Entry (FDE).
+///
+/// \param Fde The Frame Descriptor Entry to extract the table from. The
+/// CFIProgram is retrieved from the \a Fde object and used to create the
+/// UnwindTable.
+///
+/// \returns An error if the DWARF Call Frame Information opcodes have state
+/// machine errors, or a valid UnwindTable otherwise.
+Expected<UnwindTable> createUnwindTable(const FDE *Fde);
+
/// An entry in either debug_frame or eh_frame. This entry can be a CIE or an
/// FDE.
class FrameEntry {
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
index c46b14b4446f7..cf42151bb84aa 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
@@ -203,7 +203,7 @@ raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) {
return OS;
}
-Expected<UnwindTable> UnwindTable::create(const FDE *Fde) {
+Expected<UnwindTable> llvm::dwarf::createUnwindTable(const FDE *Fde) {
const CIE *Cie = Fde->getLinkedCIE();
if (Cie == nullptr)
return createStringError(errc::invalid_argument,
@@ -217,7 +217,7 @@ Expected<UnwindTable> UnwindTable::create(const FDE *Fde) {
UnwindTable UT;
UnwindRow Row;
Row.setAddress(Fde->getInitialLocation());
- UT.EndAddress = Fde->getInitialLocation() + Fde->getAddressRange();
+ UT.setEndAddress(Fde->getInitialLocation() + Fde->getAddressRange());
if (Error CieError = UT.parseRows(Cie->cfis(), Row, nullptr))
return std::move(CieError);
// We need to save the initial locations of registers from the CIE parsing
@@ -229,11 +229,11 @@ Expected<UnwindTable> UnwindTable::create(const FDE *Fde) {
// Do not add that to the unwind table.
if (Row.getRegisterLocations().hasLocations() ||
Row.getCFAValue().getLocation() != UnwindLocation::Unspecified)
- UT.Rows.push_back(Row);
+ UT.insertRow(Row);
return UT;
}
-Expected<UnwindTable> UnwindTable::create(const CIE *Cie) {
+Expected<UnwindTable> llvm::dwarf::createUnwindTable(const CIE *Cie) {
// Rows will be empty if there are no CFI instructions.
if (Cie->cfis().empty())
return UnwindTable();
@@ -246,7 +246,7 @@ Expected<UnwindTable> UnwindTable::create(const CIE *Cie) {
// Do not add that to the unwind table.
if (Row.getRegisterLocations().hasLocations() ||
Row.getCFAValue().getLocation() != UnwindLocation::Unspecified)
- UT.Rows.push_back(Row);
+ UT.insertRow(Row);
return UT;
}
@@ -605,7 +605,7 @@ void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
CFIs.dump(OS, DumpOpts, /*IndentLevel=*/1, /*InitialLocation=*/{});
OS << "\n";
- if (Expected<UnwindTable> RowsOrErr = UnwindTable::create(this))
+ if (Expected<UnwindTable> RowsOrErr = createUnwindTable(this))
RowsOrErr->dump(OS, DumpOpts, 1);
else {
DumpOpts.RecoverableErrorHandler(joinErrors(
@@ -633,7 +633,7 @@ void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
CFIs.dump(OS, DumpOpts, /*IndentLevel=*/1, InitialLocation);
OS << "\n";
- if (Expected<UnwindTable> RowsOrErr = UnwindTable::create(this))
+ if (Expected<UnwindTable> RowsOrErr = createUnwindTable(this))
RowsOrErr->dump(OS, DumpOpts, 1);
else {
DumpOpts.RecoverableErrorHandler(joinErrors(
diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
index 2be656547c92e..2f91b1f86f6dd 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
@@ -465,9 +465,9 @@ TEST(DWARFDebugFrame, UnwindTableEmptyRows) {
EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());
EXPECT_TRUE(TestCIE.cfis().empty());
- // Verify dwarf::UnwindTable::create() won't result in errors and
+ // Verify dwarf::createUnwindTable() won't result in errors and
// and empty rows are not added to CIE UnwindTable.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestCIE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestCIE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const size_t ExpectedNumOfRows = 0;
EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows);
@@ -486,9 +486,9 @@ TEST(DWARFDebugFrame, UnwindTableEmptyRows) {
EXPECT_THAT_ERROR(parseCFI(TestFDE, {}), Succeeded());
EXPECT_TRUE(TestFDE.cfis().empty());
- // Verify dwarf::UnwindTable::create() won't result in errors and
+ // Verify dwarf::createUnwindTable() won't result in errors and
// and empty rows are not added to FDE UnwindTable.
- RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows);
}
@@ -504,9 +504,9 @@ TEST(DWARFDebugFrame, UnwindTableEmptyRows_NOPs) {
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_nop}), Succeeded());
EXPECT_TRUE(!TestCIE.cfis().empty());
- // Verify dwarf::UnwindTable::create() won't result in errors and
+ // Verify dwarf::createUnwindTable() won't result in errors and
// and empty rows are not added to CIE UnwindTable.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestCIE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestCIE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const size_t ExpectedNumOfRows = 0;
EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows);
@@ -525,9 +525,9 @@ TEST(DWARFDebugFrame, UnwindTableEmptyRows_NOPs) {
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_nop}), Succeeded());
EXPECT_TRUE(!TestFDE.cfis().empty());
- // Verify dwarf::UnwindTable::create() won't result in errors and
+ // Verify dwarf::createUnwindTable() won't result in errors and
// and empty rows are not added to FDE UnwindTable.
- RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows);
}
@@ -567,7 +567,7 @@ TEST(DWARFDebugFrame, UnwindTableErrorNonAscendingFDERows) {
Succeeded());
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA_set_loc with adrress 0x1000 which"
" must be greater than the current row "
@@ -603,7 +603,7 @@ TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_restore_state) {
Succeeded());
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA_restore_state without a matching "
"previous DW_CFA_remember_state"));
@@ -639,7 +639,7 @@ TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_GNU_window_save) {
Succeeded());
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA opcode 0x2d is not supported for "
"architecture x86_64"));
@@ -674,7 +674,7 @@ TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_def_cfa_offset) {
Succeeded());
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA_def_cfa_offset found when CFA "
"rule was not RegPlusOffset"));
@@ -709,7 +709,7 @@ TEST(DWARFDebugFrame, UnwindTableDefCFAOffsetSFCFAError) {
Succeeded());
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA_def_cfa_offset_sf found when CFA "
"rule was not RegPlusOffset"));
@@ -745,7 +745,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa_register) {
EXPECT_THAT_ERROR(parseCFI(TestFDE, {}), Succeeded());
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
@@ -817,7 +817,7 @@ TEST(DWARFDebugFrame, UnwindTableRowPushingOpcodes) {
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
ASSERT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 6u);
@@ -892,7 +892,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore) {
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 2u);
@@ -955,7 +955,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore_extended) {
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 2u);
@@ -1016,7 +1016,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_offset) {
Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(8));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
@@ -1068,7 +1068,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_offset) {
Reg2, dwarf::UnwindLocation::createIsCFAPlusOffset(8));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
@@ -1113,7 +1113,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_nop) {
Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
@@ -1203,7 +1203,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_remember_state) {
Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(-24));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 5u);
@@ -1270,7 +1270,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_undefined) {
dwarf::UnwindLocation::createUndefined());
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
@@ -1314,7 +1314,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_same_value) {
VerifyLocs.setRegisterLocation(Reg1, dwarf::UnwindLocation::createSame());
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
@@ -1360,7 +1360,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_register) {
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
@@ -1412,7 +1412,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_expression) {
Reg, dwarf::UnwindLocation::createAtDWARFExpression(Expr));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
@@ -1464,7 +1464,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_expression) {
Reg, dwarf::UnwindLocation::createIsDWARFExpression(Expr));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
@@ -1527,7 +1527,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa) {
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 5u);
@@ -1625,7 +1625,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_LLVM_def_aspace_cfa) {
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
- Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+ Expected<dwarf::UnwindTable> RowsOrErr = dwarf::createUnwindTable(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 5u);
>From 5c807f2854fbf702bb57a91ca7e63c7b3754262c Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Tue, 17 Jun 2025 20:54:21 +0000
Subject: [PATCH 2/6] Annotate the extracted UnwindTable creators with LLVM ABI
---
llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index d48d3b628a9c8..512d8ba0e9035 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -392,7 +392,7 @@ class CIE;
///
/// \returns An error if the DWARF Call Frame Information opcodes have state
/// machine errors, or a valid UnwindTable otherwise.
-Expected<UnwindTable> createUnwindTable(const CIE *Cie);
+LLVM_ABI Expected<UnwindTable> createUnwindTable(const CIE *Cie);
class FDE;
@@ -404,7 +404,7 @@ class FDE;
///
/// \returns An error if the DWARF Call Frame Information opcodes have state
/// machine errors, or a valid UnwindTable otherwise.
-Expected<UnwindTable> createUnwindTable(const FDE *Fde);
+LLVM_ABI Expected<UnwindTable> createUnwindTable(const FDE *Fde);
/// An entry in either debug_frame or eh_frame. This entry can be a CIE or an
/// FDE.
>From aac505a7184f35bb075dd48029c16f5285f46143 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Mon, 2 Jun 2025 22:31:07 +0000
Subject: [PATCH 3/6] Remove UnwindTable dependency on CIE, and FDE
(transitively on llvm/Object)
For creating new UnwindTable, two static methods was implemented inside it, to create an instance of it from a CIE or FDE.
This static methods are moved out of the class as a library functions.
---
.../llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index 512d8ba0e9035..c47d6f137ca3c 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -406,6 +406,30 @@ class FDE;
/// machine errors, or a valid UnwindTable otherwise.
LLVM_ABI Expected<UnwindTable> createUnwindTable(const FDE *Fde);
+class CIE;
+
+/// Create an UnwindTable from a Common Information Entry (CIE).
+///
+/// \param Cie The Common Information Entry to extract the table from. The
+/// CFIProgram is retrieved from the \a Cie object and used to create the
+/// UnwindTable.
+///
+/// \returns An error if the DWARF Call Frame Information opcodes have state
+/// machine errors, or a valid UnwindTable otherwise.
+Expected<UnwindTable> createUnwindTable(const CIE *Cie);
+
+class FDE;
+
+/// Create an UnwindTable from a Frame Descriptor Entry (FDE).
+///
+/// \param Fde The Frame Descriptor Entry to extract the table from. The
+/// CFIProgram is retrieved from the \a Fde object and used to create the
+/// UnwindTable.
+///
+/// \returns An error if the DWARF Call Frame Information opcodes have state
+/// machine errors, or a valid UnwindTable otherwise.
+Expected<UnwindTable> createUnwindTable(const FDE *Fde);
+
/// An entry in either debug_frame or eh_frame. This entry can be a CIE or an
/// FDE.
class FrameEntry {
>From f75586c2b11d96de1e4133b6d7eaf6c0dc07a2f4 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Tue, 3 Jun 2025 02:37:18 +0000
Subject: [PATCH 4/6] Separate UnwindTable from DWARF Debug Frame
---
.../llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 380 +------------
.../llvm/DebugInfo/DWARF/DWARFUnwindTable.h | 377 +++++++++++++
llvm/lib/DebugInfo/DWARF/CMakeLists.txt | 1 +
llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp | 480 -----------------
llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp | 500 ++++++++++++++++++
5 files changed, 879 insertions(+), 859 deletions(-)
create mode 100644 llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
create mode 100644 llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index c47d6f137ca3c..1ea3700690edd 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -9,15 +9,13 @@
#ifndef LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H
#define LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/iterator.h"
#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
-#include "llvm/Support/Compiler.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
#include "llvm/Support/Error.h"
#include "llvm/TargetParser/Triple.h"
-#include <map>
#include <memory>
#include <vector>
@@ -30,382 +28,6 @@ struct DIDumpOptions;
namespace dwarf {
-constexpr uint32_t InvalidRegisterNumber = UINT32_MAX;
-
-/// A class that represents a location for the Call Frame Address (CFA) or a
-/// register. This is decoded from the DWARF Call Frame Information
-/// instructions and put into an UnwindRow.
-class UnwindLocation {
-public:
- enum Location {
- /// Not specified.
- Unspecified,
- /// Register is not available and can't be recovered.
- Undefined,
- /// Register value is in the register, nothing needs to be done to unwind
- /// it:
- /// reg = reg
- Same,
- /// Register is in or at the CFA plus an offset:
- /// reg = CFA + offset
- /// reg = defef(CFA + offset)
- CFAPlusOffset,
- /// Register or CFA is in or at a register plus offset, optionally in
- /// an address space:
- /// reg = reg + offset [in addrspace]
- /// reg = deref(reg + offset [in addrspace])
- RegPlusOffset,
- /// Register or CFA value is in or at a value found by evaluating a DWARF
- /// expression:
- /// reg = eval(dwarf_expr)
- /// reg = deref(eval(dwarf_expr))
- DWARFExpr,
- /// Value is a constant value contained in "Offset":
- /// reg = Offset
- Constant,
- };
-
-private:
- Location Kind; /// The type of the location that describes how to unwind it.
- uint32_t RegNum; /// The register number for Kind == RegPlusOffset.
- int32_t Offset; /// The offset for Kind == CFAPlusOffset or RegPlusOffset.
- std::optional<uint32_t> AddrSpace; /// The address space for Kind ==
- /// RegPlusOffset for CFA.
- std::optional<DWARFExpression> Expr; /// The DWARF expression for Kind ==
- /// DWARFExpression.
- bool Dereference; /// If true, the resulting location must be dereferenced
- /// after the location value is computed.
-
- // Constructors are private to force people to use the create static
- // functions.
- UnwindLocation(Location K)
- : Kind(K), RegNum(InvalidRegisterNumber), Offset(0),
- AddrSpace(std::nullopt), Dereference(false) {}
-
- UnwindLocation(Location K, uint32_t Reg, int32_t Off,
- std::optional<uint32_t> AS, bool Deref)
- : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {}
-
- UnwindLocation(DWARFExpression E, bool Deref)
- : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E),
- Dereference(Deref) {}
-
-public:
- /// Create a location whose rule is set to Unspecified. This means the
- /// register value might be in the same register but it wasn't specified in
- /// the unwind opcodes.
- LLVM_ABI static UnwindLocation createUnspecified();
- /// Create a location where the value is undefined and not available. This can
- /// happen when a register is volatile and can't be recovered.
- LLVM_ABI static UnwindLocation createUndefined();
- /// Create a location where the value is known to be in the register itself.
- LLVM_ABI static UnwindLocation createSame();
- /// Create a location that is in (Deref == false) or at (Deref == true) the
- /// CFA plus an offset. Most registers that are spilled onto the stack use
- /// this rule. The rule for the register will use this rule and specify a
- /// unique offset from the CFA with \a Deref set to true. This value will be
- /// relative to a CFA value which is typically defined using the register
- /// plus offset location. \see createRegisterPlusOffset(...) for more
- /// information.
- LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off);
- LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off);
- /// Create a location where the saved value is in (Deref == false) or at
- /// (Deref == true) a regiser plus an offset and, optionally, in the specified
- /// address space (used mostly for the CFA).
- ///
- /// The CFA is usually defined using this rule by using the stack pointer or
- /// frame pointer as the register, with an offset that accounts for all
- /// spilled registers and all local variables in a function, and Deref ==
- /// false.
- LLVM_ABI static UnwindLocation
- createIsRegisterPlusOffset(uint32_t Reg, int32_t Off,
- std::optional<uint32_t> AddrSpace = std::nullopt);
- LLVM_ABI static UnwindLocation
- createAtRegisterPlusOffset(uint32_t Reg, int32_t Off,
- std::optional<uint32_t> AddrSpace = std::nullopt);
- /// Create a location whose value is the result of evaluating a DWARF
- /// expression. This allows complex expressions to be evaluated in order to
- /// unwind a register or CFA value.
- LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr);
- LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr);
- LLVM_ABI static UnwindLocation createIsConstant(int32_t Value);
-
- Location getLocation() const { return Kind; }
- uint32_t getRegister() const { return RegNum; }
- int32_t getOffset() const { return Offset; }
- uint32_t getAddressSpace() const {
- assert(Kind == RegPlusOffset && AddrSpace);
- return *AddrSpace;
- }
- int32_t getConstant() const { return Offset; }
- /// Some opcodes will modify the CFA location's register only, so we need
- /// to be able to modify the CFA register when evaluating DWARF Call Frame
- /// Information opcodes.
- void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; }
- /// Some opcodes will modify the CFA location's offset only, so we need
- /// to be able to modify the CFA offset when evaluating DWARF Call Frame
- /// Information opcodes.
- void setOffset(int32_t NewOffset) { Offset = NewOffset; }
- /// Some opcodes modify a constant value and we need to be able to update
- /// the constant value (DW_CFA_GNU_window_save which is also known as
- // DW_CFA_AARCH64_negate_ra_state).
- void setConstant(int32_t Value) { Offset = Value; }
-
- std::optional<DWARFExpression> getDWARFExpressionBytes() const {
- return Expr;
- }
- /// Dump a location expression as text and use the register information if
- /// some is provided.
- ///
- /// \param OS the stream to use for output.
- ///
- /// \param MRI register information that helps emit register names insteead
- /// of raw register numbers.
- ///
- /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
- /// instead of from .debug_frame. This is needed for register number
- /// conversion because some register numbers differ between the two sections
- /// for certain architectures like x86.
- LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
-
- LLVM_ABI bool operator==(const UnwindLocation &RHS) const;
-};
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
-
-/// A class that can track all registers with locations in a UnwindRow object.
-///
-/// Register locations use a map where the key is the register number and the
-/// the value is a UnwindLocation.
-///
-/// The register maps are put into a class so that all register locations can
-/// be copied when parsing the unwind opcodes DW_CFA_remember_state and
-/// DW_CFA_restore_state.
-class RegisterLocations {
- std::map<uint32_t, UnwindLocation> Locations;
-
-public:
- /// Return the location for the register in \a RegNum if there is a location.
- ///
- /// \param RegNum the register number to find a location for.
- ///
- /// \returns A location if one is available for \a RegNum, or std::nullopt
- /// otherwise.
- std::optional<UnwindLocation> getRegisterLocation(uint32_t RegNum) const {
- auto Pos = Locations.find(RegNum);
- if (Pos == Locations.end())
- return std::nullopt;
- return Pos->second;
- }
-
- /// Set the location for the register in \a RegNum to \a Location.
- ///
- /// \param RegNum the register number to set the location for.
- ///
- /// \param Location the UnwindLocation that describes how to unwind the value.
- void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) {
- Locations.erase(RegNum);
- Locations.insert(std::make_pair(RegNum, Location));
- }
-
- /// Removes any rule for the register in \a RegNum.
- ///
- /// \param RegNum the register number to remove the location for.
- void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); }
-
- /// Dump all registers + locations that are currently defined in this object.
- ///
- /// \param OS the stream to use for output.
- ///
- /// \param MRI register information that helps emit register names insteead
- /// of raw register numbers.
- ///
- /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
- /// instead of from .debug_frame. This is needed for register number
- /// conversion because some register numbers differ between the two sections
- /// for certain architectures like x86.
- LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
-
- /// Returns true if we have any register locations in this object.
- bool hasLocations() const { return !Locations.empty(); }
-
- size_t size() const { return Locations.size(); }
-
- bool operator==(const RegisterLocations &RHS) const {
- return Locations == RHS.Locations;
- }
-};
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
-
-/// A class that represents a single row in the unwind table that is decoded by
-/// parsing the DWARF Call Frame Information opcodes.
-///
-/// The row consists of an optional address, the rule to unwind the CFA and all
-/// rules to unwind any registers. If the address doesn't have a value, this
-/// row represents the initial instructions for a CIE. If the address has a
-/// value the UnwindRow represents a row in the UnwindTable for a FDE. The
-/// address is the first address for which the CFA location and register rules
-/// are valid within a function.
-///
-/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame
-/// Information and UnwindRow objects are lazily populated and pushed onto a
-/// stack in the UnwindTable when evaluating this state machine. Accessors are
-/// needed for the address, CFA value, and register locations as the opcodes
-/// encode a state machine that produces a sorted array of UnwindRow objects
-/// \see UnwindTable.
-class UnwindRow {
- /// The address will be valid when parsing the instructions in a FDE. If
- /// invalid, this object represents the initial instructions of a CIE.
- std::optional<uint64_t> Address; ///< Address for row in FDE, invalid for CIE.
- UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA).
- RegisterLocations RegLocs; ///< How to unwind all registers in this list.
-
-public:
- UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {}
-
- /// Returns true if the address is valid in this object.
- bool hasAddress() const { return Address.has_value(); }
-
- /// Get the address for this row.
- ///
- /// Clients should only call this function after verifying it has a valid
- /// address with a call to \see hasAddress().
- uint64_t getAddress() const { return *Address; }
-
- /// Set the address for this UnwindRow.
- ///
- /// The address represents the first address for which the CFAValue and
- /// RegLocs are valid within a function.
- void setAddress(uint64_t Addr) { Address = Addr; }
-
- /// Offset the address for this UnwindRow.
- ///
- /// The address represents the first address for which the CFAValue and
- /// RegLocs are valid within a function. Clients must ensure that this object
- /// already has an address (\see hasAddress()) prior to calling this
- /// function.
- void slideAddress(uint64_t Offset) { *Address += Offset; }
- UnwindLocation &getCFAValue() { return CFAValue; }
- const UnwindLocation &getCFAValue() const { return CFAValue; }
- RegisterLocations &getRegisterLocations() { return RegLocs; }
- const RegisterLocations &getRegisterLocations() const { return RegLocs; }
-
- /// Dump the UnwindRow to the stream.
- ///
- /// \param OS the stream to use for output.
- ///
- /// \param MRI register information that helps emit register names insteead
- /// of raw register numbers.
- ///
- /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
- /// instead of from .debug_frame. This is needed for register number
- /// conversion because some register numbers differ between the two sections
- /// for certain architectures like x86.
- ///
- /// \param IndentLevel specify the indent level as an integer. The UnwindRow
- /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
- LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
- unsigned IndentLevel = 0) const;
-};
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
-
-/// A class that contains all UnwindRow objects for an FDE or a single unwind
-/// row for a CIE. To unwind an address the rows, which are sorted by start
-/// address, can be searched to find the UnwindRow with the lowest starting
-/// address that is greater than or equal to the address that is being looked
-/// up.
-class UnwindTable {
-public:
- using RowContainer = std::vector<UnwindRow>;
- using iterator = RowContainer::iterator;
- using const_iterator = RowContainer::const_iterator;
-
- size_t size() const { return Rows.size(); }
- iterator begin() { return Rows.begin(); }
- const_iterator begin() const { return Rows.begin(); }
- iterator end() { return Rows.end(); }
- const_iterator end() const { return Rows.end(); }
- const UnwindRow &operator[](size_t Index) const {
- assert(Index < size());
- return Rows[Index];
- }
- void insertRow(const UnwindRow &Row) { Rows.push_back(Row); }
-
- /// Set the last address that this unwinding table refers to.
- ///
- /// This is used when this table is created based on a FDE.
- void setEndAddress(uint64_t Addr) { EndAddress = Addr; }
-
- /// Dump the UnwindTable to the stream.
- ///
- /// \param OS the stream to use for output.
- ///
- /// \param MRI register information that helps emit register names insteead
- /// of raw register numbers.
- ///
- /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
- /// instead of from .debug_frame. This is needed for register number
- /// conversion because some register numbers differ between the two sections
- /// for certain architectures like x86.
- ///
- /// \param IndentLevel specify the indent level as an integer. The UnwindRow
- /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
- LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
- unsigned IndentLevel = 0) const;
-
- /// Parse the information in the CFIProgram and update the CurrRow object
- /// that the state machine describes.
- ///
- /// This is an internal implementation that emulates the state machine
- /// described in the DWARF Call Frame Information opcodes and will push
- /// CurrRow onto the Rows container when needed.
- ///
- /// \param CFIP the CFI program that contains the opcodes from a CIE or FDE.
- ///
- /// \param CurrRow the current row to modify while parsing the state machine.
- ///
- /// \param InitialLocs If non-NULL, we are parsing a FDE and this contains
- /// the initial register locations from the CIE. If NULL, then a CIE's
- /// opcodes are being parsed and this is not needed. This is used for the
- /// DW_CFA_restore and DW_CFA_restore_extended opcodes.
- Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
- const RegisterLocations *InitialLocs);
-
-private:
- RowContainer Rows;
- /// The end address when data is extracted from a FDE. This value will be
- /// invalid when a UnwindTable is extracted from a CIE.
- std::optional<uint64_t> EndAddress;
-};
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
-
-class CIE;
-
-/// Create an UnwindTable from a Common Information Entry (CIE).
-///
-/// \param Cie The Common Information Entry to extract the table from. The
-/// CFIProgram is retrieved from the \a Cie object and used to create the
-/// UnwindTable.
-///
-/// \returns An error if the DWARF Call Frame Information opcodes have state
-/// machine errors, or a valid UnwindTable otherwise.
-LLVM_ABI Expected<UnwindTable> createUnwindTable(const CIE *Cie);
-
-class FDE;
-
-/// Create an UnwindTable from a Frame Descriptor Entry (FDE).
-///
-/// \param Fde The Frame Descriptor Entry to extract the table from. The
-/// CFIProgram is retrieved from the \a Fde object and used to create the
-/// UnwindTable.
-///
-/// \returns An error if the DWARF Call Frame Information opcodes have state
-/// machine errors, or a valid UnwindTable otherwise.
-LLVM_ABI Expected<UnwindTable> createUnwindTable(const FDE *Fde);
-
class CIE;
/// Create an UnwindTable from a Common Information Entry (CIE).
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
new file mode 100644
index 0000000000000..7a36b1630e7e1
--- /dev/null
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
@@ -0,0 +1,377 @@
+//===- DWARFUnwindTable.h ----------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H
+#define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H
+
+#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
+#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <vector>
+
+namespace llvm {
+
+namespace dwarf {
+constexpr uint32_t InvalidRegisterNumber = UINT32_MAX;
+
+/// A class that represents a location for the Call Frame Address (CFA) or a
+/// register. This is decoded from the DWARF Call Frame Information
+/// instructions and put into an UnwindRow.
+class UnwindLocation {
+public:
+ enum Location {
+ /// Not specified.
+ Unspecified,
+ /// Register is not available and can't be recovered.
+ Undefined,
+ /// Register value is in the register, nothing needs to be done to unwind
+ /// it:
+ /// reg = reg
+ Same,
+ /// Register is in or at the CFA plus an offset:
+ /// reg = CFA + offset
+ /// reg = defef(CFA + offset)
+ CFAPlusOffset,
+ /// Register or CFA is in or at a register plus offset, optionally in
+ /// an address space:
+ /// reg = reg + offset [in addrspace]
+ /// reg = deref(reg + offset [in addrspace])
+ RegPlusOffset,
+ /// Register or CFA value is in or at a value found by evaluating a DWARF
+ /// expression:
+ /// reg = eval(dwarf_expr)
+ /// reg = deref(eval(dwarf_expr))
+ DWARFExpr,
+ /// Value is a constant value contained in "Offset":
+ /// reg = Offset
+ Constant,
+ };
+
+private:
+ Location Kind; /// The type of the location that describes how to unwind it.
+ uint32_t RegNum; /// The register number for Kind == RegPlusOffset.
+ int32_t Offset; /// The offset for Kind == CFAPlusOffset or RegPlusOffset.
+ std::optional<uint32_t> AddrSpace; /// The address space for Kind ==
+ /// RegPlusOffset for CFA.
+ std::optional<DWARFExpression> Expr; /// The DWARF expression for Kind ==
+ /// DWARFExpression.
+ bool Dereference; /// If true, the resulting location must be dereferenced
+ /// after the location value is computed.
+
+ // Constructors are private to force people to use the create static
+ // functions.
+ UnwindLocation(Location K)
+ : Kind(K), RegNum(InvalidRegisterNumber), Offset(0),
+ AddrSpace(std::nullopt), Dereference(false) {}
+
+ UnwindLocation(Location K, uint32_t Reg, int32_t Off,
+ std::optional<uint32_t> AS, bool Deref)
+ : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {}
+
+ UnwindLocation(DWARFExpression E, bool Deref)
+ : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E),
+ Dereference(Deref) {}
+
+public:
+ /// Create a location whose rule is set to Unspecified. This means the
+ /// register value might be in the same register but it wasn't specified in
+ /// the unwind opcodes.
+ static UnwindLocation createUnspecified();
+ /// Create a location where the value is undefined and not available. This can
+ /// happen when a register is volatile and can't be recovered.
+ static UnwindLocation createUndefined();
+ /// Create a location where the value is known to be in the register itself.
+ static UnwindLocation createSame();
+ /// Create a location that is in (Deref == false) or at (Deref == true) the
+ /// CFA plus an offset. Most registers that are spilled onto the stack use
+ /// this rule. The rule for the register will use this rule and specify a
+ /// unique offset from the CFA with \a Deref set to true. This value will be
+ /// relative to a CFA value which is typically defined using the register
+ /// plus offset location. \see createRegisterPlusOffset(...) for more
+ /// information.
+ static UnwindLocation createIsCFAPlusOffset(int32_t Off);
+ static UnwindLocation createAtCFAPlusOffset(int32_t Off);
+ /// Create a location where the saved value is in (Deref == false) or at
+ /// (Deref == true) a regiser plus an offset and, optionally, in the specified
+ /// address space (used mostly for the CFA).
+ ///
+ /// The CFA is usually defined using this rule by using the stack pointer or
+ /// frame pointer as the register, with an offset that accounts for all
+ /// spilled registers and all local variables in a function, and Deref ==
+ /// false.
+ static UnwindLocation
+ createIsRegisterPlusOffset(uint32_t Reg, int32_t Off,
+ std::optional<uint32_t> AddrSpace = std::nullopt);
+ static UnwindLocation
+ createAtRegisterPlusOffset(uint32_t Reg, int32_t Off,
+ std::optional<uint32_t> AddrSpace = std::nullopt);
+ /// Create a location whose value is the result of evaluating a DWARF
+ /// expression. This allows complex expressions to be evaluated in order to
+ /// unwind a register or CFA value.
+ static UnwindLocation createIsDWARFExpression(DWARFExpression Expr);
+ static UnwindLocation createAtDWARFExpression(DWARFExpression Expr);
+ static UnwindLocation createIsConstant(int32_t Value);
+
+ Location getLocation() const { return Kind; }
+ uint32_t getRegister() const { return RegNum; }
+ int32_t getOffset() const { return Offset; }
+ uint32_t getAddressSpace() const {
+ assert(Kind == RegPlusOffset && AddrSpace);
+ return *AddrSpace;
+ }
+ int32_t getConstant() const { return Offset; }
+ /// Some opcodes will modify the CFA location's register only, so we need
+ /// to be able to modify the CFA register when evaluating DWARF Call Frame
+ /// Information opcodes.
+ void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; }
+ /// Some opcodes will modify the CFA location's offset only, so we need
+ /// to be able to modify the CFA offset when evaluating DWARF Call Frame
+ /// Information opcodes.
+ void setOffset(int32_t NewOffset) { Offset = NewOffset; }
+ /// Some opcodes modify a constant value and we need to be able to update
+ /// the constant value (DW_CFA_GNU_window_save which is also known as
+ // DW_CFA_AARCH64_negate_ra_state).
+ void setConstant(int32_t Value) { Offset = Value; }
+
+ std::optional<DWARFExpression> getDWARFExpressionBytes() const {
+ return Expr;
+ }
+ /// Dump a location expression as text and use the register information if
+ /// some is provided.
+ ///
+ /// \param OS the stream to use for output.
+ ///
+ /// \param MRI register information that helps emit register names insteead
+ /// of raw register numbers.
+ ///
+ /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
+ /// instead of from .debug_frame. This is needed for register number
+ /// conversion because some register numbers differ between the two sections
+ /// for certain architectures like x86.
+ void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
+
+ bool operator==(const UnwindLocation &RHS) const;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
+
+/// A class that can track all registers with locations in a UnwindRow object.
+///
+/// Register locations use a map where the key is the register number and the
+/// the value is a UnwindLocation.
+///
+/// The register maps are put into a class so that all register locations can
+/// be copied when parsing the unwind opcodes DW_CFA_remember_state and
+/// DW_CFA_restore_state.
+class RegisterLocations {
+ std::map<uint32_t, UnwindLocation> Locations;
+
+public:
+ /// Return the location for the register in \a RegNum if there is a location.
+ ///
+ /// \param RegNum the register number to find a location for.
+ ///
+ /// \returns A location if one is available for \a RegNum, or std::nullopt
+ /// otherwise.
+ std::optional<UnwindLocation> getRegisterLocation(uint32_t RegNum) const {
+ auto Pos = Locations.find(RegNum);
+ if (Pos == Locations.end())
+ return std::nullopt;
+ return Pos->second;
+ }
+
+ /// Set the location for the register in \a RegNum to \a Location.
+ ///
+ /// \param RegNum the register number to set the location for.
+ ///
+ /// \param Location the UnwindLocation that describes how to unwind the value.
+ void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) {
+ Locations.erase(RegNum);
+ Locations.insert(std::make_pair(RegNum, Location));
+ }
+
+ /// Removes any rule for the register in \a RegNum.
+ ///
+ /// \param RegNum the register number to remove the location for.
+ void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); }
+
+ /// Dump all registers + locations that are currently defined in this object.
+ ///
+ /// \param OS the stream to use for output.
+ ///
+ /// \param MRI register information that helps emit register names insteead
+ /// of raw register numbers.
+ ///
+ /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
+ /// instead of from .debug_frame. This is needed for register number
+ /// conversion because some register numbers differ between the two sections
+ /// for certain architectures like x86.
+ void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
+
+ /// Returns true if we have any register locations in this object.
+ bool hasLocations() const { return !Locations.empty(); }
+
+ size_t size() const { return Locations.size(); }
+
+ bool operator==(const RegisterLocations &RHS) const {
+ return Locations == RHS.Locations;
+ }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
+
+/// A class that represents a single row in the unwind table that is decoded by
+/// parsing the DWARF Call Frame Information opcodes.
+///
+/// The row consists of an optional address, the rule to unwind the CFA and all
+/// rules to unwind any registers. If the address doesn't have a value, this
+/// row represents the initial instructions for a CIE. If the address has a
+/// value the UnwindRow represents a row in the UnwindTable for a FDE. The
+/// address is the first address for which the CFA location and register rules
+/// are valid within a function.
+///
+/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame
+/// Information and UnwindRow objects are lazily populated and pushed onto a
+/// stack in the UnwindTable when evaluating this state machine. Accessors are
+/// needed for the address, CFA value, and register locations as the opcodes
+/// encode a state machine that produces a sorted array of UnwindRow objects
+/// \see UnwindTable.
+class UnwindRow {
+ /// The address will be valid when parsing the instructions in a FDE. If
+ /// invalid, this object represents the initial instructions of a CIE.
+ std::optional<uint64_t> Address; ///< Address for row in FDE, invalid for CIE.
+ UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA).
+ RegisterLocations RegLocs; ///< How to unwind all registers in this list.
+
+public:
+ UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {}
+
+ /// Returns true if the address is valid in this object.
+ bool hasAddress() const { return Address.has_value(); }
+
+ /// Get the address for this row.
+ ///
+ /// Clients should only call this function after verifying it has a valid
+ /// address with a call to \see hasAddress().
+ uint64_t getAddress() const { return *Address; }
+
+ /// Set the address for this UnwindRow.
+ ///
+ /// The address represents the first address for which the CFAValue and
+ /// RegLocs are valid within a function.
+ void setAddress(uint64_t Addr) { Address = Addr; }
+
+ /// Offset the address for this UnwindRow.
+ ///
+ /// The address represents the first address for which the CFAValue and
+ /// RegLocs are valid within a function. Clients must ensure that this object
+ /// already has an address (\see hasAddress()) prior to calling this
+ /// function.
+ void slideAddress(uint64_t Offset) { *Address += Offset; }
+ UnwindLocation &getCFAValue() { return CFAValue; }
+ const UnwindLocation &getCFAValue() const { return CFAValue; }
+ RegisterLocations &getRegisterLocations() { return RegLocs; }
+ const RegisterLocations &getRegisterLocations() const { return RegLocs; }
+
+ /// Dump the UnwindRow to the stream.
+ ///
+ /// \param OS the stream to use for output.
+ ///
+ /// \param MRI register information that helps emit register names insteead
+ /// of raw register numbers.
+ ///
+ /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
+ /// instead of from .debug_frame. This is needed for register number
+ /// conversion because some register numbers differ between the two sections
+ /// for certain architectures like x86.
+ ///
+ /// \param IndentLevel specify the indent level as an integer. The UnwindRow
+ /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
+ void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+ unsigned IndentLevel = 0) const;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
+
+/// A class that contains all UnwindRow objects for an FDE or a single unwind
+/// row for a CIE. To unwind an address the rows, which are sorted by start
+/// address, can be searched to find the UnwindRow with the lowest starting
+/// address that is greater than or equal to the address that is being looked
+/// up.
+class UnwindTable {
+public:
+ using RowContainer = std::vector<UnwindRow>;
+ using iterator = RowContainer::iterator;
+ using const_iterator = RowContainer::const_iterator;
+
+ size_t size() const { return Rows.size(); }
+ iterator begin() { return Rows.begin(); }
+ const_iterator begin() const { return Rows.begin(); }
+ iterator end() { return Rows.end(); }
+ const_iterator end() const { return Rows.end(); }
+ const UnwindRow &operator[](size_t Index) const {
+ assert(Index < size());
+ return Rows[Index];
+ }
+ void insertRow(const UnwindRow &Row) { Rows.push_back(Row); }
+
+ /// Set the last address that this unwinding table refers to.
+ ///
+ /// This is used when this table is created based on a FDE.
+ void setEndAddress(uint64_t Addr) { EndAddress = Addr; }
+
+ /// Dump the UnwindTable to the stream.
+ ///
+ /// \param OS the stream to use for output.
+ ///
+ /// \param MRI register information that helps emit register names insteead
+ /// of raw register numbers.
+ ///
+ /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
+ /// instead of from .debug_frame. This is needed for register number
+ /// conversion because some register numbers differ between the two sections
+ /// for certain architectures like x86.
+ ///
+ /// \param IndentLevel specify the indent level as an integer. The UnwindRow
+ /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
+ void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+ unsigned IndentLevel = 0) const;
+
+ /// Parse the information in the CFIProgram and update the CurrRow object
+ /// that the state machine describes.
+ ///
+ /// This is an internal implementation that emulates the state machine
+ /// described in the DWARF Call Frame Information opcodes and will push
+ /// CurrRow onto the Rows container when needed.
+ ///
+ /// \param CFIP the CFI program that contains the opcodes from a CIE or FDE.
+ ///
+ /// \param CurrRow the current row to modify while parsing the state machine.
+ ///
+ /// \param InitialLocs If non-NULL, we are parsing a FDE and this contains
+ /// the initial register locations from the CIE. If NULL, then a CIE's
+ /// opcodes are being parsed and this is not needed. This is used for the
+ /// DW_CFA_restore and DW_CFA_restore_extended opcodes.
+ Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
+ const RegisterLocations *InitialLocs);
+
+private:
+ RowContainer Rows;
+ /// The end address when data is extracted from a FDE. This value will be
+ /// invalid when a UnwindTable is extracted from a CIE.
+ std::optional<uint64_t> EndAddress;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
+
+} // end namespace dwarf
+
+} // end namespace llvm
+
+#endif
\ No newline at end of file
diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
index cc9734f9f22be..b9ff0d676c4c3 100644
--- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
+++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
@@ -26,6 +26,7 @@ add_llvm_component_library(LLVMDebugInfoDWARF
DWARFTypeUnit.cpp
DWARFUnitIndex.cpp
DWARFUnit.cpp
+ DWARFUnwindTable.cpp
DWARFVerifier.cpp
ADDITIONAL_HEADER_DIRS
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
index cf42151bb84aa..e6c37203dd557 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
@@ -29,180 +29,6 @@
using namespace llvm;
using namespace dwarf;
-static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts,
- unsigned RegNum) {
- if (DumpOpts.GetNameForDWARFReg) {
- auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH);
- if (!RegName.empty()) {
- OS << RegName;
- return;
- }
- }
- OS << "reg" << RegNum;
-}
-
-UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; }
-
-UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; }
-
-UnwindLocation UnwindLocation::createSame() { return {Same}; }
-
-UnwindLocation UnwindLocation::createIsConstant(int32_t Value) {
- return {Constant, InvalidRegisterNumber, Value, std::nullopt, false};
-}
-
-UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) {
- return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false};
-}
-
-UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) {
- return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true};
-}
-
-UnwindLocation
-UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
- std::optional<uint32_t> AddrSpace) {
- return {RegPlusOffset, RegNum, Offset, AddrSpace, false};
-}
-
-UnwindLocation
-UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
- std::optional<uint32_t> AddrSpace) {
- return {RegPlusOffset, RegNum, Offset, AddrSpace, true};
-}
-
-UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) {
- return {Expr, false};
-}
-
-UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) {
- return {Expr, true};
-}
-
-void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
- if (Dereference)
- OS << '[';
- switch (Kind) {
- case Unspecified:
- OS << "unspecified";
- break;
- case Undefined:
- OS << "undefined";
- break;
- case Same:
- OS << "same";
- break;
- case CFAPlusOffset:
- OS << "CFA";
- if (Offset == 0)
- break;
- if (Offset > 0)
- OS << "+";
- OS << Offset;
- break;
- case RegPlusOffset:
- printRegister(OS, DumpOpts, RegNum);
- if (Offset == 0 && !AddrSpace)
- break;
- if (Offset >= 0)
- OS << "+";
- OS << Offset;
- if (AddrSpace)
- OS << " in addrspace" << *AddrSpace;
- break;
- case DWARFExpr: {
- if (Expr)
- DWARFExpressionPrinter::print(&(*Expr), OS, DumpOpts, nullptr);
- break;
- }
- case Constant:
- OS << Offset;
- break;
- }
- if (Dereference)
- OS << ']';
-}
-
-raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
- const UnwindLocation &UL) {
- auto DumpOpts = DIDumpOptions();
- UL.dump(OS, DumpOpts);
- return OS;
-}
-
-bool UnwindLocation::operator==(const UnwindLocation &RHS) const {
- if (Kind != RHS.Kind)
- return false;
- switch (Kind) {
- case Unspecified:
- case Undefined:
- case Same:
- return true;
- case CFAPlusOffset:
- return Offset == RHS.Offset && Dereference == RHS.Dereference;
- case RegPlusOffset:
- return RegNum == RHS.RegNum && Offset == RHS.Offset &&
- Dereference == RHS.Dereference;
- case DWARFExpr:
- return *Expr == *RHS.Expr && Dereference == RHS.Dereference;
- case Constant:
- return Offset == RHS.Offset;
- }
- return false;
-}
-
-void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
- bool First = true;
- for (const auto &RegLocPair : Locations) {
- if (First)
- First = false;
- else
- OS << ", ";
- printRegister(OS, DumpOpts, RegLocPair.first);
- OS << '=';
- RegLocPair.second.dump(OS, DumpOpts);
- }
-}
-
-raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
- const RegisterLocations &RL) {
- auto DumpOpts = DIDumpOptions();
- RL.dump(OS, DumpOpts);
- return OS;
-}
-
-void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
- unsigned IndentLevel) const {
- OS.indent(2 * IndentLevel);
- if (hasAddress())
- OS << format("0x%" PRIx64 ": ", *Address);
- OS << "CFA=";
- CFAValue.dump(OS, DumpOpts);
- if (RegLocs.hasLocations()) {
- OS << ": ";
- RegLocs.dump(OS, DumpOpts);
- }
- OS << "\n";
-}
-
-raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) {
- auto DumpOpts = DIDumpOptions();
- Row.dump(OS, DumpOpts, 0);
- return OS;
-}
-
-void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
- unsigned IndentLevel) const {
- for (const UnwindRow &Row : Rows)
- Row.dump(OS, DumpOpts, IndentLevel);
-}
-
-raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) {
- auto DumpOpts = DIDumpOptions();
- Rows.dump(OS, DumpOpts, 0);
- return OS;
-}
-
Expected<UnwindTable> llvm::dwarf::createUnwindTable(const FDE *Fde) {
const CIE *Cie = Fde->getLinkedCIE();
if (Cie == nullptr)
@@ -250,312 +76,6 @@ Expected<UnwindTable> llvm::dwarf::createUnwindTable(const CIE *Cie) {
return UT;
}
-Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
- const RegisterLocations *InitialLocs) {
- // State consists of CFA value and register locations.
- std::vector<std::pair<UnwindLocation, RegisterLocations>> States;
- for (const CFIProgram::Instruction &Inst : CFIP) {
- switch (Inst.Opcode) {
- case dwarf::DW_CFA_set_loc: {
- // The DW_CFA_set_loc instruction takes a single operand that
- // represents a target address. The required action is to create a new
- // table row using the specified address as the location. All other
- // values in the new row are initially identical to the current row.
- // The new location value is always greater than the current one. If
- // the segment_size field of this FDE's CIE is non- zero, the initial
- // location is preceded by a segment selector of the given length
- llvm::Expected<uint64_t> NewAddress = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!NewAddress)
- return NewAddress.takeError();
- if (*NewAddress <= Row.getAddress())
- return createStringError(
- errc::invalid_argument,
- "%s with adrress 0x%" PRIx64 " which must be greater than the "
- "current row address 0x%" PRIx64,
- CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress,
- Row.getAddress());
- Rows.push_back(Row);
- Row.setAddress(*NewAddress);
- break;
- }
-
- case dwarf::DW_CFA_advance_loc:
- case dwarf::DW_CFA_advance_loc1:
- case dwarf::DW_CFA_advance_loc2:
- case dwarf::DW_CFA_advance_loc4: {
- // The DW_CFA_advance instruction takes a single operand that
- // represents a constant delta. The required action is to create a new
- // table row with a location value that is computed by taking the
- // current entry’s location value and adding the value of delta *
- // code_alignment_factor. All other values in the new row are initially
- // identical to the current row.
- Rows.push_back(Row);
- llvm::Expected<uint64_t> Offset = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!Offset)
- return Offset.takeError();
- Row.slideAddress(*Offset);
- break;
- }
-
- case dwarf::DW_CFA_restore:
- case dwarf::DW_CFA_restore_extended: {
- // The DW_CFA_restore instruction takes a single operand (encoded with
- // the opcode) that represents a register number. The required action
- // is to change the rule for the indicated register to the rule
- // assigned it by the initial_instructions in the CIE.
- if (InitialLocs == nullptr)
- return createStringError(
- errc::invalid_argument, "%s encountered while parsing a CIE",
- CFIP.callFrameString(Inst.Opcode).str().c_str());
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- if (std::optional<UnwindLocation> O =
- InitialLocs->getRegisterLocation(*RegNum))
- Row.getRegisterLocations().setRegisterLocation(*RegNum, *O);
- else
- Row.getRegisterLocations().removeRegisterLocation(*RegNum);
- break;
- }
-
- case dwarf::DW_CFA_offset:
- case dwarf::DW_CFA_offset_extended:
- case dwarf::DW_CFA_offset_extended_sf: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
- if (!Offset)
- return Offset.takeError();
- Row.getRegisterLocations().setRegisterLocation(
- *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset));
- break;
- }
-
- case dwarf::DW_CFA_nop:
- break;
-
- case dwarf::DW_CFA_remember_state:
- States.push_back(
- std::make_pair(Row.getCFAValue(), Row.getRegisterLocations()));
- break;
-
- case dwarf::DW_CFA_restore_state:
- if (States.empty())
- return createStringError(errc::invalid_argument,
- "DW_CFA_restore_state without a matching "
- "previous DW_CFA_remember_state");
- Row.getCFAValue() = States.back().first;
- Row.getRegisterLocations() = States.back().second;
- States.pop_back();
- break;
-
- case dwarf::DW_CFA_GNU_window_save:
- switch (CFIP.triple()) {
- case Triple::aarch64:
- case Triple::aarch64_be:
- case Triple::aarch64_32: {
- // DW_CFA_GNU_window_save is used for different things on different
- // architectures. For aarch64 it is known as
- // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the
- // value of the return address state between 1 and 0. If there is
- // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it
- // should be initially set to 1.
- constexpr uint32_t AArch64DWARFPAuthRaState = 34;
- auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
- AArch64DWARFPAuthRaState);
- if (LRLoc) {
- if (LRLoc->getLocation() == UnwindLocation::Constant) {
- // Toggle the constant value from 0 to 1 or 1 to 0.
- LRLoc->setConstant(LRLoc->getConstant() ^ 1);
- Row.getRegisterLocations().setRegisterLocation(
- AArch64DWARFPAuthRaState, *LRLoc);
- } else {
- return createStringError(
- errc::invalid_argument,
- "%s encountered when existing rule for this register is not "
- "a constant",
- CFIP.callFrameString(Inst.Opcode).str().c_str());
- }
- } else {
- Row.getRegisterLocations().setRegisterLocation(
- AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1));
- }
- break;
- }
-
- case Triple::sparc:
- case Triple::sparcv9:
- case Triple::sparcel:
- for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) {
- Row.getRegisterLocations().setRegisterLocation(
- RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8));
- }
- break;
-
- default: {
- return createStringError(
- errc::not_supported,
- "DW_CFA opcode %#x is not supported for architecture %s",
- Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str());
-
- break;
- }
- }
- break;
-
- case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: {
- constexpr uint32_t AArch64DWARFPAuthRaState = 34;
- auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
- AArch64DWARFPAuthRaState);
- if (LRLoc) {
- if (LRLoc->getLocation() == UnwindLocation::Constant) {
- // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0.
- LRLoc->setConstant(LRLoc->getConstant() ^ 0x3);
- } else {
- return createStringError(
- errc::invalid_argument,
- "%s encountered when existing rule for this register is not "
- "a constant",
- CFIP.callFrameString(Inst.Opcode).str().c_str());
- }
- } else {
- Row.getRegisterLocations().setRegisterLocation(
- AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3));
- }
- break;
- }
-
- case dwarf::DW_CFA_undefined: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- Row.getRegisterLocations().setRegisterLocation(
- *RegNum, UnwindLocation::createUndefined());
- break;
- }
-
- case dwarf::DW_CFA_same_value: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- Row.getRegisterLocations().setRegisterLocation(
- *RegNum, UnwindLocation::createSame());
- break;
- }
-
- case dwarf::DW_CFA_GNU_args_size:
- break;
-
- case dwarf::DW_CFA_register: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- llvm::Expected<uint64_t> NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1);
- if (!NewRegNum)
- return NewRegNum.takeError();
- Row.getRegisterLocations().setRegisterLocation(
- *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0));
- break;
- }
-
- case dwarf::DW_CFA_val_offset:
- case dwarf::DW_CFA_val_offset_sf: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
- if (!Offset)
- return Offset.takeError();
- Row.getRegisterLocations().setRegisterLocation(
- *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset));
- break;
- }
-
- case dwarf::DW_CFA_expression: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- Row.getRegisterLocations().setRegisterLocation(
- *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression));
- break;
- }
-
- case dwarf::DW_CFA_val_expression: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- Row.getRegisterLocations().setRegisterLocation(
- *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression));
- break;
- }
-
- case dwarf::DW_CFA_def_cfa_register: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset)
- Row.getCFAValue() =
- UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0);
- else
- Row.getCFAValue().setRegister(*RegNum);
- break;
- }
-
- case dwarf::DW_CFA_def_cfa_offset:
- case dwarf::DW_CFA_def_cfa_offset_sf: {
- llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 0);
- if (!Offset)
- return Offset.takeError();
- if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) {
- return createStringError(
- errc::invalid_argument,
- "%s found when CFA rule was not RegPlusOffset",
- CFIP.callFrameString(Inst.Opcode).str().c_str());
- }
- Row.getCFAValue().setOffset(*Offset);
- break;
- }
-
- case dwarf::DW_CFA_def_cfa:
- case dwarf::DW_CFA_def_cfa_sf: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
- if (!Offset)
- return Offset.takeError();
- Row.getCFAValue() =
- UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset);
- break;
- }
-
- case dwarf::DW_CFA_LLVM_def_aspace_cfa:
- case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: {
- llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
- if (!RegNum)
- return RegNum.takeError();
- llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
- if (!Offset)
- return Offset.takeError();
- llvm::Expected<uint32_t> CFAAddrSpace =
- Inst.getOperandAsUnsigned(CFIP, 2);
- if (!CFAAddrSpace)
- return CFAAddrSpace.takeError();
- Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset(
- *RegNum, *Offset, *CFAAddrSpace);
- break;
- }
-
- case dwarf::DW_CFA_def_cfa_expression:
- Row.getCFAValue() =
- UnwindLocation::createIsDWARFExpression(*Inst.Expression);
- break;
- }
- }
- return Error::success();
-}
-
// Returns the CIE identifier to be used by the requested format.
// CIE ids for .debug_frame sections are defined in Section 7.24 of DWARFv5.
// For CIE ID in .eh_frame sections see
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
new file mode 100644
index 0000000000000..330eed900221e
--- /dev/null
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
@@ -0,0 +1,500 @@
+//=== DWARFUnwindTable.cpp - Parsing CFI instructions into unwinding table ===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cinttypes>
+#include <cstdint>
+#include <optional>
+
+using namespace llvm;
+using namespace dwarf;
+
+static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts,
+ unsigned RegNum) {
+ if (DumpOpts.GetNameForDWARFReg) {
+ auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH);
+ if (!RegName.empty()) {
+ OS << RegName;
+ return;
+ }
+ }
+ OS << "reg" << RegNum;
+}
+
+UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; }
+
+UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; }
+
+UnwindLocation UnwindLocation::createSame() { return {Same}; }
+
+UnwindLocation UnwindLocation::createIsConstant(int32_t Value) {
+ return {Constant, InvalidRegisterNumber, Value, std::nullopt, false};
+}
+
+UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) {
+ return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false};
+}
+
+UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) {
+ return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true};
+}
+
+UnwindLocation
+UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
+ std::optional<uint32_t> AddrSpace) {
+ return {RegPlusOffset, RegNum, Offset, AddrSpace, false};
+}
+
+UnwindLocation
+UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
+ std::optional<uint32_t> AddrSpace) {
+ return {RegPlusOffset, RegNum, Offset, AddrSpace, true};
+}
+
+UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) {
+ return {Expr, false};
+}
+
+UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) {
+ return {Expr, true};
+}
+
+void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
+ if (Dereference)
+ OS << '[';
+ switch (Kind) {
+ case Unspecified:
+ OS << "unspecified";
+ break;
+ case Undefined:
+ OS << "undefined";
+ break;
+ case Same:
+ OS << "same";
+ break;
+ case CFAPlusOffset:
+ OS << "CFA";
+ if (Offset == 0)
+ break;
+ if (Offset > 0)
+ OS << "+";
+ OS << Offset;
+ break;
+ case RegPlusOffset:
+ printRegister(OS, DumpOpts, RegNum);
+ if (Offset == 0 && !AddrSpace)
+ break;
+ if (Offset >= 0)
+ OS << "+";
+ OS << Offset;
+ if (AddrSpace)
+ OS << " in addrspace" << *AddrSpace;
+ break;
+ case DWARFExpr: {
+ Expr->print(OS, DumpOpts, nullptr);
+ break;
+ }
+ case Constant:
+ OS << Offset;
+ break;
+ }
+ if (Dereference)
+ OS << ']';
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
+ const UnwindLocation &UL) {
+ auto DumpOpts = DIDumpOptions();
+ UL.dump(OS, DumpOpts);
+ return OS;
+}
+
+bool UnwindLocation::operator==(const UnwindLocation &RHS) const {
+ if (Kind != RHS.Kind)
+ return false;
+ switch (Kind) {
+ case Unspecified:
+ case Undefined:
+ case Same:
+ return true;
+ case CFAPlusOffset:
+ return Offset == RHS.Offset && Dereference == RHS.Dereference;
+ case RegPlusOffset:
+ return RegNum == RHS.RegNum && Offset == RHS.Offset &&
+ Dereference == RHS.Dereference;
+ case DWARFExpr:
+ return *Expr == *RHS.Expr && Dereference == RHS.Dereference;
+ case Constant:
+ return Offset == RHS.Offset;
+ }
+ return false;
+}
+
+void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
+ bool First = true;
+ for (const auto &RegLocPair : Locations) {
+ if (First)
+ First = false;
+ else
+ OS << ", ";
+ printRegister(OS, DumpOpts, RegLocPair.first);
+ OS << '=';
+ RegLocPair.second.dump(OS, DumpOpts);
+ }
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
+ const RegisterLocations &RL) {
+ auto DumpOpts = DIDumpOptions();
+ RL.dump(OS, DumpOpts);
+ return OS;
+}
+
+void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+ unsigned IndentLevel) const {
+ OS.indent(2 * IndentLevel);
+ if (hasAddress())
+ OS << format("0x%" PRIx64 ": ", *Address);
+ OS << "CFA=";
+ CFAValue.dump(OS, DumpOpts);
+ if (RegLocs.hasLocations()) {
+ OS << ": ";
+ RegLocs.dump(OS, DumpOpts);
+ }
+ OS << "\n";
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) {
+ auto DumpOpts = DIDumpOptions();
+ Row.dump(OS, DumpOpts, 0);
+ return OS;
+}
+
+void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+ unsigned IndentLevel) const {
+ for (const UnwindRow &Row : Rows)
+ Row.dump(OS, DumpOpts, IndentLevel);
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) {
+ auto DumpOpts = DIDumpOptions();
+ Rows.dump(OS, DumpOpts, 0);
+ return OS;
+}
+
+Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
+ const RegisterLocations *InitialLocs) {
+ // State consists of CFA value and register locations.
+ std::vector<std::pair<UnwindLocation, RegisterLocations>> States;
+ for (const CFIProgram::Instruction &Inst : CFIP) {
+ switch (Inst.Opcode) {
+ case dwarf::DW_CFA_set_loc: {
+ // The DW_CFA_set_loc instruction takes a single operand that
+ // represents a target address. The required action is to create a new
+ // table row using the specified address as the location. All other
+ // values in the new row are initially identical to the current row.
+ // The new location value is always greater than the current one. If
+ // the segment_size field of this FDE's CIE is non- zero, the initial
+ // location is preceded by a segment selector of the given length
+ llvm::Expected<uint64_t> NewAddress = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!NewAddress)
+ return NewAddress.takeError();
+ if (*NewAddress <= Row.getAddress())
+ return createStringError(
+ errc::invalid_argument,
+ "%s with adrress 0x%" PRIx64 " which must be greater than the "
+ "current row address 0x%" PRIx64,
+ CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress,
+ Row.getAddress());
+ Rows.push_back(Row);
+ Row.setAddress(*NewAddress);
+ break;
+ }
+
+ case dwarf::DW_CFA_advance_loc:
+ case dwarf::DW_CFA_advance_loc1:
+ case dwarf::DW_CFA_advance_loc2:
+ case dwarf::DW_CFA_advance_loc4: {
+ // The DW_CFA_advance instruction takes a single operand that
+ // represents a constant delta. The required action is to create a new
+ // table row with a location value that is computed by taking the
+ // current entry’s location value and adding the value of delta *
+ // code_alignment_factor. All other values in the new row are initially
+ // identical to the current row.
+ Rows.push_back(Row);
+ llvm::Expected<uint64_t> Offset = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!Offset)
+ return Offset.takeError();
+ Row.slideAddress(*Offset);
+ break;
+ }
+
+ case dwarf::DW_CFA_restore:
+ case dwarf::DW_CFA_restore_extended: {
+ // The DW_CFA_restore instruction takes a single operand (encoded with
+ // the opcode) that represents a register number. The required action
+ // is to change the rule for the indicated register to the rule
+ // assigned it by the initial_instructions in the CIE.
+ if (InitialLocs == nullptr)
+ return createStringError(
+ errc::invalid_argument, "%s encountered while parsing a CIE",
+ CFIP.callFrameString(Inst.Opcode).str().c_str());
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ if (std::optional<UnwindLocation> O =
+ InitialLocs->getRegisterLocation(*RegNum))
+ Row.getRegisterLocations().setRegisterLocation(*RegNum, *O);
+ else
+ Row.getRegisterLocations().removeRegisterLocation(*RegNum);
+ break;
+ }
+
+ case dwarf::DW_CFA_offset:
+ case dwarf::DW_CFA_offset_extended:
+ case dwarf::DW_CFA_offset_extended_sf: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
+ if (!Offset)
+ return Offset.takeError();
+ Row.getRegisterLocations().setRegisterLocation(
+ *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset));
+ break;
+ }
+
+ case dwarf::DW_CFA_nop:
+ break;
+
+ case dwarf::DW_CFA_remember_state:
+ States.push_back(
+ std::make_pair(Row.getCFAValue(), Row.getRegisterLocations()));
+ break;
+
+ case dwarf::DW_CFA_restore_state:
+ if (States.empty())
+ return createStringError(errc::invalid_argument,
+ "DW_CFA_restore_state without a matching "
+ "previous DW_CFA_remember_state");
+ Row.getCFAValue() = States.back().first;
+ Row.getRegisterLocations() = States.back().second;
+ States.pop_back();
+ break;
+
+ case dwarf::DW_CFA_GNU_window_save:
+ switch (CFIP.triple()) {
+ case Triple::aarch64:
+ case Triple::aarch64_be:
+ case Triple::aarch64_32: {
+ // DW_CFA_GNU_window_save is used for different things on different
+ // architectures. For aarch64 it is known as
+ // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the
+ // value of the return address state between 1 and 0. If there is
+ // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it
+ // should be initially set to 1.
+ constexpr uint32_t AArch64DWARFPAuthRaState = 34;
+ auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
+ AArch64DWARFPAuthRaState);
+ if (LRLoc) {
+ if (LRLoc->getLocation() == UnwindLocation::Constant) {
+ // Toggle the constant value from 0 to 1 or 1 to 0.
+ LRLoc->setConstant(LRLoc->getConstant() ^ 1);
+ Row.getRegisterLocations().setRegisterLocation(
+ AArch64DWARFPAuthRaState, *LRLoc);
+ } else {
+ return createStringError(
+ errc::invalid_argument,
+ "%s encountered when existing rule for this register is not "
+ "a constant",
+ CFIP.callFrameString(Inst.Opcode).str().c_str());
+ }
+ } else {
+ Row.getRegisterLocations().setRegisterLocation(
+ AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1));
+ }
+ break;
+ }
+
+ case Triple::sparc:
+ case Triple::sparcv9:
+ case Triple::sparcel:
+ for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) {
+ Row.getRegisterLocations().setRegisterLocation(
+ RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8));
+ }
+ break;
+
+ default: {
+ return createStringError(
+ errc::not_supported,
+ "DW_CFA opcode %#x is not supported for architecture %s",
+ Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str());
+
+ break;
+ }
+ }
+ break;
+
+ case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: {
+ constexpr uint32_t AArch64DWARFPAuthRaState = 34;
+ auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
+ AArch64DWARFPAuthRaState);
+ if (LRLoc) {
+ if (LRLoc->getLocation() == UnwindLocation::Constant) {
+ // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0.
+ LRLoc->setConstant(LRLoc->getConstant() ^ 0x3);
+ } else {
+ return createStringError(
+ errc::invalid_argument,
+ "%s encountered when existing rule for this register is not "
+ "a constant",
+ CFIP.callFrameString(Inst.Opcode).str().c_str());
+ }
+ } else {
+ Row.getRegisterLocations().setRegisterLocation(
+ AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3));
+ }
+ break;
+ }
+
+ case dwarf::DW_CFA_undefined: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ Row.getRegisterLocations().setRegisterLocation(
+ *RegNum, UnwindLocation::createUndefined());
+ break;
+ }
+
+ case dwarf::DW_CFA_same_value: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ Row.getRegisterLocations().setRegisterLocation(
+ *RegNum, UnwindLocation::createSame());
+ break;
+ }
+
+ case dwarf::DW_CFA_GNU_args_size:
+ break;
+
+ case dwarf::DW_CFA_register: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ llvm::Expected<uint64_t> NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1);
+ if (!NewRegNum)
+ return NewRegNum.takeError();
+ Row.getRegisterLocations().setRegisterLocation(
+ *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0));
+ break;
+ }
+
+ case dwarf::DW_CFA_val_offset:
+ case dwarf::DW_CFA_val_offset_sf: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
+ if (!Offset)
+ return Offset.takeError();
+ Row.getRegisterLocations().setRegisterLocation(
+ *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset));
+ break;
+ }
+
+ case dwarf::DW_CFA_expression: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ Row.getRegisterLocations().setRegisterLocation(
+ *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression));
+ break;
+ }
+
+ case dwarf::DW_CFA_val_expression: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ Row.getRegisterLocations().setRegisterLocation(
+ *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression));
+ break;
+ }
+
+ case dwarf::DW_CFA_def_cfa_register: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset)
+ Row.getCFAValue() =
+ UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0);
+ else
+ Row.getCFAValue().setRegister(*RegNum);
+ break;
+ }
+
+ case dwarf::DW_CFA_def_cfa_offset:
+ case dwarf::DW_CFA_def_cfa_offset_sf: {
+ llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 0);
+ if (!Offset)
+ return Offset.takeError();
+ if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) {
+ return createStringError(
+ errc::invalid_argument,
+ "%s found when CFA rule was not RegPlusOffset",
+ CFIP.callFrameString(Inst.Opcode).str().c_str());
+ }
+ Row.getCFAValue().setOffset(*Offset);
+ break;
+ }
+
+ case dwarf::DW_CFA_def_cfa:
+ case dwarf::DW_CFA_def_cfa_sf: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
+ if (!Offset)
+ return Offset.takeError();
+ Row.getCFAValue() =
+ UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset);
+ break;
+ }
+
+ case dwarf::DW_CFA_LLVM_def_aspace_cfa:
+ case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: {
+ llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+ if (!RegNum)
+ return RegNum.takeError();
+ llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
+ if (!Offset)
+ return Offset.takeError();
+ llvm::Expected<uint32_t> CFAAddrSpace =
+ Inst.getOperandAsUnsigned(CFIP, 2);
+ if (!CFAAddrSpace)
+ return CFAAddrSpace.takeError();
+ Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset(
+ *RegNum, *Offset, *CFAAddrSpace);
+ break;
+ }
+
+ case dwarf::DW_CFA_def_cfa_expression:
+ Row.getCFAValue() =
+ UnwindLocation::createIsDWARFExpression(*Inst.Expression);
+ break;
+ }
+ }
+ return Error::success();
+}
>From 80ac83c369506f733f0461ead993849551ca5fbd Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Tue, 17 Jun 2025 21:26:16 +0000
Subject: [PATCH 5/6] Annotate UnwindTable exported API with LLVM_ABI
---
.../llvm/DebugInfo/DWARF/DWARFUnwindTable.h | 47 ++++++++++---------
1 file changed, 24 insertions(+), 23 deletions(-)
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
index 7a36b1630e7e1..e91c5175a3e88 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
@@ -11,6 +11,7 @@
#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/Error.h"
#include <map>
#include <vector>
@@ -82,12 +83,12 @@ class UnwindLocation {
/// Create a location whose rule is set to Unspecified. This means the
/// register value might be in the same register but it wasn't specified in
/// the unwind opcodes.
- static UnwindLocation createUnspecified();
+ LLVM_ABI static UnwindLocation createUnspecified();
/// Create a location where the value is undefined and not available. This can
/// happen when a register is volatile and can't be recovered.
- static UnwindLocation createUndefined();
+ LLVM_ABI static UnwindLocation createUndefined();
/// Create a location where the value is known to be in the register itself.
- static UnwindLocation createSame();
+ LLVM_ABI static UnwindLocation createSame();
/// Create a location that is in (Deref == false) or at (Deref == true) the
/// CFA plus an offset. Most registers that are spilled onto the stack use
/// this rule. The rule for the register will use this rule and specify a
@@ -95,8 +96,8 @@ class UnwindLocation {
/// relative to a CFA value which is typically defined using the register
/// plus offset location. \see createRegisterPlusOffset(...) for more
/// information.
- static UnwindLocation createIsCFAPlusOffset(int32_t Off);
- static UnwindLocation createAtCFAPlusOffset(int32_t Off);
+ LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off);
+ LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off);
/// Create a location where the saved value is in (Deref == false) or at
/// (Deref == true) a regiser plus an offset and, optionally, in the specified
/// address space (used mostly for the CFA).
@@ -105,18 +106,18 @@ class UnwindLocation {
/// frame pointer as the register, with an offset that accounts for all
/// spilled registers and all local variables in a function, and Deref ==
/// false.
- static UnwindLocation
+ LLVM_ABI static UnwindLocation
createIsRegisterPlusOffset(uint32_t Reg, int32_t Off,
std::optional<uint32_t> AddrSpace = std::nullopt);
- static UnwindLocation
+ LLVM_ABI static UnwindLocation
createAtRegisterPlusOffset(uint32_t Reg, int32_t Off,
std::optional<uint32_t> AddrSpace = std::nullopt);
/// Create a location whose value is the result of evaluating a DWARF
/// expression. This allows complex expressions to be evaluated in order to
/// unwind a register or CFA value.
- static UnwindLocation createIsDWARFExpression(DWARFExpression Expr);
- static UnwindLocation createAtDWARFExpression(DWARFExpression Expr);
- static UnwindLocation createIsConstant(int32_t Value);
+ LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr);
+ LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr);
+ LLVM_ABI static UnwindLocation createIsConstant(int32_t Value);
Location getLocation() const { return Kind; }
uint32_t getRegister() const { return RegNum; }
@@ -154,12 +155,12 @@ class UnwindLocation {
/// instead of from .debug_frame. This is needed for register number
/// conversion because some register numbers differ between the two sections
/// for certain architectures like x86.
- void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
+ LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
- bool operator==(const UnwindLocation &RHS) const;
+ LLVM_ABI bool operator==(const UnwindLocation &RHS) const;
};
-raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
/// A class that can track all registers with locations in a UnwindRow object.
///
@@ -212,7 +213,7 @@ class RegisterLocations {
/// instead of from .debug_frame. This is needed for register number
/// conversion because some register numbers differ between the two sections
/// for certain architectures like x86.
- void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
+ LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
/// Returns true if we have any register locations in this object.
bool hasLocations() const { return !Locations.empty(); }
@@ -224,7 +225,7 @@ class RegisterLocations {
}
};
-raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
/// A class that represents a single row in the unwind table that is decoded by
/// parsing the DWARF Call Frame Information opcodes.
@@ -293,11 +294,11 @@ class UnwindRow {
///
/// \param IndentLevel specify the indent level as an integer. The UnwindRow
/// will be output to the stream preceded by 2 * IndentLevel number of spaces.
- void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
- unsigned IndentLevel = 0) const;
+ LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+ unsigned IndentLevel = 0) const;
};
-raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
/// A class that contains all UnwindRow objects for an FDE or a single unwind
/// row for a CIE. To unwind an address the rows, which are sorted by start
@@ -340,8 +341,8 @@ class UnwindTable {
///
/// \param IndentLevel specify the indent level as an integer. The UnwindRow
/// will be output to the stream preceded by 2 * IndentLevel number of spaces.
- void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
- unsigned IndentLevel = 0) const;
+ LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+ unsigned IndentLevel = 0) const;
/// Parse the information in the CFIProgram and update the CurrRow object
/// that the state machine describes.
@@ -358,8 +359,8 @@ class UnwindTable {
/// the initial register locations from the CIE. If NULL, then a CIE's
/// opcodes are being parsed and this is not needed. This is used for the
/// DW_CFA_restore and DW_CFA_restore_extended opcodes.
- Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
- const RegisterLocations *InitialLocs);
+ LLVM_ABI Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
+ const RegisterLocations *InitialLocs);
private:
RowContainer Rows;
@@ -368,7 +369,7 @@ class UnwindTable {
std::optional<uint64_t> EndAddress;
};
-raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
} // end namespace dwarf
>From 8a4f86149fbf6a98acb2d8e1a3e26b7a751cf6c5 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Tue, 17 Jun 2025 21:33:01 +0000
Subject: [PATCH 6/6] Annotate exported API of DebugFrame with LLVM_ABI
---
llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index 1ea3700690edd..4d2e6fd6bb4e0 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -14,6 +14,7 @@
#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/Error.h"
#include "llvm/TargetParser/Triple.h"
#include <memory>
@@ -38,7 +39,7 @@ class CIE;
///
/// \returns An error if the DWARF Call Frame Information opcodes have state
/// machine errors, or a valid UnwindTable otherwise.
-Expected<UnwindTable> createUnwindTable(const CIE *Cie);
+LLVM_ABI Expected<UnwindTable> createUnwindTable(const CIE *Cie);
class FDE;
@@ -50,7 +51,7 @@ class FDE;
///
/// \returns An error if the DWARF Call Frame Information opcodes have state
/// machine errors, or a valid UnwindTable otherwise.
-Expected<UnwindTable> createUnwindTable(const FDE *Fde);
+LLVM_ABI Expected<UnwindTable> createUnwindTable(const FDE *Fde);
/// An entry in either debug_frame or eh_frame. This entry can be a CIE or an
/// FDE.
More information about the llvm-commits
mailing list