[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:13:59 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/4] 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/4] 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/4] 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/4] 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();
+}



More information about the llvm-commits mailing list