[llvm] [NFC] Separate UnwindTable from DebugFrame into a different type (PR #142521)

AmirHossein PashaeeHir via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 5 21:20: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 01/16] 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 02/16] 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 03/16] 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 04/16] Separate UnwindTable from DWARF Debug Frame

---
 .../llvm/DebugInfo/DWARF/DWARFDebugFrame.h    | 380 +------------
 .../llvm/DebugInfo/DWARF/DWARFUnwindTable.h   | 377 +++++++++++++
 llvm/lib/DebugInfo/DWARF/CMakeLists.txt       |   1 +
 llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp  | 480 -----------------
 llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp | 500 ++++++++++++++++++
 5 files changed, 879 insertions(+), 859 deletions(-)
 create mode 100644 llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
 create mode 100644 llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index c47d6f137ca3c..1ea3700690edd 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -9,15 +9,13 @@
 #ifndef LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H
 #define LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H
 
-#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/iterator.h"
 #include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
-#include "llvm/Support/Compiler.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
 #include "llvm/Support/Error.h"
 #include "llvm/TargetParser/Triple.h"
-#include <map>
 #include <memory>
 #include <vector>
 
@@ -30,382 +28,6 @@ struct DIDumpOptions;
 
 namespace dwarf {
 
-constexpr uint32_t InvalidRegisterNumber = UINT32_MAX;
-
-/// A class that represents a location for the Call Frame Address (CFA) or a
-/// register. This is decoded from the DWARF Call Frame Information
-/// instructions and put into an UnwindRow.
-class UnwindLocation {
-public:
-  enum Location {
-    /// Not specified.
-    Unspecified,
-    /// Register is not available and can't be recovered.
-    Undefined,
-    /// Register value is in the register, nothing needs to be done to unwind
-    /// it:
-    ///   reg = reg
-    Same,
-    /// Register is in or at the CFA plus an offset:
-    ///   reg = CFA + offset
-    ///   reg = defef(CFA + offset)
-    CFAPlusOffset,
-    /// Register or CFA is in or at a register plus offset, optionally in
-    /// an address space:
-    ///   reg = reg + offset [in addrspace]
-    ///   reg = deref(reg + offset [in addrspace])
-    RegPlusOffset,
-    /// Register or CFA value is in or at a value found by evaluating a DWARF
-    /// expression:
-    ///   reg = eval(dwarf_expr)
-    ///   reg = deref(eval(dwarf_expr))
-    DWARFExpr,
-    /// Value is a constant value contained in "Offset":
-    ///   reg = Offset
-    Constant,
-  };
-
-private:
-  Location Kind;   /// The type of the location that describes how to unwind it.
-  uint32_t RegNum; /// The register number for Kind == RegPlusOffset.
-  int32_t Offset;  /// The offset for Kind == CFAPlusOffset or RegPlusOffset.
-  std::optional<uint32_t> AddrSpace;   /// The address space for Kind ==
-                                       /// RegPlusOffset for CFA.
-  std::optional<DWARFExpression> Expr; /// The DWARF expression for Kind ==
-                                       /// DWARFExpression.
-  bool Dereference; /// If true, the resulting location must be dereferenced
-                    /// after the location value is computed.
-
-  // Constructors are private to force people to use the create static
-  // functions.
-  UnwindLocation(Location K)
-      : Kind(K), RegNum(InvalidRegisterNumber), Offset(0),
-        AddrSpace(std::nullopt), Dereference(false) {}
-
-  UnwindLocation(Location K, uint32_t Reg, int32_t Off,
-                 std::optional<uint32_t> AS, bool Deref)
-      : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {}
-
-  UnwindLocation(DWARFExpression E, bool Deref)
-      : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E),
-        Dereference(Deref) {}
-
-public:
-  /// Create a location whose rule is set to Unspecified. This means the
-  /// register value might be in the same register but it wasn't specified in
-  /// the unwind opcodes.
-  LLVM_ABI static UnwindLocation createUnspecified();
-  /// Create a location where the value is undefined and not available. This can
-  /// happen when a register is volatile and can't be recovered.
-  LLVM_ABI static UnwindLocation createUndefined();
-  /// Create a location where the value is known to be in the register itself.
-  LLVM_ABI static UnwindLocation createSame();
-  /// Create a location that is in (Deref == false) or at (Deref == true) the
-  /// CFA plus an offset. Most registers that are spilled onto the stack use
-  /// this rule. The rule for the register will use this rule and specify a
-  /// unique offset from the CFA with \a Deref set to true. This value will be
-  /// relative to a CFA value which is typically defined using the register
-  /// plus offset location. \see createRegisterPlusOffset(...) for more
-  /// information.
-  LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off);
-  LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off);
-  /// Create a location where the saved value is in (Deref == false) or at
-  /// (Deref == true) a regiser plus an offset and, optionally, in the specified
-  /// address space (used mostly for the CFA).
-  ///
-  /// The CFA is usually defined using this rule by using the stack pointer or
-  /// frame pointer as the register, with an offset that accounts for all
-  /// spilled registers and all local variables in a function, and Deref ==
-  /// false.
-  LLVM_ABI static UnwindLocation
-  createIsRegisterPlusOffset(uint32_t Reg, int32_t Off,
-                             std::optional<uint32_t> AddrSpace = std::nullopt);
-  LLVM_ABI static UnwindLocation
-  createAtRegisterPlusOffset(uint32_t Reg, int32_t Off,
-                             std::optional<uint32_t> AddrSpace = std::nullopt);
-  /// Create a location whose value is the result of evaluating a DWARF
-  /// expression. This allows complex expressions to be evaluated in order to
-  /// unwind a register or CFA value.
-  LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr);
-  LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr);
-  LLVM_ABI static UnwindLocation createIsConstant(int32_t Value);
-
-  Location getLocation() const { return Kind; }
-  uint32_t getRegister() const { return RegNum; }
-  int32_t getOffset() const { return Offset; }
-  uint32_t getAddressSpace() const {
-    assert(Kind == RegPlusOffset && AddrSpace);
-    return *AddrSpace;
-  }
-  int32_t getConstant() const { return Offset; }
-  /// Some opcodes will modify the CFA location's register only, so we need
-  /// to be able to modify the CFA register when evaluating DWARF Call Frame
-  /// Information opcodes.
-  void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; }
-  /// Some opcodes will modify the CFA location's offset only, so we need
-  /// to be able to modify the CFA offset when evaluating DWARF Call Frame
-  /// Information opcodes.
-  void setOffset(int32_t NewOffset) { Offset = NewOffset; }
-  /// Some opcodes modify a constant value and we need to be able to update
-  /// the constant value (DW_CFA_GNU_window_save which is also known as
-  // DW_CFA_AARCH64_negate_ra_state).
-  void setConstant(int32_t Value) { Offset = Value; }
-
-  std::optional<DWARFExpression> getDWARFExpressionBytes() const {
-    return Expr;
-  }
-  /// Dump a location expression as text and use the register information if
-  /// some is provided.
-  ///
-  /// \param OS the stream to use for output.
-  ///
-  /// \param MRI register information that helps emit register names insteead
-  /// of raw register numbers.
-  ///
-  /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
-  /// instead of from .debug_frame. This is needed for register number
-  /// conversion because some register numbers differ between the two sections
-  /// for certain architectures like x86.
-  LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
-
-  LLVM_ABI bool operator==(const UnwindLocation &RHS) const;
-};
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
-
-/// A class that can track all registers with locations in a UnwindRow object.
-///
-/// Register locations use a map where the key is the register number and the
-/// the value is a UnwindLocation.
-///
-/// The register maps are put into a class so that all register locations can
-/// be copied when parsing the unwind opcodes DW_CFA_remember_state and
-/// DW_CFA_restore_state.
-class RegisterLocations {
-  std::map<uint32_t, UnwindLocation> Locations;
-
-public:
-  /// Return the location for the register in \a RegNum if there is a location.
-  ///
-  /// \param RegNum the register number to find a location for.
-  ///
-  /// \returns A location if one is available for \a RegNum, or std::nullopt
-  /// otherwise.
-  std::optional<UnwindLocation> getRegisterLocation(uint32_t RegNum) const {
-    auto Pos = Locations.find(RegNum);
-    if (Pos == Locations.end())
-      return std::nullopt;
-    return Pos->second;
-  }
-
-  /// Set the location for the register in \a RegNum to \a Location.
-  ///
-  /// \param RegNum the register number to set the location for.
-  ///
-  /// \param Location the UnwindLocation that describes how to unwind the value.
-  void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) {
-    Locations.erase(RegNum);
-    Locations.insert(std::make_pair(RegNum, Location));
-  }
-
-  /// Removes any rule for the register in \a RegNum.
-  ///
-  /// \param RegNum the register number to remove the location for.
-  void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); }
-
-  /// Dump all registers + locations that are currently defined in this object.
-  ///
-  /// \param OS the stream to use for output.
-  ///
-  /// \param MRI register information that helps emit register names insteead
-  /// of raw register numbers.
-  ///
-  /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
-  /// instead of from .debug_frame. This is needed for register number
-  /// conversion because some register numbers differ between the two sections
-  /// for certain architectures like x86.
-  LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
-
-  /// Returns true if we have any register locations in this object.
-  bool hasLocations() const { return !Locations.empty(); }
-
-  size_t size() const { return Locations.size(); }
-
-  bool operator==(const RegisterLocations &RHS) const {
-    return Locations == RHS.Locations;
-  }
-};
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
-
-/// A class that represents a single row in the unwind table that is decoded by
-/// parsing the DWARF Call Frame Information opcodes.
-///
-/// The row consists of an optional address, the rule to unwind the CFA and all
-/// rules to unwind any registers. If the address doesn't have a value, this
-/// row represents the initial instructions for a CIE. If the address has a
-/// value the UnwindRow represents a row in the UnwindTable for a FDE. The
-/// address is the first address for which the CFA location and register rules
-/// are valid within a function.
-///
-/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame
-/// Information and UnwindRow objects are lazily populated and pushed onto a
-/// stack in the UnwindTable when evaluating this state machine. Accessors are
-/// needed for the address, CFA value, and register locations as the opcodes
-/// encode a state machine that produces a sorted array of UnwindRow objects
-/// \see UnwindTable.
-class UnwindRow {
-  /// The address will be valid when parsing the instructions in a FDE. If
-  /// invalid, this object represents the initial instructions of a CIE.
-  std::optional<uint64_t> Address; ///< Address for row in FDE, invalid for CIE.
-  UnwindLocation CFAValue;    ///< How to unwind the Call Frame Address (CFA).
-  RegisterLocations RegLocs;  ///< How to unwind all registers in this list.
-
-public:
-  UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {}
-
-  /// Returns true if the address is valid in this object.
-  bool hasAddress() const { return Address.has_value(); }
-
-  /// Get the address for this row.
-  ///
-  /// Clients should only call this function after verifying it has a valid
-  /// address with a call to \see hasAddress().
-  uint64_t getAddress() const { return *Address; }
-
-  /// Set the address for this UnwindRow.
-  ///
-  /// The address represents the first address for which the CFAValue and
-  /// RegLocs are valid within a function.
-  void setAddress(uint64_t Addr) { Address = Addr; }
-
-  /// Offset the address for this UnwindRow.
-  ///
-  /// The address represents the first address for which the CFAValue and
-  /// RegLocs are valid within a function. Clients must ensure that this object
-  /// already has an address (\see hasAddress()) prior to calling this
-  /// function.
-  void slideAddress(uint64_t Offset) { *Address += Offset; }
-  UnwindLocation &getCFAValue() { return CFAValue; }
-  const UnwindLocation &getCFAValue() const { return CFAValue; }
-  RegisterLocations &getRegisterLocations() { return RegLocs; }
-  const RegisterLocations &getRegisterLocations() const { return RegLocs; }
-
-  /// Dump the UnwindRow to the stream.
-  ///
-  /// \param OS the stream to use for output.
-  ///
-  /// \param MRI register information that helps emit register names insteead
-  /// of raw register numbers.
-  ///
-  /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
-  /// instead of from .debug_frame. This is needed for register number
-  /// conversion because some register numbers differ between the two sections
-  /// for certain architectures like x86.
-  ///
-  /// \param IndentLevel specify the indent level as an integer. The UnwindRow
-  /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
-  LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
-                     unsigned IndentLevel = 0) const;
-};
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
-
-/// A class that contains all UnwindRow objects for an FDE or a single unwind
-/// row for a CIE. To unwind an address the rows, which are sorted by start
-/// address, can be searched to find the UnwindRow with the lowest starting
-/// address that is greater than or equal to the address that is being looked
-/// up.
-class UnwindTable {
-public:
-  using RowContainer = std::vector<UnwindRow>;
-  using iterator = RowContainer::iterator;
-  using const_iterator = RowContainer::const_iterator;
-
-  size_t size() const { return Rows.size(); }
-  iterator begin() { return Rows.begin(); }
-  const_iterator begin() const { return Rows.begin(); }
-  iterator end() { return Rows.end(); }
-  const_iterator end() const { return Rows.end(); }
-  const UnwindRow &operator[](size_t Index) const {
-    assert(Index < size());
-    return Rows[Index];
-  }
-  void insertRow(const UnwindRow &Row) { Rows.push_back(Row); }
-
-  /// Set the last address that this unwinding table refers to.
-  ///
-  /// This is used when this table is created based on a FDE.
-  void setEndAddress(uint64_t Addr) { EndAddress = Addr; }
-
-  /// Dump the UnwindTable to the stream.
-  ///
-  /// \param OS the stream to use for output.
-  ///
-  /// \param MRI register information that helps emit register names insteead
-  /// of raw register numbers.
-  ///
-  /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
-  /// instead of from .debug_frame. This is needed for register number
-  /// conversion because some register numbers differ between the two sections
-  /// for certain architectures like x86.
-  ///
-  /// \param IndentLevel specify the indent level as an integer. The UnwindRow
-  /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
-  LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
-                     unsigned IndentLevel = 0) const;
-
-  /// Parse the information in the CFIProgram and update the CurrRow object
-  /// that the state machine describes.
-  ///
-  /// This is an internal implementation that emulates the state machine
-  /// described in the DWARF Call Frame Information opcodes and will push
-  /// CurrRow onto the Rows container when needed.
-  ///
-  /// \param CFIP the CFI program that contains the opcodes from a CIE or FDE.
-  ///
-  /// \param CurrRow the current row to modify while parsing the state machine.
-  ///
-  /// \param InitialLocs If non-NULL, we are parsing a FDE and this contains
-  /// the initial register locations from the CIE. If NULL, then a CIE's
-  /// opcodes are being parsed and this is not needed. This is used for the
-  /// DW_CFA_restore and DW_CFA_restore_extended opcodes.
-  Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
-                  const RegisterLocations *InitialLocs);
-
-private:
-  RowContainer Rows;
-  /// The end address when data is extracted from a FDE. This value will be
-  /// invalid when a UnwindTable is extracted from a CIE.
-  std::optional<uint64_t> EndAddress;
-};
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
-
-class CIE;
-
-/// Create an UnwindTable from a Common Information Entry (CIE).
-///
-/// \param Cie The Common Information Entry to extract the table from. The
-/// CFIProgram is retrieved from the \a Cie object and used to create the
-/// UnwindTable.
-///
-/// \returns An error if the DWARF Call Frame Information opcodes have state
-/// machine errors, or a valid UnwindTable otherwise.
-LLVM_ABI Expected<UnwindTable> createUnwindTable(const CIE *Cie);
-
-class FDE;
-
-/// Create an UnwindTable from a Frame Descriptor Entry (FDE).
-///
-/// \param Fde The Frame Descriptor Entry to extract the table from. The
-/// CFIProgram is retrieved from the \a Fde object and used to create the
-/// UnwindTable.
-///
-/// \returns An error if the DWARF Call Frame Information opcodes have state
-/// machine errors, or a valid UnwindTable otherwise.
-LLVM_ABI Expected<UnwindTable> createUnwindTable(const FDE *Fde);
-
 class CIE;
 
 /// Create an UnwindTable from a Common Information Entry (CIE).
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
new file mode 100644
index 0000000000000..7a36b1630e7e1
--- /dev/null
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
@@ -0,0 +1,377 @@
+//===- DWARFUnwindTable.h ----------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H
+#define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H
+
+#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
+#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <vector>
+
+namespace llvm {
+
+namespace dwarf {
+constexpr uint32_t InvalidRegisterNumber = UINT32_MAX;
+
+/// A class that represents a location for the Call Frame Address (CFA) or a
+/// register. This is decoded from the DWARF Call Frame Information
+/// instructions and put into an UnwindRow.
+class UnwindLocation {
+public:
+  enum Location {
+    /// Not specified.
+    Unspecified,
+    /// Register is not available and can't be recovered.
+    Undefined,
+    /// Register value is in the register, nothing needs to be done to unwind
+    /// it:
+    ///   reg = reg
+    Same,
+    /// Register is in or at the CFA plus an offset:
+    ///   reg = CFA + offset
+    ///   reg = defef(CFA + offset)
+    CFAPlusOffset,
+    /// Register or CFA is in or at a register plus offset, optionally in
+    /// an address space:
+    ///   reg = reg + offset [in addrspace]
+    ///   reg = deref(reg + offset [in addrspace])
+    RegPlusOffset,
+    /// Register or CFA value is in or at a value found by evaluating a DWARF
+    /// expression:
+    ///   reg = eval(dwarf_expr)
+    ///   reg = deref(eval(dwarf_expr))
+    DWARFExpr,
+    /// Value is a constant value contained in "Offset":
+    ///   reg = Offset
+    Constant,
+  };
+
+private:
+  Location Kind;   /// The type of the location that describes how to unwind it.
+  uint32_t RegNum; /// The register number for Kind == RegPlusOffset.
+  int32_t Offset;  /// The offset for Kind == CFAPlusOffset or RegPlusOffset.
+  std::optional<uint32_t> AddrSpace;   /// The address space for Kind ==
+                                       /// RegPlusOffset for CFA.
+  std::optional<DWARFExpression> Expr; /// The DWARF expression for Kind ==
+                                       /// DWARFExpression.
+  bool Dereference; /// If true, the resulting location must be dereferenced
+                    /// after the location value is computed.
+
+  // Constructors are private to force people to use the create static
+  // functions.
+  UnwindLocation(Location K)
+      : Kind(K), RegNum(InvalidRegisterNumber), Offset(0),
+        AddrSpace(std::nullopt), Dereference(false) {}
+
+  UnwindLocation(Location K, uint32_t Reg, int32_t Off,
+                 std::optional<uint32_t> AS, bool Deref)
+      : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {}
+
+  UnwindLocation(DWARFExpression E, bool Deref)
+      : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E),
+        Dereference(Deref) {}
+
+public:
+  /// Create a location whose rule is set to Unspecified. This means the
+  /// register value might be in the same register but it wasn't specified in
+  /// the unwind opcodes.
+  static UnwindLocation createUnspecified();
+  /// Create a location where the value is undefined and not available. This can
+  /// happen when a register is volatile and can't be recovered.
+  static UnwindLocation createUndefined();
+  /// Create a location where the value is known to be in the register itself.
+  static UnwindLocation createSame();
+  /// Create a location that is in (Deref == false) or at (Deref == true) the
+  /// CFA plus an offset. Most registers that are spilled onto the stack use
+  /// this rule. The rule for the register will use this rule and specify a
+  /// unique offset from the CFA with \a Deref set to true. This value will be
+  /// relative to a CFA value which is typically defined using the register
+  /// plus offset location. \see createRegisterPlusOffset(...) for more
+  /// information.
+  static UnwindLocation createIsCFAPlusOffset(int32_t Off);
+  static UnwindLocation createAtCFAPlusOffset(int32_t Off);
+  /// Create a location where the saved value is in (Deref == false) or at
+  /// (Deref == true) a regiser plus an offset and, optionally, in the specified
+  /// address space (used mostly for the CFA).
+  ///
+  /// The CFA is usually defined using this rule by using the stack pointer or
+  /// frame pointer as the register, with an offset that accounts for all
+  /// spilled registers and all local variables in a function, and Deref ==
+  /// false.
+  static UnwindLocation
+  createIsRegisterPlusOffset(uint32_t Reg, int32_t Off,
+                             std::optional<uint32_t> AddrSpace = std::nullopt);
+  static UnwindLocation
+  createAtRegisterPlusOffset(uint32_t Reg, int32_t Off,
+                             std::optional<uint32_t> AddrSpace = std::nullopt);
+  /// Create a location whose value is the result of evaluating a DWARF
+  /// expression. This allows complex expressions to be evaluated in order to
+  /// unwind a register or CFA value.
+  static UnwindLocation createIsDWARFExpression(DWARFExpression Expr);
+  static UnwindLocation createAtDWARFExpression(DWARFExpression Expr);
+  static UnwindLocation createIsConstant(int32_t Value);
+
+  Location getLocation() const { return Kind; }
+  uint32_t getRegister() const { return RegNum; }
+  int32_t getOffset() const { return Offset; }
+  uint32_t getAddressSpace() const {
+    assert(Kind == RegPlusOffset && AddrSpace);
+    return *AddrSpace;
+  }
+  int32_t getConstant() const { return Offset; }
+  /// Some opcodes will modify the CFA location's register only, so we need
+  /// to be able to modify the CFA register when evaluating DWARF Call Frame
+  /// Information opcodes.
+  void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; }
+  /// Some opcodes will modify the CFA location's offset only, so we need
+  /// to be able to modify the CFA offset when evaluating DWARF Call Frame
+  /// Information opcodes.
+  void setOffset(int32_t NewOffset) { Offset = NewOffset; }
+  /// Some opcodes modify a constant value and we need to be able to update
+  /// the constant value (DW_CFA_GNU_window_save which is also known as
+  // DW_CFA_AARCH64_negate_ra_state).
+  void setConstant(int32_t Value) { Offset = Value; }
+
+  std::optional<DWARFExpression> getDWARFExpressionBytes() const {
+    return Expr;
+  }
+  /// Dump a location expression as text and use the register information if
+  /// some is provided.
+  ///
+  /// \param OS the stream to use for output.
+  ///
+  /// \param MRI register information that helps emit register names insteead
+  /// of raw register numbers.
+  ///
+  /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
+  /// instead of from .debug_frame. This is needed for register number
+  /// conversion because some register numbers differ between the two sections
+  /// for certain architectures like x86.
+  void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
+
+  bool operator==(const UnwindLocation &RHS) const;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
+
+/// A class that can track all registers with locations in a UnwindRow object.
+///
+/// Register locations use a map where the key is the register number and the
+/// the value is a UnwindLocation.
+///
+/// The register maps are put into a class so that all register locations can
+/// be copied when parsing the unwind opcodes DW_CFA_remember_state and
+/// DW_CFA_restore_state.
+class RegisterLocations {
+  std::map<uint32_t, UnwindLocation> Locations;
+
+public:
+  /// Return the location for the register in \a RegNum if there is a location.
+  ///
+  /// \param RegNum the register number to find a location for.
+  ///
+  /// \returns A location if one is available for \a RegNum, or std::nullopt
+  /// otherwise.
+  std::optional<UnwindLocation> getRegisterLocation(uint32_t RegNum) const {
+    auto Pos = Locations.find(RegNum);
+    if (Pos == Locations.end())
+      return std::nullopt;
+    return Pos->second;
+  }
+
+  /// Set the location for the register in \a RegNum to \a Location.
+  ///
+  /// \param RegNum the register number to set the location for.
+  ///
+  /// \param Location the UnwindLocation that describes how to unwind the value.
+  void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) {
+    Locations.erase(RegNum);
+    Locations.insert(std::make_pair(RegNum, Location));
+  }
+
+  /// Removes any rule for the register in \a RegNum.
+  ///
+  /// \param RegNum the register number to remove the location for.
+  void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); }
+
+  /// Dump all registers + locations that are currently defined in this object.
+  ///
+  /// \param OS the stream to use for output.
+  ///
+  /// \param MRI register information that helps emit register names insteead
+  /// of raw register numbers.
+  ///
+  /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
+  /// instead of from .debug_frame. This is needed for register number
+  /// conversion because some register numbers differ between the two sections
+  /// for certain architectures like x86.
+  void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
+
+  /// Returns true if we have any register locations in this object.
+  bool hasLocations() const { return !Locations.empty(); }
+
+  size_t size() const { return Locations.size(); }
+
+  bool operator==(const RegisterLocations &RHS) const {
+    return Locations == RHS.Locations;
+  }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
+
+/// A class that represents a single row in the unwind table that is decoded by
+/// parsing the DWARF Call Frame Information opcodes.
+///
+/// The row consists of an optional address, the rule to unwind the CFA and all
+/// rules to unwind any registers. If the address doesn't have a value, this
+/// row represents the initial instructions for a CIE. If the address has a
+/// value the UnwindRow represents a row in the UnwindTable for a FDE. The
+/// address is the first address for which the CFA location and register rules
+/// are valid within a function.
+///
+/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame
+/// Information and UnwindRow objects are lazily populated and pushed onto a
+/// stack in the UnwindTable when evaluating this state machine. Accessors are
+/// needed for the address, CFA value, and register locations as the opcodes
+/// encode a state machine that produces a sorted array of UnwindRow objects
+/// \see UnwindTable.
+class UnwindRow {
+  /// The address will be valid when parsing the instructions in a FDE. If
+  /// invalid, this object represents the initial instructions of a CIE.
+  std::optional<uint64_t> Address; ///< Address for row in FDE, invalid for CIE.
+  UnwindLocation CFAValue;   ///< How to unwind the Call Frame Address (CFA).
+  RegisterLocations RegLocs; ///< How to unwind all registers in this list.
+
+public:
+  UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {}
+
+  /// Returns true if the address is valid in this object.
+  bool hasAddress() const { return Address.has_value(); }
+
+  /// Get the address for this row.
+  ///
+  /// Clients should only call this function after verifying it has a valid
+  /// address with a call to \see hasAddress().
+  uint64_t getAddress() const { return *Address; }
+
+  /// Set the address for this UnwindRow.
+  ///
+  /// The address represents the first address for which the CFAValue and
+  /// RegLocs are valid within a function.
+  void setAddress(uint64_t Addr) { Address = Addr; }
+
+  /// Offset the address for this UnwindRow.
+  ///
+  /// The address represents the first address for which the CFAValue and
+  /// RegLocs are valid within a function. Clients must ensure that this object
+  /// already has an address (\see hasAddress()) prior to calling this
+  /// function.
+  void slideAddress(uint64_t Offset) { *Address += Offset; }
+  UnwindLocation &getCFAValue() { return CFAValue; }
+  const UnwindLocation &getCFAValue() const { return CFAValue; }
+  RegisterLocations &getRegisterLocations() { return RegLocs; }
+  const RegisterLocations &getRegisterLocations() const { return RegLocs; }
+
+  /// Dump the UnwindRow to the stream.
+  ///
+  /// \param OS the stream to use for output.
+  ///
+  /// \param MRI register information that helps emit register names insteead
+  /// of raw register numbers.
+  ///
+  /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
+  /// instead of from .debug_frame. This is needed for register number
+  /// conversion because some register numbers differ between the two sections
+  /// for certain architectures like x86.
+  ///
+  /// \param IndentLevel specify the indent level as an integer. The UnwindRow
+  /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
+  void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+            unsigned IndentLevel = 0) const;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
+
+/// A class that contains all UnwindRow objects for an FDE or a single unwind
+/// row for a CIE. To unwind an address the rows, which are sorted by start
+/// address, can be searched to find the UnwindRow with the lowest starting
+/// address that is greater than or equal to the address that is being looked
+/// up.
+class UnwindTable {
+public:
+  using RowContainer = std::vector<UnwindRow>;
+  using iterator = RowContainer::iterator;
+  using const_iterator = RowContainer::const_iterator;
+
+  size_t size() const { return Rows.size(); }
+  iterator begin() { return Rows.begin(); }
+  const_iterator begin() const { return Rows.begin(); }
+  iterator end() { return Rows.end(); }
+  const_iterator end() const { return Rows.end(); }
+  const UnwindRow &operator[](size_t Index) const {
+    assert(Index < size());
+    return Rows[Index];
+  }
+  void insertRow(const UnwindRow &Row) { Rows.push_back(Row); }
+
+  /// Set the last address that this unwinding table refers to.
+  ///
+  /// This is used when this table is created based on a FDE.
+  void setEndAddress(uint64_t Addr) { EndAddress = Addr; }
+
+  /// Dump the UnwindTable to the stream.
+  ///
+  /// \param OS the stream to use for output.
+  ///
+  /// \param MRI register information that helps emit register names insteead
+  /// of raw register numbers.
+  ///
+  /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
+  /// instead of from .debug_frame. This is needed for register number
+  /// conversion because some register numbers differ between the two sections
+  /// for certain architectures like x86.
+  ///
+  /// \param IndentLevel specify the indent level as an integer. The UnwindRow
+  /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
+  void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+            unsigned IndentLevel = 0) const;
+
+  /// Parse the information in the CFIProgram and update the CurrRow object
+  /// that the state machine describes.
+  ///
+  /// This is an internal implementation that emulates the state machine
+  /// described in the DWARF Call Frame Information opcodes and will push
+  /// CurrRow onto the Rows container when needed.
+  ///
+  /// \param CFIP the CFI program that contains the opcodes from a CIE or FDE.
+  ///
+  /// \param CurrRow the current row to modify while parsing the state machine.
+  ///
+  /// \param InitialLocs If non-NULL, we are parsing a FDE and this contains
+  /// the initial register locations from the CIE. If NULL, then a CIE's
+  /// opcodes are being parsed and this is not needed. This is used for the
+  /// DW_CFA_restore and DW_CFA_restore_extended opcodes.
+  Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
+                  const RegisterLocations *InitialLocs);
+
+private:
+  RowContainer Rows;
+  /// The end address when data is extracted from a FDE. This value will be
+  /// invalid when a UnwindTable is extracted from a CIE.
+  std::optional<uint64_t> EndAddress;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
+
+} // end namespace dwarf
+
+} // end namespace llvm
+
+#endif
\ No newline at end of file
diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
index cc9734f9f22be..b9ff0d676c4c3 100644
--- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
+++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
@@ -26,6 +26,7 @@ add_llvm_component_library(LLVMDebugInfoDWARF
   DWARFTypeUnit.cpp
   DWARFUnitIndex.cpp
   DWARFUnit.cpp
+  DWARFUnwindTable.cpp
   DWARFVerifier.cpp
 
   ADDITIONAL_HEADER_DIRS
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
index cf42151bb84aa..e6c37203dd557 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
@@ -29,180 +29,6 @@
 using namespace llvm;
 using namespace dwarf;
 
-static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts,
-                          unsigned RegNum) {
-  if (DumpOpts.GetNameForDWARFReg) {
-    auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH);
-    if (!RegName.empty()) {
-      OS << RegName;
-      return;
-    }
-  }
-  OS << "reg" << RegNum;
-}
-
-UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; }
-
-UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; }
-
-UnwindLocation UnwindLocation::createSame() { return {Same}; }
-
-UnwindLocation UnwindLocation::createIsConstant(int32_t Value) {
-  return {Constant, InvalidRegisterNumber, Value, std::nullopt, false};
-}
-
-UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) {
-  return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false};
-}
-
-UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) {
-  return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true};
-}
-
-UnwindLocation
-UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
-                                           std::optional<uint32_t> AddrSpace) {
-  return {RegPlusOffset, RegNum, Offset, AddrSpace, false};
-}
-
-UnwindLocation
-UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
-                                           std::optional<uint32_t> AddrSpace) {
-  return {RegPlusOffset, RegNum, Offset, AddrSpace, true};
-}
-
-UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) {
-  return {Expr, false};
-}
-
-UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) {
-  return {Expr, true};
-}
-
-void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
-  if (Dereference)
-    OS << '[';
-  switch (Kind) {
-  case Unspecified:
-    OS << "unspecified";
-    break;
-  case Undefined:
-    OS << "undefined";
-    break;
-  case Same:
-    OS << "same";
-    break;
-  case CFAPlusOffset:
-    OS << "CFA";
-    if (Offset == 0)
-      break;
-    if (Offset > 0)
-      OS << "+";
-    OS << Offset;
-    break;
-  case RegPlusOffset:
-    printRegister(OS, DumpOpts, RegNum);
-    if (Offset == 0 && !AddrSpace)
-      break;
-    if (Offset >= 0)
-      OS << "+";
-    OS << Offset;
-    if (AddrSpace)
-      OS << " in addrspace" << *AddrSpace;
-    break;
-  case DWARFExpr: {
-    if (Expr)
-      DWARFExpressionPrinter::print(&(*Expr), OS, DumpOpts, nullptr);
-    break;
-  }
-  case Constant:
-    OS << Offset;
-    break;
-  }
-  if (Dereference)
-    OS << ']';
-}
-
-raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
-                                     const UnwindLocation &UL) {
-  auto DumpOpts = DIDumpOptions();
-  UL.dump(OS, DumpOpts);
-  return OS;
-}
-
-bool UnwindLocation::operator==(const UnwindLocation &RHS) const {
-  if (Kind != RHS.Kind)
-    return false;
-  switch (Kind) {
-  case Unspecified:
-  case Undefined:
-  case Same:
-    return true;
-  case CFAPlusOffset:
-    return Offset == RHS.Offset && Dereference == RHS.Dereference;
-  case RegPlusOffset:
-    return RegNum == RHS.RegNum && Offset == RHS.Offset &&
-           Dereference == RHS.Dereference;
-  case DWARFExpr:
-    return *Expr == *RHS.Expr && Dereference == RHS.Dereference;
-  case Constant:
-    return Offset == RHS.Offset;
-  }
-  return false;
-}
-
-void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
-  bool First = true;
-  for (const auto &RegLocPair : Locations) {
-    if (First)
-      First = false;
-    else
-      OS << ", ";
-    printRegister(OS, DumpOpts, RegLocPair.first);
-    OS << '=';
-    RegLocPair.second.dump(OS, DumpOpts);
-  }
-}
-
-raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
-                                     const RegisterLocations &RL) {
-  auto DumpOpts = DIDumpOptions();
-  RL.dump(OS, DumpOpts);
-  return OS;
-}
-
-void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
-                     unsigned IndentLevel) const {
-  OS.indent(2 * IndentLevel);
-  if (hasAddress())
-    OS << format("0x%" PRIx64 ": ", *Address);
-  OS << "CFA=";
-  CFAValue.dump(OS, DumpOpts);
-  if (RegLocs.hasLocations()) {
-    OS << ": ";
-    RegLocs.dump(OS, DumpOpts);
-  }
-  OS << "\n";
-}
-
-raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) {
-  auto DumpOpts = DIDumpOptions();
-  Row.dump(OS, DumpOpts, 0);
-  return OS;
-}
-
-void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
-                       unsigned IndentLevel) const {
-  for (const UnwindRow &Row : Rows)
-    Row.dump(OS, DumpOpts, IndentLevel);
-}
-
-raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) {
-  auto DumpOpts = DIDumpOptions();
-  Rows.dump(OS, DumpOpts, 0);
-  return OS;
-}
-
 Expected<UnwindTable> llvm::dwarf::createUnwindTable(const FDE *Fde) {
   const CIE *Cie = Fde->getLinkedCIE();
   if (Cie == nullptr)
@@ -250,312 +76,6 @@ Expected<UnwindTable> llvm::dwarf::createUnwindTable(const CIE *Cie) {
   return UT;
 }
 
-Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
-                             const RegisterLocations *InitialLocs) {
-  // State consists of CFA value and register locations.
-  std::vector<std::pair<UnwindLocation, RegisterLocations>> States;
-  for (const CFIProgram::Instruction &Inst : CFIP) {
-    switch (Inst.Opcode) {
-    case dwarf::DW_CFA_set_loc: {
-      // The DW_CFA_set_loc instruction takes a single operand that
-      // represents a target address. The required action is to create a new
-      // table row using the specified address as the location. All other
-      // values in the new row are initially identical to the current row.
-      // The new location value is always greater than the current one. If
-      // the segment_size field of this FDE's CIE is non- zero, the initial
-      // location is preceded by a segment selector of the given length
-      llvm::Expected<uint64_t> NewAddress = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!NewAddress)
-        return NewAddress.takeError();
-      if (*NewAddress <= Row.getAddress())
-        return createStringError(
-            errc::invalid_argument,
-            "%s with adrress 0x%" PRIx64 " which must be greater than the "
-            "current row address 0x%" PRIx64,
-            CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress,
-            Row.getAddress());
-      Rows.push_back(Row);
-      Row.setAddress(*NewAddress);
-      break;
-    }
-
-    case dwarf::DW_CFA_advance_loc:
-    case dwarf::DW_CFA_advance_loc1:
-    case dwarf::DW_CFA_advance_loc2:
-    case dwarf::DW_CFA_advance_loc4: {
-      // The DW_CFA_advance instruction takes a single operand that
-      // represents a constant delta. The required action is to create a new
-      // table row with a location value that is computed by taking the
-      // current entry’s location value and adding the value of delta *
-      // code_alignment_factor. All other values in the new row are initially
-      // identical to the current row.
-      Rows.push_back(Row);
-      llvm::Expected<uint64_t> Offset = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!Offset)
-        return Offset.takeError();
-      Row.slideAddress(*Offset);
-      break;
-    }
-
-    case dwarf::DW_CFA_restore:
-    case dwarf::DW_CFA_restore_extended: {
-      // The DW_CFA_restore instruction takes a single operand (encoded with
-      // the opcode) that represents a register number. The required action
-      // is to change the rule for the indicated register to the rule
-      // assigned it by the initial_instructions in the CIE.
-      if (InitialLocs == nullptr)
-        return createStringError(
-            errc::invalid_argument, "%s encountered while parsing a CIE",
-            CFIP.callFrameString(Inst.Opcode).str().c_str());
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      if (std::optional<UnwindLocation> O =
-              InitialLocs->getRegisterLocation(*RegNum))
-        Row.getRegisterLocations().setRegisterLocation(*RegNum, *O);
-      else
-        Row.getRegisterLocations().removeRegisterLocation(*RegNum);
-      break;
-    }
-
-    case dwarf::DW_CFA_offset:
-    case dwarf::DW_CFA_offset_extended:
-    case dwarf::DW_CFA_offset_extended_sf: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
-      if (!Offset)
-        return Offset.takeError();
-      Row.getRegisterLocations().setRegisterLocation(
-          *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset));
-      break;
-    }
-
-    case dwarf::DW_CFA_nop:
-      break;
-
-    case dwarf::DW_CFA_remember_state:
-      States.push_back(
-          std::make_pair(Row.getCFAValue(), Row.getRegisterLocations()));
-      break;
-
-    case dwarf::DW_CFA_restore_state:
-      if (States.empty())
-        return createStringError(errc::invalid_argument,
-                                 "DW_CFA_restore_state without a matching "
-                                 "previous DW_CFA_remember_state");
-      Row.getCFAValue() = States.back().first;
-      Row.getRegisterLocations() = States.back().second;
-      States.pop_back();
-      break;
-
-    case dwarf::DW_CFA_GNU_window_save:
-      switch (CFIP.triple()) {
-      case Triple::aarch64:
-      case Triple::aarch64_be:
-      case Triple::aarch64_32: {
-        // DW_CFA_GNU_window_save is used for different things on different
-        // architectures. For aarch64 it is known as
-        // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the
-        // value of the return address state between 1 and 0. If there is
-        // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it
-        // should be initially set to 1.
-        constexpr uint32_t AArch64DWARFPAuthRaState = 34;
-        auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
-            AArch64DWARFPAuthRaState);
-        if (LRLoc) {
-          if (LRLoc->getLocation() == UnwindLocation::Constant) {
-            // Toggle the constant value from 0 to 1 or 1 to 0.
-            LRLoc->setConstant(LRLoc->getConstant() ^ 1);
-            Row.getRegisterLocations().setRegisterLocation(
-                AArch64DWARFPAuthRaState, *LRLoc);
-          } else {
-            return createStringError(
-                errc::invalid_argument,
-                "%s encountered when existing rule for this register is not "
-                "a constant",
-                CFIP.callFrameString(Inst.Opcode).str().c_str());
-          }
-        } else {
-          Row.getRegisterLocations().setRegisterLocation(
-              AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1));
-        }
-        break;
-      }
-
-      case Triple::sparc:
-      case Triple::sparcv9:
-      case Triple::sparcel:
-        for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) {
-          Row.getRegisterLocations().setRegisterLocation(
-              RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8));
-        }
-        break;
-
-      default: {
-        return createStringError(
-            errc::not_supported,
-            "DW_CFA opcode %#x is not supported for architecture %s",
-            Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str());
-
-        break;
-      }
-      }
-      break;
-
-    case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: {
-      constexpr uint32_t AArch64DWARFPAuthRaState = 34;
-      auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
-          AArch64DWARFPAuthRaState);
-      if (LRLoc) {
-        if (LRLoc->getLocation() == UnwindLocation::Constant) {
-          // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0.
-          LRLoc->setConstant(LRLoc->getConstant() ^ 0x3);
-        } else {
-          return createStringError(
-              errc::invalid_argument,
-              "%s encountered when existing rule for this register is not "
-              "a constant",
-              CFIP.callFrameString(Inst.Opcode).str().c_str());
-        }
-      } else {
-        Row.getRegisterLocations().setRegisterLocation(
-            AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3));
-      }
-      break;
-    }
-
-    case dwarf::DW_CFA_undefined: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      Row.getRegisterLocations().setRegisterLocation(
-          *RegNum, UnwindLocation::createUndefined());
-      break;
-    }
-
-    case dwarf::DW_CFA_same_value: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      Row.getRegisterLocations().setRegisterLocation(
-          *RegNum, UnwindLocation::createSame());
-      break;
-    }
-
-    case dwarf::DW_CFA_GNU_args_size:
-      break;
-
-    case dwarf::DW_CFA_register: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      llvm::Expected<uint64_t> NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1);
-      if (!NewRegNum)
-        return NewRegNum.takeError();
-      Row.getRegisterLocations().setRegisterLocation(
-          *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0));
-      break;
-    }
-
-    case dwarf::DW_CFA_val_offset:
-    case dwarf::DW_CFA_val_offset_sf: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
-      if (!Offset)
-        return Offset.takeError();
-      Row.getRegisterLocations().setRegisterLocation(
-          *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset));
-      break;
-    }
-
-    case dwarf::DW_CFA_expression: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      Row.getRegisterLocations().setRegisterLocation(
-          *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression));
-      break;
-    }
-
-    case dwarf::DW_CFA_val_expression: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      Row.getRegisterLocations().setRegisterLocation(
-          *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression));
-      break;
-    }
-
-    case dwarf::DW_CFA_def_cfa_register: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset)
-        Row.getCFAValue() =
-            UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0);
-      else
-        Row.getCFAValue().setRegister(*RegNum);
-      break;
-    }
-
-    case dwarf::DW_CFA_def_cfa_offset:
-    case dwarf::DW_CFA_def_cfa_offset_sf: {
-      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 0);
-      if (!Offset)
-        return Offset.takeError();
-      if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) {
-        return createStringError(
-            errc::invalid_argument,
-            "%s found when CFA rule was not RegPlusOffset",
-            CFIP.callFrameString(Inst.Opcode).str().c_str());
-      }
-      Row.getCFAValue().setOffset(*Offset);
-      break;
-    }
-
-    case dwarf::DW_CFA_def_cfa:
-    case dwarf::DW_CFA_def_cfa_sf: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
-      if (!Offset)
-        return Offset.takeError();
-      Row.getCFAValue() =
-          UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset);
-      break;
-    }
-
-    case dwarf::DW_CFA_LLVM_def_aspace_cfa:
-    case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: {
-      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
-      if (!RegNum)
-        return RegNum.takeError();
-      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
-      if (!Offset)
-        return Offset.takeError();
-      llvm::Expected<uint32_t> CFAAddrSpace =
-          Inst.getOperandAsUnsigned(CFIP, 2);
-      if (!CFAAddrSpace)
-        return CFAAddrSpace.takeError();
-      Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset(
-          *RegNum, *Offset, *CFAAddrSpace);
-      break;
-    }
-
-    case dwarf::DW_CFA_def_cfa_expression:
-      Row.getCFAValue() =
-          UnwindLocation::createIsDWARFExpression(*Inst.Expression);
-      break;
-    }
-  }
-  return Error::success();
-}
-
 // Returns the CIE identifier to be used by the requested format.
 // CIE ids for .debug_frame sections are defined in Section 7.24 of DWARFv5.
 // For CIE ID in .eh_frame sections see
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
new file mode 100644
index 0000000000000..330eed900221e
--- /dev/null
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
@@ -0,0 +1,500 @@
+//=== DWARFUnwindTable.cpp - Parsing CFI instructions into unwinding table ===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cinttypes>
+#include <cstdint>
+#include <optional>
+
+using namespace llvm;
+using namespace dwarf;
+
+static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts,
+                          unsigned RegNum) {
+  if (DumpOpts.GetNameForDWARFReg) {
+    auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH);
+    if (!RegName.empty()) {
+      OS << RegName;
+      return;
+    }
+  }
+  OS << "reg" << RegNum;
+}
+
+UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; }
+
+UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; }
+
+UnwindLocation UnwindLocation::createSame() { return {Same}; }
+
+UnwindLocation UnwindLocation::createIsConstant(int32_t Value) {
+  return {Constant, InvalidRegisterNumber, Value, std::nullopt, false};
+}
+
+UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) {
+  return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false};
+}
+
+UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) {
+  return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true};
+}
+
+UnwindLocation
+UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
+                                           std::optional<uint32_t> AddrSpace) {
+  return {RegPlusOffset, RegNum, Offset, AddrSpace, false};
+}
+
+UnwindLocation
+UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
+                                           std::optional<uint32_t> AddrSpace) {
+  return {RegPlusOffset, RegNum, Offset, AddrSpace, true};
+}
+
+UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) {
+  return {Expr, false};
+}
+
+UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) {
+  return {Expr, true};
+}
+
+void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
+  if (Dereference)
+    OS << '[';
+  switch (Kind) {
+  case Unspecified:
+    OS << "unspecified";
+    break;
+  case Undefined:
+    OS << "undefined";
+    break;
+  case Same:
+    OS << "same";
+    break;
+  case CFAPlusOffset:
+    OS << "CFA";
+    if (Offset == 0)
+      break;
+    if (Offset > 0)
+      OS << "+";
+    OS << Offset;
+    break;
+  case RegPlusOffset:
+    printRegister(OS, DumpOpts, RegNum);
+    if (Offset == 0 && !AddrSpace)
+      break;
+    if (Offset >= 0)
+      OS << "+";
+    OS << Offset;
+    if (AddrSpace)
+      OS << " in addrspace" << *AddrSpace;
+    break;
+  case DWARFExpr: {
+    Expr->print(OS, DumpOpts, nullptr);
+    break;
+  }
+  case Constant:
+    OS << Offset;
+    break;
+  }
+  if (Dereference)
+    OS << ']';
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
+                                     const UnwindLocation &UL) {
+  auto DumpOpts = DIDumpOptions();
+  UL.dump(OS, DumpOpts);
+  return OS;
+}
+
+bool UnwindLocation::operator==(const UnwindLocation &RHS) const {
+  if (Kind != RHS.Kind)
+    return false;
+  switch (Kind) {
+  case Unspecified:
+  case Undefined:
+  case Same:
+    return true;
+  case CFAPlusOffset:
+    return Offset == RHS.Offset && Dereference == RHS.Dereference;
+  case RegPlusOffset:
+    return RegNum == RHS.RegNum && Offset == RHS.Offset &&
+           Dereference == RHS.Dereference;
+  case DWARFExpr:
+    return *Expr == *RHS.Expr && Dereference == RHS.Dereference;
+  case Constant:
+    return Offset == RHS.Offset;
+  }
+  return false;
+}
+
+void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
+  bool First = true;
+  for (const auto &RegLocPair : Locations) {
+    if (First)
+      First = false;
+    else
+      OS << ", ";
+    printRegister(OS, DumpOpts, RegLocPair.first);
+    OS << '=';
+    RegLocPair.second.dump(OS, DumpOpts);
+  }
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
+                                     const RegisterLocations &RL) {
+  auto DumpOpts = DIDumpOptions();
+  RL.dump(OS, DumpOpts);
+  return OS;
+}
+
+void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+                     unsigned IndentLevel) const {
+  OS.indent(2 * IndentLevel);
+  if (hasAddress())
+    OS << format("0x%" PRIx64 ": ", *Address);
+  OS << "CFA=";
+  CFAValue.dump(OS, DumpOpts);
+  if (RegLocs.hasLocations()) {
+    OS << ": ";
+    RegLocs.dump(OS, DumpOpts);
+  }
+  OS << "\n";
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) {
+  auto DumpOpts = DIDumpOptions();
+  Row.dump(OS, DumpOpts, 0);
+  return OS;
+}
+
+void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+                       unsigned IndentLevel) const {
+  for (const UnwindRow &Row : Rows)
+    Row.dump(OS, DumpOpts, IndentLevel);
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) {
+  auto DumpOpts = DIDumpOptions();
+  Rows.dump(OS, DumpOpts, 0);
+  return OS;
+}
+
+Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
+                             const RegisterLocations *InitialLocs) {
+  // State consists of CFA value and register locations.
+  std::vector<std::pair<UnwindLocation, RegisterLocations>> States;
+  for (const CFIProgram::Instruction &Inst : CFIP) {
+    switch (Inst.Opcode) {
+    case dwarf::DW_CFA_set_loc: {
+      // The DW_CFA_set_loc instruction takes a single operand that
+      // represents a target address. The required action is to create a new
+      // table row using the specified address as the location. All other
+      // values in the new row are initially identical to the current row.
+      // The new location value is always greater than the current one. If
+      // the segment_size field of this FDE's CIE is non- zero, the initial
+      // location is preceded by a segment selector of the given length
+      llvm::Expected<uint64_t> NewAddress = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!NewAddress)
+        return NewAddress.takeError();
+      if (*NewAddress <= Row.getAddress())
+        return createStringError(
+            errc::invalid_argument,
+            "%s with adrress 0x%" PRIx64 " which must be greater than the "
+            "current row address 0x%" PRIx64,
+            CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress,
+            Row.getAddress());
+      Rows.push_back(Row);
+      Row.setAddress(*NewAddress);
+      break;
+    }
+
+    case dwarf::DW_CFA_advance_loc:
+    case dwarf::DW_CFA_advance_loc1:
+    case dwarf::DW_CFA_advance_loc2:
+    case dwarf::DW_CFA_advance_loc4: {
+      // The DW_CFA_advance instruction takes a single operand that
+      // represents a constant delta. The required action is to create a new
+      // table row with a location value that is computed by taking the
+      // current entry’s location value and adding the value of delta *
+      // code_alignment_factor. All other values in the new row are initially
+      // identical to the current row.
+      Rows.push_back(Row);
+      llvm::Expected<uint64_t> Offset = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!Offset)
+        return Offset.takeError();
+      Row.slideAddress(*Offset);
+      break;
+    }
+
+    case dwarf::DW_CFA_restore:
+    case dwarf::DW_CFA_restore_extended: {
+      // The DW_CFA_restore instruction takes a single operand (encoded with
+      // the opcode) that represents a register number. The required action
+      // is to change the rule for the indicated register to the rule
+      // assigned it by the initial_instructions in the CIE.
+      if (InitialLocs == nullptr)
+        return createStringError(
+            errc::invalid_argument, "%s encountered while parsing a CIE",
+            CFIP.callFrameString(Inst.Opcode).str().c_str());
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      if (std::optional<UnwindLocation> O =
+              InitialLocs->getRegisterLocation(*RegNum))
+        Row.getRegisterLocations().setRegisterLocation(*RegNum, *O);
+      else
+        Row.getRegisterLocations().removeRegisterLocation(*RegNum);
+      break;
+    }
+
+    case dwarf::DW_CFA_offset:
+    case dwarf::DW_CFA_offset_extended:
+    case dwarf::DW_CFA_offset_extended_sf: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
+      if (!Offset)
+        return Offset.takeError();
+      Row.getRegisterLocations().setRegisterLocation(
+          *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset));
+      break;
+    }
+
+    case dwarf::DW_CFA_nop:
+      break;
+
+    case dwarf::DW_CFA_remember_state:
+      States.push_back(
+          std::make_pair(Row.getCFAValue(), Row.getRegisterLocations()));
+      break;
+
+    case dwarf::DW_CFA_restore_state:
+      if (States.empty())
+        return createStringError(errc::invalid_argument,
+                                 "DW_CFA_restore_state without a matching "
+                                 "previous DW_CFA_remember_state");
+      Row.getCFAValue() = States.back().first;
+      Row.getRegisterLocations() = States.back().second;
+      States.pop_back();
+      break;
+
+    case dwarf::DW_CFA_GNU_window_save:
+      switch (CFIP.triple()) {
+      case Triple::aarch64:
+      case Triple::aarch64_be:
+      case Triple::aarch64_32: {
+        // DW_CFA_GNU_window_save is used for different things on different
+        // architectures. For aarch64 it is known as
+        // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the
+        // value of the return address state between 1 and 0. If there is
+        // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it
+        // should be initially set to 1.
+        constexpr uint32_t AArch64DWARFPAuthRaState = 34;
+        auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
+            AArch64DWARFPAuthRaState);
+        if (LRLoc) {
+          if (LRLoc->getLocation() == UnwindLocation::Constant) {
+            // Toggle the constant value from 0 to 1 or 1 to 0.
+            LRLoc->setConstant(LRLoc->getConstant() ^ 1);
+            Row.getRegisterLocations().setRegisterLocation(
+                AArch64DWARFPAuthRaState, *LRLoc);
+          } else {
+            return createStringError(
+                errc::invalid_argument,
+                "%s encountered when existing rule for this register is not "
+                "a constant",
+                CFIP.callFrameString(Inst.Opcode).str().c_str());
+          }
+        } else {
+          Row.getRegisterLocations().setRegisterLocation(
+              AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1));
+        }
+        break;
+      }
+
+      case Triple::sparc:
+      case Triple::sparcv9:
+      case Triple::sparcel:
+        for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) {
+          Row.getRegisterLocations().setRegisterLocation(
+              RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8));
+        }
+        break;
+
+      default: {
+        return createStringError(
+            errc::not_supported,
+            "DW_CFA opcode %#x is not supported for architecture %s",
+            Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str());
+
+        break;
+      }
+      }
+      break;
+
+    case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: {
+      constexpr uint32_t AArch64DWARFPAuthRaState = 34;
+      auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
+          AArch64DWARFPAuthRaState);
+      if (LRLoc) {
+        if (LRLoc->getLocation() == UnwindLocation::Constant) {
+          // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0.
+          LRLoc->setConstant(LRLoc->getConstant() ^ 0x3);
+        } else {
+          return createStringError(
+              errc::invalid_argument,
+              "%s encountered when existing rule for this register is not "
+              "a constant",
+              CFIP.callFrameString(Inst.Opcode).str().c_str());
+        }
+      } else {
+        Row.getRegisterLocations().setRegisterLocation(
+            AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3));
+      }
+      break;
+    }
+
+    case dwarf::DW_CFA_undefined: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      Row.getRegisterLocations().setRegisterLocation(
+          *RegNum, UnwindLocation::createUndefined());
+      break;
+    }
+
+    case dwarf::DW_CFA_same_value: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      Row.getRegisterLocations().setRegisterLocation(
+          *RegNum, UnwindLocation::createSame());
+      break;
+    }
+
+    case dwarf::DW_CFA_GNU_args_size:
+      break;
+
+    case dwarf::DW_CFA_register: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      llvm::Expected<uint64_t> NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1);
+      if (!NewRegNum)
+        return NewRegNum.takeError();
+      Row.getRegisterLocations().setRegisterLocation(
+          *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0));
+      break;
+    }
+
+    case dwarf::DW_CFA_val_offset:
+    case dwarf::DW_CFA_val_offset_sf: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
+      if (!Offset)
+        return Offset.takeError();
+      Row.getRegisterLocations().setRegisterLocation(
+          *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset));
+      break;
+    }
+
+    case dwarf::DW_CFA_expression: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      Row.getRegisterLocations().setRegisterLocation(
+          *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression));
+      break;
+    }
+
+    case dwarf::DW_CFA_val_expression: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      Row.getRegisterLocations().setRegisterLocation(
+          *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression));
+      break;
+    }
+
+    case dwarf::DW_CFA_def_cfa_register: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset)
+        Row.getCFAValue() =
+            UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0);
+      else
+        Row.getCFAValue().setRegister(*RegNum);
+      break;
+    }
+
+    case dwarf::DW_CFA_def_cfa_offset:
+    case dwarf::DW_CFA_def_cfa_offset_sf: {
+      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 0);
+      if (!Offset)
+        return Offset.takeError();
+      if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) {
+        return createStringError(
+            errc::invalid_argument,
+            "%s found when CFA rule was not RegPlusOffset",
+            CFIP.callFrameString(Inst.Opcode).str().c_str());
+      }
+      Row.getCFAValue().setOffset(*Offset);
+      break;
+    }
+
+    case dwarf::DW_CFA_def_cfa:
+    case dwarf::DW_CFA_def_cfa_sf: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
+      if (!Offset)
+        return Offset.takeError();
+      Row.getCFAValue() =
+          UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset);
+      break;
+    }
+
+    case dwarf::DW_CFA_LLVM_def_aspace_cfa:
+    case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
+      if (!Offset)
+        return Offset.takeError();
+      llvm::Expected<uint32_t> CFAAddrSpace =
+          Inst.getOperandAsUnsigned(CFIP, 2);
+      if (!CFAAddrSpace)
+        return CFAAddrSpace.takeError();
+      Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset(
+          *RegNum, *Offset, *CFAAddrSpace);
+      break;
+    }
+
+    case dwarf::DW_CFA_def_cfa_expression:
+      Row.getCFAValue() =
+          UnwindLocation::createIsDWARFExpression(*Inst.Expression);
+      break;
+    }
+  }
+  return Error::success();
+}

>From 80ac83c369506f733f0461ead993849551ca5fbd Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Tue, 17 Jun 2025 21:26:16 +0000
Subject: [PATCH 05/16] Annotate UnwindTable exported API with LLVM_ABI

---
 .../llvm/DebugInfo/DWARF/DWARFUnwindTable.h   | 47 ++++++++++---------
 1 file changed, 24 insertions(+), 23 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
index 7a36b1630e7e1..e91c5175a3e88 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
@@ -11,6 +11,7 @@
 
 #include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
+#include "llvm/Support/Compiler.h"
 #include "llvm/Support/Error.h"
 #include <map>
 #include <vector>
@@ -82,12 +83,12 @@ class UnwindLocation {
   /// Create a location whose rule is set to Unspecified. This means the
   /// register value might be in the same register but it wasn't specified in
   /// the unwind opcodes.
-  static UnwindLocation createUnspecified();
+  LLVM_ABI static UnwindLocation createUnspecified();
   /// Create a location where the value is undefined and not available. This can
   /// happen when a register is volatile and can't be recovered.
-  static UnwindLocation createUndefined();
+  LLVM_ABI static UnwindLocation createUndefined();
   /// Create a location where the value is known to be in the register itself.
-  static UnwindLocation createSame();
+  LLVM_ABI static UnwindLocation createSame();
   /// Create a location that is in (Deref == false) or at (Deref == true) the
   /// CFA plus an offset. Most registers that are spilled onto the stack use
   /// this rule. The rule for the register will use this rule and specify a
@@ -95,8 +96,8 @@ class UnwindLocation {
   /// relative to a CFA value which is typically defined using the register
   /// plus offset location. \see createRegisterPlusOffset(...) for more
   /// information.
-  static UnwindLocation createIsCFAPlusOffset(int32_t Off);
-  static UnwindLocation createAtCFAPlusOffset(int32_t Off);
+  LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off);
+  LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off);
   /// Create a location where the saved value is in (Deref == false) or at
   /// (Deref == true) a regiser plus an offset and, optionally, in the specified
   /// address space (used mostly for the CFA).
@@ -105,18 +106,18 @@ class UnwindLocation {
   /// frame pointer as the register, with an offset that accounts for all
   /// spilled registers and all local variables in a function, and Deref ==
   /// false.
-  static UnwindLocation
+  LLVM_ABI static UnwindLocation
   createIsRegisterPlusOffset(uint32_t Reg, int32_t Off,
                              std::optional<uint32_t> AddrSpace = std::nullopt);
-  static UnwindLocation
+  LLVM_ABI static UnwindLocation
   createAtRegisterPlusOffset(uint32_t Reg, int32_t Off,
                              std::optional<uint32_t> AddrSpace = std::nullopt);
   /// Create a location whose value is the result of evaluating a DWARF
   /// expression. This allows complex expressions to be evaluated in order to
   /// unwind a register or CFA value.
-  static UnwindLocation createIsDWARFExpression(DWARFExpression Expr);
-  static UnwindLocation createAtDWARFExpression(DWARFExpression Expr);
-  static UnwindLocation createIsConstant(int32_t Value);
+  LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr);
+  LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr);
+  LLVM_ABI static UnwindLocation createIsConstant(int32_t Value);
 
   Location getLocation() const { return Kind; }
   uint32_t getRegister() const { return RegNum; }
@@ -154,12 +155,12 @@ class UnwindLocation {
   /// instead of from .debug_frame. This is needed for register number
   /// conversion because some register numbers differ between the two sections
   /// for certain architectures like x86.
-  void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
+  LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
 
-  bool operator==(const UnwindLocation &RHS) const;
+  LLVM_ABI bool operator==(const UnwindLocation &RHS) const;
 };
 
-raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
 
 /// A class that can track all registers with locations in a UnwindRow object.
 ///
@@ -212,7 +213,7 @@ class RegisterLocations {
   /// instead of from .debug_frame. This is needed for register number
   /// conversion because some register numbers differ between the two sections
   /// for certain architectures like x86.
-  void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
+  LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const;
 
   /// Returns true if we have any register locations in this object.
   bool hasLocations() const { return !Locations.empty(); }
@@ -224,7 +225,7 @@ class RegisterLocations {
   }
 };
 
-raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
 
 /// A class that represents a single row in the unwind table that is decoded by
 /// parsing the DWARF Call Frame Information opcodes.
@@ -293,11 +294,11 @@ class UnwindRow {
   ///
   /// \param IndentLevel specify the indent level as an integer. The UnwindRow
   /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
-  void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
-            unsigned IndentLevel = 0) const;
+  LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+                     unsigned IndentLevel = 0) const;
 };
 
-raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
 
 /// A class that contains all UnwindRow objects for an FDE or a single unwind
 /// row for a CIE. To unwind an address the rows, which are sorted by start
@@ -340,8 +341,8 @@ class UnwindTable {
   ///
   /// \param IndentLevel specify the indent level as an integer. The UnwindRow
   /// will be output to the stream preceded by 2 * IndentLevel number of spaces.
-  void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
-            unsigned IndentLevel = 0) const;
+  LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts,
+                     unsigned IndentLevel = 0) const;
 
   /// Parse the information in the CFIProgram and update the CurrRow object
   /// that the state machine describes.
@@ -358,8 +359,8 @@ class UnwindTable {
   /// the initial register locations from the CIE. If NULL, then a CIE's
   /// opcodes are being parsed and this is not needed. This is used for the
   /// DW_CFA_restore and DW_CFA_restore_extended opcodes.
-  Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
-                  const RegisterLocations *InitialLocs);
+  LLVM_ABI Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
+                           const RegisterLocations *InitialLocs);
 
 private:
   RowContainer Rows;
@@ -368,7 +369,7 @@ class UnwindTable {
   std::optional<uint64_t> EndAddress;
 };
 
-raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
 
 } // end namespace dwarf
 

>From 8a4f86149fbf6a98acb2d8e1a3e26b7a751cf6c5 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Tue, 17 Jun 2025 21:33:01 +0000
Subject: [PATCH 06/16] Annotate exported API of DebugFrame with LLVM_ABI

---
 llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index 1ea3700690edd..4d2e6fd6bb4e0 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -14,6 +14,7 @@
 #include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h"
 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
 #include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
+#include "llvm/Support/Compiler.h"
 #include "llvm/Support/Error.h"
 #include "llvm/TargetParser/Triple.h"
 #include <memory>
@@ -38,7 +39,7 @@ class CIE;
 ///
 /// \returns An error if the DWARF Call Frame Information opcodes have state
 /// machine errors, or a valid UnwindTable otherwise.
-Expected<UnwindTable> createUnwindTable(const CIE *Cie);
+LLVM_ABI Expected<UnwindTable> createUnwindTable(const CIE *Cie);
 
 class FDE;
 
@@ -50,7 +51,7 @@ class FDE;
 ///
 /// \returns An error if the DWARF Call Frame Information opcodes have state
 /// machine errors, or a valid UnwindTable otherwise.
-Expected<UnwindTable> createUnwindTable(const FDE *Fde);
+LLVM_ABI Expected<UnwindTable> createUnwindTable(const FDE *Fde);
 
 /// An entry in either debug_frame or eh_frame. This entry can be a CIE or an
 /// FDE.

>From fbadafcb02f1a25fa96a4e5e0b3644ddc241e336 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Wed, 25 Jun 2025 18:39:01 +0000
Subject: [PATCH 07/16] Redo the separation with the updates

---
 .../llvm/DebugInfo/DWARF/DWARFDebugFrame.h    | 348 -------------
 .../llvm/DebugInfo/DWARF/DWARFUnwindTable.h   |  52 +-
 llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp  | 484 ------------------
 llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp |  13 +-
 4 files changed, 33 insertions(+), 864 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index cd467d6c8c771..4d2e6fd6bb4e0 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -29,354 +29,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;
-
-  UnwindTable(RowContainer &&Rows) : Rows(std::move(Rows)) {}
-
-  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];
-  }
-
-  /// Dump the UnwindTable to the stream.
-  ///
-  /// \param OS the stream to use for output.
-  ///
-  /// \param MRI register information that helps emit register names instead
-  /// 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;
-
-private:
-  RowContainer Rows;
-};
-
-/// Parse the information in the CFIProgram and update the CurrRow object
-/// that the state machine describes.
-///
-/// This function emulates the state machine described in the DWARF Call Frame
-/// Information opcodes and will push CurrRow onto a RowContainer 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.
-///
-/// \returns An error if the DWARF Call Frame Information opcodes have state
-/// machine errors, or the accumulated rows otherwise.
-LLVM_ABI Expected<UnwindTable::RowContainer>
-parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
-          const RegisterLocations *InitialLocs);
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
-
 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
index e91c5175a3e88..802ff40419f49 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
@@ -311,6 +311,8 @@ class UnwindTable {
   using iterator = RowContainer::iterator;
   using const_iterator = RowContainer::const_iterator;
 
+  UnwindTable(RowContainer &&Rows) : Rows(std::move(Rows)) {}
+
   size_t size() const { return Rows.size(); }
   iterator begin() { return Rows.begin(); }
   const_iterator begin() const { return Rows.begin(); }
@@ -320,18 +322,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.
   ///
   /// \param OS the stream to use for output.
   ///
-  /// \param MRI register information that helps emit register names insteead
+  /// \param MRI register information that helps emit register names instead
   /// of raw register numbers.
   ///
   /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame
@@ -344,31 +340,31 @@ class UnwindTable {
   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.
-  LLVM_ABI 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;
 };
 
+/// Parse the information in the CFIProgram and update the CurrRow object
+/// that the state machine describes.
+///
+/// This function emulates the state machine described in the DWARF Call Frame
+/// Information opcodes and will push CurrRow onto a RowContainer 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.
+///
+/// \returns An error if the DWARF Call Frame Information opcodes have state
+/// machine errors, or the accumulated rows otherwise.
+LLVM_ABI Expected<UnwindTable::RowContainer>
+parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
+          const RegisterLocations *InitialLocs);
+
 LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
 
 } // end namespace dwarf
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
index 9184502adc7cf..de6c279d71557 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
@@ -31,180 +31,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)
@@ -258,316 +84,6 @@ Expected<UnwindTable> llvm::dwarf::createUnwindTable(const CIE *Cie) {
   return UnwindTable(std::move(Rows));
 }
 
-Expected<UnwindTable::RowContainer>
-llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
-                       const RegisterLocations *InitialLocs) {
-  // All the unwinding rows parsed during processing of the CFI program.
-  UnwindTable::RowContainer Rows;
-
-  // 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 Rows;
-}
-
 // 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
index 330eed900221e..429eed77384a8 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
@@ -102,7 +102,8 @@ void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
       OS << " in addrspace" << *AddrSpace;
     break;
   case DWARFExpr: {
-    Expr->print(OS, DumpOpts, nullptr);
+    if (Expr)
+      DWARFExpressionPrinter::print(&(*Expr), OS, DumpOpts, nullptr);
     break;
   }
   case Constant:
@@ -193,8 +194,12 @@ raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) {
   return OS;
 }
 
-Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
-                             const RegisterLocations *InitialLocs) {
+Expected<UnwindTable::RowContainer>
+llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
+                       const RegisterLocations *InitialLocs) {
+  // All the unwinding rows parsed during processing of the CFI program.
+  UnwindTable::RowContainer Rows;
+
   // State consists of CFA value and register locations.
   std::vector<std::pair<UnwindLocation, RegisterLocations>> States;
   for (const CFIProgram::Instruction &Inst : CFIP) {
@@ -496,5 +501,5 @@ Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
       break;
     }
   }
-  return Error::success();
+  return Rows;
 }

>From 1b6686ab0599f0aafb16523bbae5b140e758c616 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Thu, 26 Jun 2025 19:08:51 +0000
Subject: [PATCH 08/16] Use the new expression printer function

---
 llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
index 429eed77384a8..253e5a9f0410d 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
@@ -8,6 +8,7 @@
 
 #include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
 #include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/Format.h"
@@ -103,7 +104,7 @@ void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
     break;
   case DWARFExpr: {
     if (Expr)
-      DWARFExpressionPrinter::print(&(*Expr), OS, DumpOpts, nullptr);
+      printDwarfExpression(&(*Expr), OS, DumpOpts, nullptr);
     break;
   }
   case Constant:

>From 571edd81c000caea0c05d99eb4bf5552aedb6141 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Thu, 26 Jun 2025 19:14:35 +0000
Subject: [PATCH 09/16] Lint

---
 llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index 78b4c1af44f32..a821013c70a45 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -11,9 +11,9 @@
 
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/iterator.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h"
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
-#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Error.h"
 #include "llvm/TargetParser/Triple.h"

>From 5d34629826d0e91679954ebac017868d78a55789 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Wed, 2 Jul 2025 00:43:56 +0000
Subject: [PATCH 10/16] Merge main

---
 .../llvm/DebugInfo/DWARF/DWARFDebugFrame.h    | 350 ------------------
 .../llvm/DebugInfo/DWARF/DWARFUnwindTable.h   |   6 +-
 2 files changed, 4 insertions(+), 352 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index ee9e3621e64c0..a821013c70a45 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -29,356 +29,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; }
-  bool getDereference() const { return Dereference; }
-
-  /// 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;
-
-  UnwindTable(RowContainer &&Rows) : Rows(std::move(Rows)) {}
-
-  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];
-  }
-
-  /// Dump the UnwindTable to the stream.
-  ///
-  /// \param OS the stream to use for output.
-  ///
-  /// \param MRI register information that helps emit register names instead
-  /// 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;
-
-private:
-  RowContainer Rows;
-};
-
-/// Parse the information in the CFIProgram and update the CurrRow object
-/// that the state machine describes.
-///
-/// This function emulates the state machine described in the DWARF Call Frame
-/// Information opcodes and will push CurrRow onto a RowContainer 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.
-///
-/// \returns An error if the DWARF Call Frame Information opcodes have state
-/// machine errors, or the accumulated rows otherwise.
-LLVM_ABI Expected<UnwindTable::RowContainer>
-parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
-          const RegisterLocations *InitialLocs);
-
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
-
 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
index 143f668824227..b7a1c77b35ebb 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
@@ -127,6 +127,8 @@ class UnwindLocation {
     return *AddrSpace;
   }
   int32_t getConstant() const { return Offset; }
+  bool getDereference() const { return Dereference; }
+
   /// 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.
@@ -247,8 +249,8 @@ 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.
+  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()) {}

>From 682dcae0147d9298061ab65b7b9324c1f7512ca5 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Wed, 2 Jul 2025 01:20:40 +0000
Subject: [PATCH 11/16] Lint

---
 llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
index b7a1c77b35ebb..d324e4dc8329d 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
@@ -249,8 +249,8 @@ 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.
+  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()) {}

>From 1ed0df26166199c0081f54b06f4d606f34c01834 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Wed, 2 Jul 2025 20:53:29 +0000
Subject: [PATCH 12/16] [TEMP] move unwind table to where it should be, the
 layering is not correct

---
 llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h             | 2 +-
 .../llvm/DebugInfo/DWARF/{ => LowLevel}/DWARFUnwindTable.h      | 0
 llvm/lib/DebugInfo/DWARF/CMakeLists.txt                         | 1 -
 llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt                | 1 +
 llvm/lib/DebugInfo/DWARF/{ => LowLevel}/DWARFUnwindTable.cpp    | 2 +-
 5 files changed, 3 insertions(+), 3 deletions(-)
 rename llvm/include/llvm/DebugInfo/DWARF/{ => LowLevel}/DWARFUnwindTable.h (100%)
 rename llvm/lib/DebugInfo/DWARF/{ => LowLevel}/DWARFUnwindTable.cpp (99%)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index a821013c70a45..65189916b41f1 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -11,9 +11,9 @@
 
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/iterator.h"
-#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h"
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
+#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Error.h"
 #include "llvm/TargetParser/Triple.h"
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
similarity index 100%
rename from llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h
rename to llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
index da2e2896faeae..ce426aeba3f3f 100644
--- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
+++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
@@ -28,7 +28,6 @@ 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/LowLevel/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt
index c11a2589a552d..4e920aa7ae1a6 100644
--- a/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt
+++ b/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_llvm_component_library(LLVMDebugInfoDWARFLowLevel
   DWARFCFIProgram.cpp
   DWARFExpression.cpp
+  DWARFUnwindTable.cpp
 
   ADDITIONAL_HEADER_DIRS
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/DWARF/LowLevel
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp
similarity index 99%
rename from llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
rename to llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp
index 253e5a9f0410d..6e57d3344b95e 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp
+++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h"
+#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"
 #include "llvm/DebugInfo/DIContext.h"
 #include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h"
 #include "llvm/Support/Errc.h"

>From 4042154200ff5ac6615ad3bd18a8c150ea524062 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Thu, 3 Jul 2025 07:04:00 +0000
Subject: [PATCH 13/16] Move the printing logic of UnwindTable to
 UnwindTablePrinter

---
 .../DebugInfo/DWARF/DWARFUnwindTablePrinter.h | 102 +++++++++++++
 .../DWARF/LowLevel/DWARFUnwindTable.h         |  81 ++--------
 llvm/lib/DebugInfo/DWARF/CMakeLists.txt       |   1 +
 llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp  |   5 +-
 .../DWARF/DWARFUnwindTablePrinter.cpp         | 143 ++++++++++++++++++
 .../DWARF/LowLevel/DWARFUnwindTable.cpp       | 118 ---------------
 .../DebugInfo/DWARF/DWARFDebugFrameTest.cpp   |   1 +
 7 files changed, 263 insertions(+), 188 deletions(-)
 create mode 100644 llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h
 create mode 100644 llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h
new file mode 100644
index 0000000000000..8498077ea4bd8
--- /dev/null
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h
@@ -0,0 +1,102 @@
+//===- DWARFUnwindTablePrinter.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_DWARFUNWINDTABLEPRINTER_H
+#define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLEPRINTER_H
+
+#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+struct DIDumpOptions;
+
+namespace dwarf {
+
+/// Print an unwind location expression as text and use the register information
+/// if some is provided.
+///
+/// \param R the unwind location to print.
+///
+/// \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 printUnwindLocation(const UnwindLocation &R, raw_ostream &OS,
+                                  DIDumpOptions DumpOpts);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
+
+/// Print all registers + locations that are currently defined in a register
+/// locations.
+///
+/// \param RL the register locations to print.
+///
+/// \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 printRegisterLocations(const RegisterLocations &RL,
+                                     raw_ostream &OS, DIDumpOptions DumpOpts);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
+
+/// Print an UnwindRow to the stream.
+///
+/// \param Row the UnwindRow to print.
+///
+/// \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 printUnwindRow(const UnwindRow &Row, raw_ostream &OS,
+                             DIDumpOptions DumpOpts, unsigned IndentLevel = 0);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
+
+/// Print a UnwindTable to the stream.
+///
+/// \param Rows the UnwindTable to print.
+///
+/// \param OS the stream to use for output.
+///
+/// \param MRI register information that helps emit register names instead
+/// 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 printUnwindTable(const UnwindTable &Rows, raw_ostream &OS,
+                               DIDumpOptions DumpOpts,
+                               unsigned IndentLevel = 0);
+LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
+
+} // end namespace dwarf
+
+} // end namespace llvm
+
+#endif // LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLEPRINTER_H
diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
index d324e4dc8329d..c8cad196cc2b5 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H
 #define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H
 
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h"
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
 #include "llvm/Support/Compiler.h"
@@ -122,6 +123,11 @@ class UnwindLocation {
   Location getLocation() const { return Kind; }
   uint32_t getRegister() const { return RegNum; }
   int32_t getOffset() const { return Offset; }
+  bool hasAddressSpace() const {
+    if(AddrSpace)
+      return true;
+    return false;
+  }
   uint32_t getAddressSpace() const {
     assert(Kind == RegPlusOffset && AddrSpace);
     return *AddrSpace;
@@ -145,25 +151,10 @@ class UnwindLocation {
   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
@@ -189,6 +180,13 @@ class RegisterLocations {
     return Pos->second;
   }
 
+  SmallVector<uint32_t, 4> getRegisters() const {
+    SmallVector<uint32_t, 4> Registers;
+    for(auto &&[Register, _]: Locations)
+      Registers.push_back(Register);
+    return Registers;
+  }
+
   /// Set the location for the register in \a RegNum to \a Location.
   ///
   /// \param RegNum the register number to set the location for.
@@ -204,19 +202,6 @@ class RegisterLocations {
   /// \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(); }
 
@@ -227,8 +212,6 @@ class RegisterLocations {
   }
 };
 
-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.
 ///
@@ -281,27 +264,8 @@ class UnwindRow {
   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
@@ -325,23 +289,6 @@ class UnwindTable {
     return Rows[Index];
   }
 
-  /// Dump the UnwindTable to the stream.
-  ///
-  /// \param OS the stream to use for output.
-  ///
-  /// \param MRI register information that helps emit register names instead
-  /// 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;
-
 private:
   RowContainer Rows;
 };
@@ -367,8 +314,6 @@ LLVM_ABI Expected<UnwindTable::RowContainer>
 parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
           const RegisterLocations *InitialLocs);
 
-LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
-
 } // end namespace dwarf
 
 } // end namespace llvm
diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
index ce426aeba3f3f..3755be256a305 100644
--- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
+++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt
@@ -28,6 +28,7 @@ add_llvm_component_library(LLVMDebugInfoDWARF
   DWARFTypeUnit.cpp
   DWARFUnitIndex.cpp
   DWARFUnit.cpp
+  DWARFUnwindTablePrinter.cpp
   DWARFVerifier.cpp
 
   ADDITIONAL_HEADER_DIRS
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
index f8505743aace0..6610eef54801b 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
@@ -15,6 +15,7 @@
 #include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h"
 #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
 #include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h"
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h"
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
 #include "llvm/Support/Compiler.h"
@@ -136,7 +137,7 @@ void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
   OS << "\n";
 
   if (Expected<UnwindTable> RowsOrErr = createUnwindTable(this))
-    RowsOrErr->dump(OS, DumpOpts, 1);
+    printUnwindTable(*RowsOrErr, OS, DumpOpts, 1);
   else {
     DumpOpts.RecoverableErrorHandler(joinErrors(
         createStringError(errc::invalid_argument,
@@ -164,7 +165,7 @@ void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
   OS << "\n";
 
   if (Expected<UnwindTable> RowsOrErr = createUnwindTable(this))
-    RowsOrErr->dump(OS, DumpOpts, 1);
+    printUnwindTable(*RowsOrErr, OS, DumpOpts, 1);
   else {
     DumpOpts.RecoverableErrorHandler(joinErrors(
         createStringError(errc::invalid_argument,
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp
new file mode 100644
index 0000000000000..607c6f70eac52
--- /dev/null
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp
@@ -0,0 +1,143 @@
+//=== DWARFUnwindTablePrinter.cpp - Print the cfi-portions of .debug_frame ===//
+//
+// 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/DWARFUnwindTablePrinter.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.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;
+}
+
+void llvm::dwarf::printUnwindLocation(const UnwindLocation &UL, raw_ostream &OS,
+                                      DIDumpOptions DumpOpts) {
+  if (UL.getDereference())
+    OS << '[';
+  switch (UL.getLocation()) {
+  case UnwindLocation::Unspecified:
+    OS << "unspecified";
+    break;
+  case UnwindLocation::Undefined:
+    OS << "undefined";
+    break;
+  case UnwindLocation::Same:
+    OS << "same";
+    break;
+  case UnwindLocation::CFAPlusOffset:
+    OS << "CFA";
+    if (UL.getOffset() == 0)
+      break;
+    if (UL.getOffset() > 0)
+      OS << "+";
+    OS << UL.getOffset();
+    break;
+  case UnwindLocation::RegPlusOffset:
+    printRegister(OS, DumpOpts, UL.getRegister());
+    if (UL.getOffset() == 0 && !UL.hasAddressSpace())
+      break;
+    if (UL.getOffset() >= 0)
+      OS << "+";
+    OS << UL.getOffset();
+    if (UL.hasAddressSpace())
+      OS << " in addrspace" << UL.getAddressSpace();
+    break;
+  case UnwindLocation::DWARFExpr: {
+    if (UL.getDWARFExpressionBytes()) {
+      auto Expr = *UL.getDWARFExpressionBytes();
+      printDwarfExpression(&Expr, OS, DumpOpts, nullptr);
+    }
+    break;
+  }
+  case UnwindLocation::Constant:
+    OS << UL.getOffset();
+    break;
+  }
+  if (UL.getDereference())
+    OS << ']';
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
+                                     const UnwindLocation &UL) {
+  auto DumpOpts = DIDumpOptions();
+  printUnwindLocation(UL, OS, DumpOpts);
+  return OS;
+}
+
+void llvm::dwarf::printRegisterLocations(const RegisterLocations &RL,
+                                         raw_ostream &OS,
+                                         DIDumpOptions DumpOpts) {
+  bool First = true;
+  for (uint32_t Reg : RL.getRegisters()) {
+    auto Loc = *RL.getRegisterLocation(Reg);
+    if (First)
+      First = false;
+    else
+      OS << ", ";
+    printRegister(OS, DumpOpts, Reg);
+    OS << '=';
+    printUnwindLocation(Loc, OS, DumpOpts);
+  }
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
+                                     const RegisterLocations &RL) {
+  auto DumpOpts = DIDumpOptions();
+  printRegisterLocations(RL, OS, DumpOpts);
+  return OS;
+}
+
+void llvm::dwarf::printUnwindRow(const UnwindRow &Row, raw_ostream &OS,
+                                 DIDumpOptions DumpOpts, unsigned IndentLevel) {
+  OS.indent(2 * IndentLevel);
+  if (Row.hasAddress())
+    OS << format("0x%" PRIx64 ": ", Row.getAddress());
+  OS << "CFA=";
+  printUnwindLocation(Row.getCFAValue(), OS, DumpOpts);
+  if (Row.getRegisterLocations().hasLocations()) {
+    OS << ": ";
+    printRegisterLocations(Row.getRegisterLocations(), OS, DumpOpts);
+  }
+  OS << "\n";
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) {
+  auto DumpOpts = DIDumpOptions();
+  printUnwindRow(Row, OS, DumpOpts, 0);
+  return OS;
+}
+
+void llvm::dwarf::printUnwindTable(const UnwindTable &Rows, raw_ostream &OS,
+                                   DIDumpOptions DumpOpts,
+                                   unsigned IndentLevel) {
+  for (const UnwindRow &Row : Rows)
+    printUnwindRow(Row, OS, DumpOpts, IndentLevel);
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) {
+  auto DumpOpts = DIDumpOptions();
+  printUnwindTable(Rows, OS, DumpOpts, 0);
+  return OS;
+}
diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp
index 6e57d3344b95e..bcf89bf58ea71 100644
--- a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp
+++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp
@@ -7,11 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"
-#include "llvm/DebugInfo/DIContext.h"
-#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.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>
@@ -21,18 +18,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}; }
@@ -71,57 +56,6 @@ 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)
-      printDwarfExpression(&(*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;
@@ -143,58 +77,6 @@ bool UnwindLocation::operator==(const UnwindLocation &RHS) const {
   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::RowContainer>
 llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
                        const RegisterLocations *InitialLocs) {
diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
index 2f91b1f86f6dd..d70e13dec6a62 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
@@ -13,6 +13,7 @@
 #include "llvm/BinaryFormat/Dwarf.h"
 #include "llvm/DebugInfo/DIContext.h"
 #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gtest/gtest.h"
 

>From 87f41c85ea0a396a3c9f1141eff2ce41785f9181 Mon Sep 17 00:00:00 2001
From: Amirhossein Pashaeehir <amirhosseinp at google.com>
Date: Thu, 3 Jul 2025 07:06:06 +0000
Subject: [PATCH 14/16] Lint

---
 llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
index c8cad196cc2b5..703cfd06a00b1 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
@@ -124,7 +124,7 @@ class UnwindLocation {
   uint32_t getRegister() const { return RegNum; }
   int32_t getOffset() const { return Offset; }
   bool hasAddressSpace() const {
-    if(AddrSpace)
+    if (AddrSpace)
       return true;
     return false;
   }
@@ -182,7 +182,7 @@ class RegisterLocations {
 
   SmallVector<uint32_t, 4> getRegisters() const {
     SmallVector<uint32_t, 4> Registers;
-    for(auto &&[Register, _]: Locations)
+    for (auto &&[Register, _] : Locations)
       Registers.push_back(Register);
     return Registers;
   }

>From 859bfb6f1beabc2690dd9ca72b673c587eb2b0f4 Mon Sep 17 00:00:00 2001
From: amsen <ahph1380 at gmail.com>
Date: Sat, 5 Jul 2025 10:16:15 -0700
Subject: [PATCH 15/16] Remove unnecessary print functions from library

---
 .../DebugInfo/DWARF/DWARFUnwindTablePrinter.h | 50 ----------------
 .../DWARF/DWARFUnwindTablePrinter.cpp         | 57 ++++++++++++++++---
 2 files changed, 50 insertions(+), 57 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h
index 8498077ea4bd8..f754e13614eab 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h
@@ -18,60 +18,10 @@ struct DIDumpOptions;
 
 namespace dwarf {
 
-/// Print an unwind location expression as text and use the register information
-/// if some is provided.
-///
-/// \param R the unwind location to print.
-///
-/// \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 printUnwindLocation(const UnwindLocation &R, raw_ostream &OS,
-                                  DIDumpOptions DumpOpts);
 LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R);
 
-/// Print all registers + locations that are currently defined in a register
-/// locations.
-///
-/// \param RL the register locations to print.
-///
-/// \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 printRegisterLocations(const RegisterLocations &RL,
-                                     raw_ostream &OS, DIDumpOptions DumpOpts);
 LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL);
 
-/// Print an UnwindRow to the stream.
-///
-/// \param Row the UnwindRow to print.
-///
-/// \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 printUnwindRow(const UnwindRow &Row, raw_ostream &OS,
-                             DIDumpOptions DumpOpts, unsigned IndentLevel = 0);
 LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
 
 /// Print a UnwindTable to the stream.
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp
index 607c6f70eac52..8248b142bf574 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp
@@ -32,8 +32,22 @@ static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts,
   OS << "reg" << RegNum;
 }
 
-void llvm::dwarf::printUnwindLocation(const UnwindLocation &UL, raw_ostream &OS,
-                                      DIDumpOptions DumpOpts) {
+/// Print an unwind location expression as text and use the register information
+/// if some is provided.
+///
+/// \param R the unwind location to print.
+///
+/// \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.
+static void printUnwindLocation(const UnwindLocation &UL, raw_ostream &OS,
+                                DIDumpOptions DumpOpts) {
   if (UL.getDereference())
     OS << '[';
   switch (UL.getLocation()) {
@@ -86,9 +100,22 @@ raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
   return OS;
 }
 
-void llvm::dwarf::printRegisterLocations(const RegisterLocations &RL,
-                                         raw_ostream &OS,
-                                         DIDumpOptions DumpOpts) {
+/// Print all registers + locations that are currently defined in a register
+/// locations.
+///
+/// \param RL the register locations to print.
+///
+/// \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.
+static void printRegisterLocations(const RegisterLocations &RL, raw_ostream &OS,
+                                   DIDumpOptions DumpOpts) {
   bool First = true;
   for (uint32_t Reg : RL.getRegisters()) {
     auto Loc = *RL.getRegisterLocation(Reg);
@@ -109,8 +136,24 @@ raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
   return OS;
 }
 
-void llvm::dwarf::printUnwindRow(const UnwindRow &Row, raw_ostream &OS,
-                                 DIDumpOptions DumpOpts, unsigned IndentLevel) {
+/// Print an UnwindRow to the stream.
+///
+/// \param Row the UnwindRow to print.
+///
+/// \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.
+static void printUnwindRow(const UnwindRow &Row, raw_ostream &OS,
+                           DIDumpOptions DumpOpts, unsigned IndentLevel) {
   OS.indent(2 * IndentLevel);
   if (Row.hasAddress())
     OS << format("0x%" PRIx64 ": ", Row.getAddress());

>From b7b47cd5eeac324fb758aa6c6fde999d15f86c0f Mon Sep 17 00:00:00 2001
From: amsen <ahph1380 at gmail.com>
Date: Sat, 5 Jul 2025 21:20:31 -0700
Subject: [PATCH 16/16] Fix header guards match folder paths

---
 .../llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h        | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
index 703cfd06a00b1..5874810201607 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H
-#define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H
+#ifndef LLVM_DEBUGINFO_DWARF_LOWLEVEL_DWARFUNWINDTABLE_H
+#define LLVM_DEBUGINFO_DWARF_LOWLEVEL_DWARFUNWINDTABLE_H
 
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h"
@@ -318,4 +318,4 @@ parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow,
 
 } // end namespace llvm
 
-#endif
+#endif // LLVM_DEBUGINFO_DWARF_LOWLEVEL_DWARFUNWINDTABLE_H



More information about the llvm-commits mailing list