[llvm] f8122d3 - Add the ability to extract the unwind rows from DWARF Call Frame Information.

Greg Clayton via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 28 13:39:31 PST 2021


Author: Greg Clayton
Date: 2021-01-28T13:39:17-08:00
New Revision: f8122d35325d9a5d3db7d4c0d40bcdeae15bee5a

URL: https://github.com/llvm/llvm-project/commit/f8122d35325d9a5d3db7d4c0d40bcdeae15bee5a
DIFF: https://github.com/llvm/llvm-project/commit/f8122d35325d9a5d3db7d4c0d40bcdeae15bee5a.diff

LOG: Add the ability to extract the unwind rows from DWARF Call Frame Information.

This patch adds the ability to evaluate the state machine for CIE and FDE unwind objects and produce a UnwindTable with all UnwindRow objects needed to unwind registers. It will also dump the UnwindTable for each CIE and FDE when dumping DWARF .debug_frame or .eh_frame sections in llvm-dwarfdump or llvm-objdump. This allows users to see what the unwind rows actually look like for a given CIE or FDE instead of just seeing a list of opcodes.

This patch adds new classes: UnwindLocation, RegisterLocations, UnwindRow, and UnwindTable.

UnwindLocation is a class that describes how to unwind a register or Call Frame Address (CFA).

RegisterLocations is a class that tracks registers and their UnwindLocations. It gets populated when parsing the DWARF call frame instruction opcodes for a unwind row. The registers are mapped from their register numbers to the UnwindLocation in a map.

UnwindRow contains the result of evaluating a row of DWARF call frame instructions for the CIE, or a row from a FDE. The CIE can produce a set of initial instructions that each FDE that points to that CIE will use as the seed for the state machine when parsing FDE opcodes. A UnwindRow for a CIE will not have a valid address, whille a UnwindRow for a FDE will have a valid address.

The UnwindTable is a class that contains a sorted (by address) vector of UnwindRow objects and is the result of parsing all opcodes in a CIE, or FDE. Parsing a CIE should produce a UnwindTable with a single row. Parsing a FDE will produce a UnwindTable with one or more UnwindRow objects where all UnwindRow objects have valid addresses. The rows in the UnwindTable will be sorted from lowest Address to highest after parsing the state machine, or an error will be returned if the table isn't sorted. To parse a UnwindTable clients can use the following methods:

    static Expected<UnwindTable> UnwindTable::create(const CIE *Cie);
    static Expected<UnwindTable> UnwindTable::create(const FDE *Fde);

A valid table will be returned if the DWARF call frame instruction opcodes have no encoding errors. There are a few things that can go wrong during the evaluation of the state machine and these create functions will catch and return them.

Differential Revision: https://reviews.llvm.org/D89845

Added: 
    

Modified: 
    llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
    llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
    llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
    llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
    llvm/test/DebugInfo/X86/debug-frame-cie-id-dwarf64.s
    llvm/test/DebugInfo/X86/debug_frame-invalid-cie-offset.s
    llvm/test/tools/llvm-objdump/eh_frame-mipsel.test
    llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index af87811f5d7d..cc1cde7c4520 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -10,12 +10,13 @@
 #define LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H
 
 #include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/iterator.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/Triple.h"
+#include "llvm/ADT/iterator.h"
 #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
 #include "llvm/Support/Error.h"
+#include <map>
 #include <memory>
 #include <vector>
 
@@ -25,6 +26,359 @@ class raw_ostream;
 
 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 it in or at a register plus offset:
+    ///   reg = reg + offset
+    ///   reg = deref(reg + offset)
+    RegPlusOffset,
+    /// Register 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.
+  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), Dereference(false) {}
+
+  UnwindLocation(Location K, uint32_t Reg, int32_t Off, bool Deref)
+      : Kind(K), RegNum(Reg), Offset(Off), 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.
+  ///
+  /// 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);
+  static UnwindLocation createAtRegisterPlusOffset(uint32_t Reg, int32_t Off);
+  /// 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; }
+  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 register 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; }
+
+  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 
diff er between the two sections
+  /// for certain architectures like x86.
+  void dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) 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 llvm::None
+  /// otherwise.
+  Optional<UnwindLocation> getRegisterLocation(uint32_t RegNum) const {
+    auto Pos = Locations.find(RegNum);
+    if (Pos == Locations.end())
+      return llvm::None;
+    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 
diff er between the two sections
+  /// for certain architectures like x86.
+  void dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) 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.
+  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.hasValue(); }
+
+  /// 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 
diff er 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, const MCRegisterInfo *MRI, bool IsEH,
+            unsigned IndentLevel = 0) const;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row);
+
+class CFIProgram;
+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
+/// 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];
+  }
+
+  /// 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 
diff er 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, const MCRegisterInfo *MRI, bool IsEH,
+            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.
+  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.
+  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.
+  Optional<uint64_t> EndAddress;
+
+  /// 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 CFID 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);
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
+
 /// Represent a sequence of Call Frame Information instructions that, when read
 /// in order, construct a table mapping PC to frame state. This can also be
 /// referred to as "CFI rules" in DWARF literature to avoid confusion with
@@ -45,6 +399,12 @@ class CFIProgram {
     Operands Ops;
     // Associated DWARF expression in case this instruction refers to one
     Optional<DWARFExpression> Expression;
+
+    Expected<uint64_t> getOperandAsUnsigned(const CFIProgram &CFIP,
+                                            uint32_t OperandIdx) const;
+
+    Expected<int64_t> getOperandAsSigned(const CFIProgram &CFIP,
+                                         uint32_t OperandIdx) const;
   };
 
   using InstrList = std::vector<Instruction>;
@@ -58,6 +418,9 @@ class CFIProgram {
 
   unsigned size() const { return (unsigned)Instructions.size(); }
   bool empty() const { return Instructions.empty(); }
+  uint64_t codeAlign() const { return CodeAlignmentFactor; }
+  int64_t dataAlign() const { return DataAlignmentFactor; }
+  Triple::ArchType triple() const { return Arch; }
 
   CFIProgram(uint64_t CodeAlignmentFactor, int64_t DataAlignmentFactor,
              Triple::ArchType Arch)
@@ -74,6 +437,11 @@ class CFIProgram {
   void dump(raw_ostream &OS, DIDumpOptions DumpOpts, const MCRegisterInfo *MRI,
             bool IsEH, unsigned IndentLevel = 1) const;
 
+  void addInstruction(const Instruction &I) { Instructions.push_back(I); }
+
+  /// Get a DWARF CFI call frame string for the given DW_CFA opcode.
+  StringRef callFrameString(unsigned Opcode) const;
+
 private:
   std::vector<Instruction> Instructions;
   const uint64_t CodeAlignmentFactor;
@@ -116,6 +484,9 @@ class CFIProgram {
     OT_Expression
   };
 
+  /// Get the OperandType as a "const char *".
+  static const char *operandTypeString(OperandType OT);
+
   /// Retrieve the array describing the types of operands according to the enum
   /// above. This is indexed by opcode.
   static ArrayRef<OperandType[2]> getOperandTypes();

diff  --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
index 447ad66b9352..0cee1b5c9be2 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
@@ -80,7 +80,7 @@ class DWARFExpression {
     friend class DWARFExpression::iterator;
     uint8_t Opcode; ///< The Op Opcode, DW_OP_<something>.
     Description Desc;
-    bool Error;
+    bool Error = false;
     uint64_t EndOffset;
     uint64_t Operands[2];
     uint64_t OperandEndOffsets[2];
@@ -157,6 +157,8 @@ class DWARFExpression {
 
   bool verify(DWARFUnit *U);
 
+  bool operator==(const DWARFExpression &RHS) const;
+
 private:
   DataExtractor Data;
   uint8_t AddressSize;

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
index 7d3d43c37c34..d95cbb70225e 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
@@ -42,6 +42,191 @@ static void printRegister(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH,
   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, false};
+}
+
+UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) {
+  return {CFAPlusOffset, InvalidRegisterNumber, Offset, false};
+}
+
+UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) {
+  return {CFAPlusOffset, InvalidRegisterNumber, Offset, true};
+}
+
+UnwindLocation UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum,
+                                                          int32_t Offset) {
+  return {RegPlusOffset, RegNum, Offset, false};
+}
+UnwindLocation UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum,
+                                                          int32_t Offset) {
+  return {RegPlusOffset, RegNum, Offset, true};
+}
+
+UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) {
+  return {Expr, false};
+}
+
+UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) {
+  return {Expr, true};
+}
+
+void UnwindLocation::dump(raw_ostream &OS, const MCRegisterInfo *MRI,
+                          bool IsEH) 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, MRI, IsEH, RegNum);
+    if (Offset == 0)
+      break;
+    if (Offset > 0)
+      OS << "+";
+    OS << Offset;
+    break;
+  case DWARFExpr:
+    Expr->print(OS, DIDumpOptions(), MRI, nullptr, IsEH);
+    break;
+  case Constant:
+    OS << Offset;
+    break;
+  }
+  if (Dereference)
+    OS << ']';
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
+                                     const UnwindLocation &UL) {
+  UL.dump(OS, nullptr, false);
+  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, const MCRegisterInfo *MRI,
+                             bool IsEH) const {
+  bool First = true;
+  for (const auto &RegLocPair : Locations) {
+    if (First)
+      First = false;
+    else
+      OS << ", ";
+    printRegister(OS, MRI, IsEH, RegLocPair.first);
+    OS << '=';
+    RegLocPair.second.dump(OS, MRI, IsEH);
+  }
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS,
+                                     const RegisterLocations &RL) {
+  RL.dump(OS, nullptr, false);
+  return OS;
+}
+
+void UnwindRow::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH,
+                     unsigned IndentLevel) const {
+  OS.indent(2 * IndentLevel);
+  if (hasAddress())
+    OS << format("0x%" PRIx64 ": ", *Address);
+  OS << "CFA=";
+  CFAValue.dump(OS, MRI, IsEH);
+  if (RegLocs.hasLocations()) {
+    OS << ": ";
+    RegLocs.dump(OS, MRI, IsEH);
+  }
+  OS << "\n";
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) {
+  Row.dump(OS, nullptr, false, 0);
+  return OS;
+}
+
+void UnwindTable::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH,
+                       unsigned IndentLevel) const {
+  for (const UnwindRow &Row : Rows)
+    Row.dump(OS, MRI, IsEH, IndentLevel);
+}
+
+raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) {
+  Rows.dump(OS, nullptr, false, 0);
+  return OS;
+}
+
+Expected<UnwindTable> UnwindTable::create(const FDE *Fde) {
+  UnwindTable UT;
+  UnwindRow Row;
+  Row.setAddress(Fde->getInitialLocation());
+  UT.EndAddress = Fde->getInitialLocation() + Fde->getAddressRange();
+
+  const CIE *Cie = Fde->getLinkedCIE();
+  if (Cie == nullptr)
+    return createStringError(errc::invalid_argument,
+                             "unable to get CIE for FDE at offset 0x%" PRIx64,
+                             Fde->getOffset());
+
+  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
+  // in case we run into DW_CFA_restore or DW_CFA_restore_extended opcodes.
+  const RegisterLocations InitialLocs = Row.getRegisterLocations();
+  if (Error FdeError = UT.parseRows(Fde->cfis(), Row, &InitialLocs))
+    return std::move(FdeError);
+  UT.Rows.push_back(Row);
+  return UT;
+}
+
+Expected<UnwindTable> UnwindTable::create(const CIE *Cie) {
+  UnwindTable UT;
+  UnwindRow Row;
+  if (Error CieError = UT.parseRows(Cie->cfis(), Row, nullptr))
+    return std::move(CieError);
+  UT.Rows.push_back(Row);
+  return UT;
+}
+
 // See DWARF standard v3, section 7.23
 const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0;
 const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f;
@@ -174,10 +359,384 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
   return C.takeError();
 }
 
-namespace {
+StringRef CFIProgram::callFrameString(unsigned Opcode) const {
+  return dwarf::CallFrameString(Opcode, Arch);
+}
+
+const char *CFIProgram::operandTypeString(CFIProgram::OperandType OT) {
+#define ENUM_TO_CSTR(e)                                                        \
+  case e:                                                                      \
+    return #e;
+  switch (OT) {
+    ENUM_TO_CSTR(OT_Unset);
+    ENUM_TO_CSTR(OT_None);
+    ENUM_TO_CSTR(OT_Address);
+    ENUM_TO_CSTR(OT_Offset);
+    ENUM_TO_CSTR(OT_FactoredCodeOffset);
+    ENUM_TO_CSTR(OT_SignedFactDataOffset);
+    ENUM_TO_CSTR(OT_UnsignedFactDataOffset);
+    ENUM_TO_CSTR(OT_Register);
+    ENUM_TO_CSTR(OT_Expression);
+  }
+  return "<unknown CFIProgram::OperandType>";
+}
 
+llvm::Expected<uint64_t>
+CFIProgram::Instruction::getOperandAsUnsigned(const CFIProgram &CFIP,
+                                              uint32_t OperandIdx) const {
+  if (OperandIdx >= 2)
+    return createStringError(errc::invalid_argument,
+                             "operand index %" PRIu32 " is not valid",
+                             OperandIdx);
+  OperandType Type = CFIP.getOperandTypes()[Opcode][OperandIdx];
+  uint64_t Operand = Ops[OperandIdx];
+  switch (Type) {
+  case OT_Unset:
+  case OT_None:
+  case OT_Expression:
+    return createStringError(errc::invalid_argument,
+                             "op[%" PRIu32 "] has type %s which has no value",
+                             OperandIdx, CFIProgram::operandTypeString(Type));
 
-} // end anonymous namespace
+  case OT_Offset:
+  case OT_SignedFactDataOffset:
+  case OT_UnsignedFactDataOffset:
+    return createStringError(
+        errc::invalid_argument,
+        "op[%" PRIu32 "] has OperandType OT_Offset which produces a signed "
+        "result, call getOperandAsSigned instead",
+        OperandIdx);
+
+  case OT_Address:
+  case OT_Register:
+    return Operand;
+
+  case OT_FactoredCodeOffset: {
+    const uint64_t CodeAlignmentFactor = CFIP.codeAlign();
+    if (CodeAlignmentFactor == 0)
+      return createStringError(
+          errc::invalid_argument,
+          "op[%" PRIu32 "] has type OT_FactoredCodeOffset but code alignment "
+          "is zero",
+          OperandIdx);
+    return Operand * CodeAlignmentFactor;
+  }
+  }
+}
+
+llvm::Expected<int64_t>
+CFIProgram::Instruction::getOperandAsSigned(const CFIProgram &CFIP,
+                                            uint32_t OperandIdx) const {
+  if (OperandIdx >= 2)
+    return createStringError(errc::invalid_argument,
+                             "operand index %" PRIu32 " is not valid",
+                             OperandIdx);
+  OperandType Type = CFIP.getOperandTypes()[Opcode][OperandIdx];
+  uint64_t Operand = Ops[OperandIdx];
+  switch (Type) {
+  case OT_Unset:
+  case OT_None:
+  case OT_Expression:
+    return createStringError(errc::invalid_argument,
+                             "op[%" PRIu32 "] has type %s which has no value",
+                             OperandIdx, CFIProgram::operandTypeString(Type));
+
+  case OT_Address:
+  case OT_Register:
+    return createStringError(
+        errc::invalid_argument,
+        "op[%" PRIu32 "] has OperandType %s which produces an unsigned result, "
+        "call getOperandAsUnsigned instead",
+        OperandIdx, CFIProgram::operandTypeString(Type));
+
+  case OT_Offset:
+    return (int64_t)Operand;
+
+  case OT_FactoredCodeOffset:
+  case OT_SignedFactDataOffset: {
+    const int64_t DataAlignmentFactor = CFIP.dataAlign();
+    if (DataAlignmentFactor == 0)
+      return createStringError(errc::invalid_argument,
+                               "op[%" PRIu32 "] has type %s but data "
+                               "alignment is zero",
+                               OperandIdx, CFIProgram::operandTypeString(Type));
+    return int64_t(Operand) * DataAlignmentFactor;
+  }
+
+  case OT_UnsignedFactDataOffset: {
+    const int64_t DataAlignmentFactor = CFIP.dataAlign();
+    if (DataAlignmentFactor == 0)
+      return createStringError(errc::invalid_argument,
+                               "op[%" PRIu32
+                               "] has type OT_UnsignedFactDataOffset but data "
+                               "alignment is zero",
+                               OperandIdx);
+    return Operand * DataAlignmentFactor;
+  }
+  }
+}
+
+Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
+                             const RegisterLocations *InitialLocs) {
+  std::vector<RegisterLocations> RegisterStates;
+  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 (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:
+      RegisterStates.push_back(Row.getRegisterLocations());
+      break;
+
+    case dwarf::DW_CFA_restore_state:
+      if (RegisterStates.empty())
+        return createStringError(errc::invalid_argument,
+                                 "DW_CFA_restore_state without a matching "
+                                 "previous DW_CFA_remember_state");
+      Row.getRegisterLocations() = RegisterStates.back();
+      RegisterStates.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 
diff erent things on 
diff erent
+        // 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);
+          } 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_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_def_cfa_expression:
+      Row.getCFAValue() =
+          UnwindLocation::createIsDWARFExpression(*Inst.Expression);
+      break;
+    }
+  }
+  return Error::success();
+}
 
 ArrayRef<CFIProgram::OperandType[2]> CFIProgram::getOperandTypes() {
   static OperandType OpTypes[DW_CFA_restore+1][2];
@@ -244,7 +803,7 @@ void CFIProgram::printOperand(raw_ostream &OS, DIDumpOptions DumpOpts,
   switch (Type) {
   case OT_Unset: {
     OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to";
-    auto OpcodeName = CallFrameString(Opcode, Arch);
+    auto OpcodeName = callFrameString(Opcode);
     if (!OpcodeName.empty())
       OS << " " << OpcodeName;
     else
@@ -297,10 +856,8 @@ void CFIProgram::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
                       unsigned IndentLevel) const {
   for (const auto &Instr : Instructions) {
     uint8_t Opcode = Instr.Opcode;
-    if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK)
-      Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK;
     OS.indent(2 * IndentLevel);
-    OS << CallFrameString(Opcode, Arch) << ":";
+    OS << callFrameString(Opcode) << ":";
     for (unsigned i = 0; i < Instr.Ops.size(); ++i)
       printOperand(OS, DumpOpts, MRI, IsEH, Instr, i, Instr.Ops[i]);
     OS << '\n';
@@ -354,6 +911,16 @@ void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
   OS << "\n";
   CFIs.dump(OS, DumpOpts, MRI, IsEH);
   OS << "\n";
+
+  if (Expected<UnwindTable> RowsOrErr = UnwindTable::create(this))
+    RowsOrErr->dump(OS, MRI, IsEH, 1);
+  else {
+    DumpOpts.RecoverableErrorHandler(joinErrors(
+        createStringError(errc::invalid_argument,
+                          "decoding the CIE opcodes into rows failed"),
+        RowsOrErr.takeError()));
+  }
+  OS << "\n";
 }
 
 void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
@@ -373,6 +940,16 @@ void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts,
     OS << format("  LSDA Address: %016" PRIx64 "\n", *LSDAAddress);
   CFIs.dump(OS, DumpOpts, MRI, IsEH);
   OS << "\n";
+
+  if (Expected<UnwindTable> RowsOrErr = UnwindTable::create(this))
+    RowsOrErr->dump(OS, MRI, IsEH, 1);
+  else {
+    DumpOpts.RecoverableErrorHandler(joinErrors(
+        createStringError(errc::invalid_argument,
+                          "decoding the FDE opcodes into rows failed"),
+        RowsOrErr.takeError()));
+  }
+  OS << "\n";
 }
 
 DWARFDebugFrame::DWARFDebugFrame(Triple::ArchType Arch,

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
index 811716111be5..6618a7731e92 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
@@ -501,4 +501,10 @@ bool DWARFExpression::printCompact(raw_ostream &OS, const MCRegisterInfo &MRI) {
   return printCompactDWARFExpr(OS, begin(), end(), MRI);
 }
 
+bool DWARFExpression::operator==(const DWARFExpression &RHS) const {
+  if (AddressSize != RHS.AddressSize || Format != RHS.Format)
+    return false;
+  return Data.getData() == RHS.Data.getData();
+}
+
 } // namespace llvm

diff  --git a/llvm/test/DebugInfo/X86/debug-frame-cie-id-dwarf64.s b/llvm/test/DebugInfo/X86/debug-frame-cie-id-dwarf64.s
index b66be5af6a4c..8cfabec3a126 100644
--- a/llvm/test/DebugInfo/X86/debug-frame-cie-id-dwarf64.s
+++ b/llvm/test/DebugInfo/X86/debug-frame-cie-id-dwarf64.s
@@ -1,8 +1,10 @@
 # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o - | \
-# RUN:   llvm-dwarfdump -debug-frame - | \
+# RUN:   not llvm-dwarfdump -debug-frame - 2>&1 | \
 # RUN:   FileCheck %s
 
 # CHECK: 00000000 {{.*}} FDE
+# CHECK: error: decoding the FDE opcodes into rows failed
+# CHECK: error: unable to get CIE for FDE at offset 0x0
 
         .section .debug_frame,"", at progbits
 ## This FDE was formerly wrongly interpreted as a CIE because its CIE pointer

diff  --git a/llvm/test/DebugInfo/X86/debug_frame-invalid-cie-offset.s b/llvm/test/DebugInfo/X86/debug_frame-invalid-cie-offset.s
index 40ce76080b60..d1e7414c379c 100644
--- a/llvm/test/DebugInfo/X86/debug_frame-invalid-cie-offset.s
+++ b/llvm/test/DebugInfo/X86/debug_frame-invalid-cie-offset.s
@@ -1,9 +1,11 @@
 # RUN: llvm-mc -triple i386-unknown-linux %s -filetype=obj -o - | \
-# RUN:  llvm-dwarfdump -debug-frame - | \
+# RUN:  not llvm-dwarfdump -debug-frame - 2>&1 | \
 # RUN:  FileCheck %s
 
 # CHECK: .debug_frame contents:
 # CHECK: 00000000 0000000c 12345678 FDE cie=<invalid offset> pc=00010000...00010010
+# CHECK: error: decoding the FDE opcodes into rows failed
+# CHECK: error: unable to get CIE for FDE at offset 0x0
 
     .section .debug_frame,"", at progbits
     .long   .LFDE0end-.LFDE0id  # Length

diff  --git a/llvm/test/tools/llvm-objdump/eh_frame-mipsel.test b/llvm/test/tools/llvm-objdump/eh_frame-mipsel.test
index 70ce588a8d73..c0c69ea281ff 100644
--- a/llvm/test/tools/llvm-objdump/eh_frame-mipsel.test
+++ b/llvm/test/tools/llvm-objdump/eh_frame-mipsel.test
@@ -14,6 +14,8 @@
 # CHECK-EMPTY:
 # CHECK-NEXT:  DW_CFA_def_cfa: reg29 +0
 # CHECK-EMPTY:
+# CHECK-NEXT:  CFA=reg29
+# CHECK-EMPTY:
 # CHECK-NEXT:  0000001c 00000018 00000020 FDE cie=00000000 pc=00400890...004008dc
 # CHECK-NEXT:    Format:       DWARF32
 # CHECK-NEXT:    LSDA Address: 0000000000400a90
@@ -23,5 +25,9 @@
 # CHECK-NEXT:    DW_CFA_offset: reg31 -4
 # CHECK-NEXT:    DW_CFA_nop:
 # CHECK-EMPTY:
+# CHECK-NEXT:    0x400890: CFA=reg29
+# CHECK-NEXT:    0x400894: CFA=reg29+24
+# CHECK-NEXT:    0x400898: CFA=reg29+24: reg31=[CFA-4]
+# CHECK-EMPTY:
 # CHECK-NEXT:  00000038 ZERO terminator
 # CHECK-NOT: {{.}}

diff  --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
index 7706c216053c..b4091d5484c0 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
@@ -130,6 +130,13 @@ static Error parseCFI(dwarf::CIE &C, ArrayRef<uint8_t> Instructions,
   return C.cfis().parse(Data, &Offset, EndOffset);
 }
 
+static Error parseCFI(dwarf::FDE &FDE, ArrayRef<uint8_t> Instructions) {
+  DWARFDataExtractor Data(Instructions, /*IsLittleEndian=*/true,
+                          /*AddressSize=*/8);
+  uint64_t Offset = 0;
+  return FDE.cfis().parse(Data, &Offset, Instructions.size());
+}
+
 TEST(DWARFDebugFrame, InvalidCFIOpcodesTest) {
   llvm::DenseSet<uint8_t> ValidExtendedOpcodes = {
       dwarf::DW_CFA_nop,
@@ -322,4 +329,1142 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
     CheckOp_ULEB128_Expr(Inst);
 }
 
+void expectDumpResult(const dwarf::UnwindLocation &Loc,
+                      StringRef ExpectedFirstLine) {
+  std::string Output;
+  raw_string_ostream OS(Output);
+  OS << Loc;
+  OS.flush();
+  StringRef FirstLine = StringRef(Output).split('\n').first;
+  EXPECT_EQ(FirstLine, ExpectedFirstLine);
+}
+
+TEST(DWARFDebugFrame, DumpUnwindLocations) {
+  // Test constructing unwind locations and dumping each kind.
+  constexpr int32_t PlusOff = 8;
+  constexpr int32_t MinusOff = -8;
+  constexpr uint32_t RegNum = 12;
+  expectDumpResult(dwarf::UnwindLocation::createUnspecified(), "unspecified");
+  expectDumpResult(dwarf::UnwindLocation::createUndefined(), "undefined");
+  expectDumpResult(dwarf::UnwindLocation::createSame(), "same");
+  expectDumpResult(dwarf::UnwindLocation::createIsCFAPlusOffset(PlusOff),
+                   "CFA+8");
+  expectDumpResult(dwarf::UnwindLocation::createIsCFAPlusOffset(MinusOff),
+                   "CFA-8");
+  expectDumpResult(dwarf::UnwindLocation::createAtCFAPlusOffset(PlusOff),
+                   "[CFA+8]");
+  expectDumpResult(dwarf::UnwindLocation::createAtCFAPlusOffset(MinusOff),
+                   "[CFA-8]");
+
+  expectDumpResult(
+      dwarf::UnwindLocation::createIsRegisterPlusOffset(RegNum, PlusOff),
+      "reg12+8");
+  expectDumpResult(
+      dwarf::UnwindLocation::createIsRegisterPlusOffset(RegNum, MinusOff),
+      "reg12-8");
+  expectDumpResult(
+      dwarf::UnwindLocation::createAtRegisterPlusOffset(RegNum, PlusOff),
+      "[reg12+8]");
+  expectDumpResult(
+      dwarf::UnwindLocation::createAtRegisterPlusOffset(RegNum, MinusOff),
+      "[reg12-8]");
+  expectDumpResult(dwarf::UnwindLocation::createIsConstant(12), "12");
+  expectDumpResult(dwarf::UnwindLocation::createIsConstant(-32), "-32");
+}
+
+void expectDumpResult(const dwarf::RegisterLocations &Locs,
+                      StringRef ExpectedFirstLine) {
+  std::string Output;
+  raw_string_ostream OS(Output);
+  OS << Locs;
+  OS.flush();
+  StringRef FirstLine = StringRef(Output).split('\n').first;
+  EXPECT_EQ(FirstLine, ExpectedFirstLine);
+}
+
+TEST(DWARFDebugFrame, RegisterLocations) {
+  // Test the functionality of the RegisterLocations class.
+  dwarf::RegisterLocations Locs;
+  expectDumpResult(Locs, "");
+  EXPECT_FALSE(Locs.hasLocations());
+  // Set a register location for reg12 to unspecified and verify it dumps
+  // correctly.
+  Locs.setRegisterLocation(12, dwarf::UnwindLocation::createUnspecified());
+  EXPECT_TRUE(Locs.hasLocations());
+  expectDumpResult(Locs, "reg12=unspecified");
+
+  // Replace the register location for reg12 to "same" and verify it dumps
+  // correctly after it is modified
+  Locs.setRegisterLocation(12, dwarf::UnwindLocation::createSame());
+  EXPECT_TRUE(Locs.hasLocations());
+  expectDumpResult(Locs, "reg12=same");
+
+  // Remove the register location for reg12 verify it dumps correctly after it
+  // is removed.
+  Locs.removeRegisterLocation(12);
+  EXPECT_FALSE(Locs.hasLocations());
+  expectDumpResult(Locs, "");
+
+  // Verify multiple registers added to the list dump correctly.
+  auto Reg12Loc = dwarf::UnwindLocation::createAtCFAPlusOffset(4);
+  auto Reg13Loc = dwarf::UnwindLocation::createAtCFAPlusOffset(8);
+  auto Reg14Loc = dwarf::UnwindLocation::createSame();
+  Locs.setRegisterLocation(12, Reg12Loc);
+  Locs.setRegisterLocation(13, Reg13Loc);
+  Locs.setRegisterLocation(14, Reg14Loc);
+  EXPECT_TRUE(Locs.hasLocations());
+  expectDumpResult(Locs, "reg12=[CFA+4], reg13=[CFA+8], reg14=same");
+
+  // Verify RegisterLocations::getRegisterLocation() works as expected.
+  Optional<dwarf::UnwindLocation> OptionalLoc;
+  OptionalLoc = Locs.getRegisterLocation(0);
+  EXPECT_FALSE(OptionalLoc.hasValue());
+
+  OptionalLoc = Locs.getRegisterLocation(12);
+  EXPECT_TRUE(OptionalLoc.hasValue());
+  EXPECT_EQ(*OptionalLoc, Reg12Loc);
+
+  OptionalLoc = Locs.getRegisterLocation(13);
+  EXPECT_TRUE(OptionalLoc.hasValue());
+  EXPECT_EQ(*OptionalLoc, Reg13Loc);
+
+  OptionalLoc = Locs.getRegisterLocation(14);
+  EXPECT_TRUE(OptionalLoc.hasValue());
+  EXPECT_EQ(*OptionalLoc, Reg14Loc);
+
+  // Verify registers are correctly removed when multiple exist in the list.
+  Locs.removeRegisterLocation(13);
+  EXPECT_FALSE(Locs.getRegisterLocation(13).hasValue());
+  EXPECT_TRUE(Locs.hasLocations());
+  expectDumpResult(Locs, "reg12=[CFA+4], reg14=same");
+  Locs.removeRegisterLocation(14);
+  EXPECT_FALSE(Locs.getRegisterLocation(14).hasValue());
+  EXPECT_TRUE(Locs.hasLocations());
+  expectDumpResult(Locs, "reg12=[CFA+4]");
+  Locs.removeRegisterLocation(12);
+  EXPECT_FALSE(Locs.getRegisterLocation(12).hasValue());
+  EXPECT_FALSE(Locs.hasLocations());
+  expectDumpResult(Locs, "");
+}
+
+TEST(DWARFDebugFrame, UnwindTableErrorNonAscendingFDERows) {
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition.
+  constexpr uint8_t Reg = 12;
+  constexpr uint8_t Offset = 32;
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, Reg, Offset}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that have valid
+  // syntax, but will cause an error when we parse them into a UnwindTable.
+  // Here we encode two DW_CFA_set_loc opcodes:
+  //   DW_CFA_set_loc(0x1100)
+  //   DW_CFA_set_loc(0x1000)
+  // These opcodes cause a new row to be appended to the rows in a UnwindTable
+  // and the resulting rows are not in ascending address order and should cause
+  // a state machine error.
+  EXPECT_THAT_ERROR(
+      parseCFI(TestFDE, {dwarf::DW_CFA_set_loc, 0x00, 0x11, 0, 0, 0, 0, 0, 0,
+                         dwarf::DW_CFA_set_loc, 0x00, 0x10, 0, 0, 0, 0, 0, 0}),
+      Succeeded());
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(),
+                    FailedWithMessage("DW_CFA_set_loc with adrress 0x1000 which"
+                                      " must be greater than the current row "
+                                      "address 0x1100"));
+}
+
+TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_restore_state) {
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition.
+  constexpr uint8_t Reg = 12;
+  constexpr uint8_t Offset = 32;
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, Reg, Offset}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that have valid
+  // syntax, but will cause an error when we parse them into a UnwindTable.
+  // Here we encode a DW_CFA_restore_state opcode that was not preceded by a
+  // DW_CFA_remember_state, and an error should be returned.
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_restore_state}),
+                    Succeeded());
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(),
+                    FailedWithMessage("DW_CFA_restore_state without a matching "
+                                      "previous DW_CFA_remember_state"));
+}
+
+TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_GNU_window_save) {
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition.
+  constexpr uint8_t Reg = 12;
+  constexpr uint8_t Offset = 32;
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, Reg, Offset}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that have valid
+  // syntax, but will cause an error when we parse them into a UnwindTable.
+  // Here we encode a DW_CFA_GNU_window_save that is not supported. I have not
+  // found any documentation that describes what this does after some brief
+  // searching.
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_GNU_window_save}),
+                    Succeeded());
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(),
+                    FailedWithMessage("DW_CFA opcode 0x2d is not supported for "
+                                      "architecture x86_64"));
+}
+
+TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_def_cfa_offset) {
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has an invalid CFA definition. We do this so we can try
+  // and use a DW_CFA_def_cfa_register opcode in the FDE and get an appropriate
+  // error back.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that have valid
+  // syntax, but will cause an error when we parse them into a UnwindTable.
+  // Here we encode a DW_CFA_def_cfa_offset with a offset of 16, but our CIE
+  // didn't define the CFA in terms of a register plus offset, so this should
+  // cause an error.
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_def_cfa_offset, 16}),
+                    Succeeded());
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(),
+                    FailedWithMessage("DW_CFA_def_cfa_offset found when CFA "
+                                      "rule was not RegPlusOffset"));
+}
+
+TEST(DWARFDebugFrame, UnwindTableDefCFAOffsetSFCFAError) {
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has an invalid CFA definition. We do this so we can try
+  // and use a DW_CFA_def_cfa_offset_sf opcode in the FDE and get an
+  // appropriate error back.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that have valid
+  // syntax, but will cause an error when we parse them into a UnwindTable.
+  // Here we encode a DW_CFA_def_cfa_offset_sf with a offset of 4, but our CIE
+  // didn't define the CFA in terms of a register plus offset, so this should
+  // cause an error.
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_def_cfa_offset_sf, 4}),
+                    Succeeded());
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(),
+                    FailedWithMessage("DW_CFA_def_cfa_offset_sf found when CFA "
+                                      "rule was not RegPlusOffset"));
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa_register) {
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has only defines the CFA register with no offset. Some
+  // architectures do this and we must ensure that we set the CFA value to be
+  // equal to that register with no offset.
+  constexpr uint8_t CFAReg = 12;
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_register, CFAReg}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that have valid
+  // syntax, but will cause an error when we parse them into a UnwindTable.
+  // Here we encode a DW_CFA_def_cfa_register with a register number of 12, but
+  // our CIE didn't define the CFA in terms of a register plus offset, so this
+  // should cause an error.
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {}), Succeeded());
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 1u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getCFAValue(),
+            dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg, 0));
+}
+
+TEST(DWARFDebugFrame, UnwindTableRowPushingOpcodes) {
+  // Test all opcodes that should end up pushing a UnwindRow into a UnwindTable.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  constexpr uint8_t CFAReg = 12;
+  constexpr uint8_t CFAOffset = 32;
+  constexpr uint8_t Reg = 13;
+  constexpr uint8_t InReg = 14;
+
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg, CFAOffset,
+                                       dwarf::DW_CFA_register, Reg, InReg}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that use all of the
+  // row pushing opcodes. This will verify that all opcodes that should create
+  // a row are correctly working. Each opcode will push a row prior to
+  // advancing the address, and then a row will be automatically pushed at the
+  // end of the parsing, so we should end up with 6 rows starting at address
+  // 0x1000 (from the FDE) and incrementing each one by 4 * CodeAlignmentFactor
+  // from the CIE.
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_advance_loc | 4,
+                                       dwarf::DW_CFA_advance_loc1,
+                                       4,
+                                       dwarf::DW_CFA_advance_loc2,
+                                       4,
+                                       0,
+                                       dwarf::DW_CFA_advance_loc4,
+                                       4,
+                                       0,
+                                       0,
+                                       0,
+                                       dwarf::DW_CFA_set_loc,
+                                       0x14,
+                                       0x10,
+                                       0,
+                                       0,
+                                       0,
+                                       0,
+                                       0,
+                                       0}),
+                    Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+  VerifyLocs.setRegisterLocation(
+      Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  ASSERT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 6u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+  EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
+  EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs);
+  EXPECT_EQ(Rows[2].getAddress(), 0x1008u);
+  EXPECT_EQ(Rows[2].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs);
+  EXPECT_EQ(Rows[3].getAddress(), 0x100cu);
+  EXPECT_EQ(Rows[3].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs);
+  EXPECT_EQ(Rows[4].getAddress(), 0x1010u);
+  EXPECT_EQ(Rows[4].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs);
+  EXPECT_EQ(Rows[5].getAddress(), 0x1014u);
+  EXPECT_EQ(Rows[5].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[5].getRegisterLocations(), VerifyLocs);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore) {
+  // Test that DW_CFA_restore works as expected when parsed in the state
+  // machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  constexpr uint8_t CFAReg = 12;
+  constexpr uint8_t CFAOffset = 32;
+  constexpr uint8_t Reg = 13;
+  constexpr uint8_t InReg = 14;
+  constexpr int32_t RegCFAOffset = -8;
+
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg, CFAOffset,
+                                       dwarf::DW_CFA_register, Reg, InReg}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that changes the rule
+  // for register "Reg" to be [CFA-8], then push a row, and then restore the
+  // register unwind rule for "Reg" using DW_CFA_restore. We should end up with
+  // two rows:
+  //   - one with Reg = [CFA-8]
+  //   - one with Reg = InReg
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_offset | Reg, 1,
+                                       dwarf::DW_CFA_advance_loc | 4,
+                                       dwarf::DW_CFA_restore | Reg}),
+                    Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs1;
+  VerifyLocs1.setRegisterLocation(
+      Reg, dwarf::UnwindLocation::createAtCFAPlusOffset(RegCFAOffset));
+
+  dwarf::RegisterLocations VerifyLocs2;
+  VerifyLocs2.setRegisterLocation(
+      Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 2u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs1);
+  EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
+  EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs2);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore_extended) {
+  // Test that DW_CFA_restore works as expected when parsed in the state
+  // machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  constexpr uint8_t CFAReg = 12;
+  constexpr uint8_t CFAOffset = 32;
+  constexpr uint8_t Reg = 13;
+  constexpr uint8_t InReg = 14;
+  constexpr int32_t RegCFAOffset = -8;
+
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg, CFAOffset,
+                                       dwarf::DW_CFA_register, Reg, InReg}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that changes the rule
+  // for register "Reg" to be [CFA-8], then push a row, and then restore the
+  // register unwind rule for "Reg" using DW_CFA_restore_extended. We should
+  // end up with two rows:
+  //   - one with Reg = [CFA-8]
+  //   - one with Reg = InReg
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_offset | Reg, 1,
+                                       dwarf::DW_CFA_advance_loc | 4,
+                                       dwarf::DW_CFA_restore_extended, Reg}),
+                    Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs1;
+  VerifyLocs1.setRegisterLocation(
+      Reg, dwarf::UnwindLocation::createAtCFAPlusOffset(RegCFAOffset));
+
+  dwarf::RegisterLocations VerifyLocs2;
+  VerifyLocs2.setRegisterLocation(
+      Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 2u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs1);
+  EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
+  EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs2);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_offset) {
+  // Test that DW_CFA_offset, DW_CFA_offset_extended and
+  // DW_CFA_offset_extended_sf work as expected when parsed in the state
+  // machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that changes the
+  // unwind rules for the follwing registers:
+  //   Reg1 = [CFA-8]
+  //   Reg2 = [CFA-16]
+  //   Reg3 = [CFA+8]
+  constexpr uint32_t Reg1 = 14;
+  constexpr uint32_t Reg2 = 15;
+  constexpr uint32_t Reg3 = 16;
+  constexpr uint8_t Neg1SLEB = 0x7f;
+  EXPECT_THAT_ERROR(
+      parseCFI(TestFDE,
+               {dwarf::DW_CFA_offset | Reg1, 1, dwarf::DW_CFA_offset_extended,
+                Reg2, 2, dwarf::DW_CFA_offset_extended_sf, Reg3, Neg1SLEB}),
+      Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+  VerifyLocs.setRegisterLocation(
+      Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
+  VerifyLocs.setRegisterLocation(
+      Reg2, dwarf::UnwindLocation::createAtCFAPlusOffset(-16));
+  VerifyLocs.setRegisterLocation(
+      Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(8));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 1u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_offset) {
+  // Test that DW_CFA_val_offset and DW_CFA_val_offset_sf work as expected when
+  // parsed in the state machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that changes the
+  // unwind rules for the follwing registers:
+  //   Reg1 = [CFA-8]
+  //   Reg2 = [CFA-16]
+  //   Reg3 = [CFA+8]
+  constexpr uint32_t Reg1 = 14;
+  constexpr uint32_t Reg2 = 15;
+  constexpr uint8_t Neg1SLEB = 0x7f;
+  EXPECT_THAT_ERROR(
+      parseCFI(TestFDE, {dwarf::DW_CFA_val_offset, Reg1, 1,
+                         dwarf::DW_CFA_val_offset_sf, Reg2, Neg1SLEB}),
+      Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+  VerifyLocs.setRegisterLocation(
+      Reg1, dwarf::UnwindLocation::createIsCFAPlusOffset(-8));
+  VerifyLocs.setRegisterLocation(
+      Reg2, dwarf::UnwindLocation::createIsCFAPlusOffset(8));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 1u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_nop) {
+  // Test that DW_CFA_nop works as expected when parsed in the state machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that changes the
+  // unwind rules for the follwing registers:
+  //   Reg1 = [CFA-8]
+  // The opcodes for setting Reg1 are preceded by a DW_CFA_nop.
+  constexpr uint32_t Reg1 = 14;
+  EXPECT_THAT_ERROR(
+      parseCFI(TestFDE, {dwarf::DW_CFA_nop, dwarf::DW_CFA_offset | Reg1, 1}),
+      Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+  VerifyLocs.setRegisterLocation(
+      Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 1u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_remember_state) {
+  // Test that DW_CFA_remember_state and DW_CFA_restore_state work as expected
+  // when parsed in the state machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that encodes the
+  // follwing rows:
+  // 0x1000: CFA=reg12+32: Reg1=[CFA-8]
+  // 0x1004: CFA=reg12+32: Reg1=[CFA-8] Reg2=[CFA-16]
+  // 0x1008: CFA=reg12+32: Reg1=[CFA-8] Reg2=[CFA-16] Reg3=[CFA-24]
+  // 0x100C: CFA=reg12+32: Reg1=[CFA-8] Reg2=[CFA-16]
+  // 0x1010: CFA=reg12+32: Reg1=[CFA-8]
+  // This state machine will:
+  //  - set Reg1 location
+  //  - push a row (from DW_CFA_advance_loc)
+  //  - remember the state
+  //  - set Reg2 location
+  //  - push a row (from DW_CFA_advance_loc)
+  //  - remember the state
+  //  - set Reg3 location
+  //  - push a row (from DW_CFA_advance_loc)
+  //  - remember the state where Reg1 and Reg2 were set
+  //  - push a row (from DW_CFA_advance_loc)
+  //  - remember the state where only Reg1 was set
+  //  - push a row (automatically at the end of instruction parsing)
+  // Then we verify that all registers are correct in all generated rows.
+  constexpr uint32_t Reg1 = 14;
+  constexpr uint32_t Reg2 = 15;
+  constexpr uint32_t Reg3 = 16;
+  EXPECT_THAT_ERROR(
+      parseCFI(TestFDE,
+               {dwarf::DW_CFA_offset | Reg1, 1, dwarf::DW_CFA_advance_loc | 4,
+                dwarf::DW_CFA_remember_state, dwarf::DW_CFA_offset | Reg2, 2,
+                dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_remember_state,
+                dwarf::DW_CFA_offset | Reg3, 3, dwarf::DW_CFA_advance_loc | 4,
+                dwarf::DW_CFA_restore_state, dwarf::DW_CFA_advance_loc | 4,
+                dwarf::DW_CFA_restore_state}),
+      Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs1;
+  VerifyLocs1.setRegisterLocation(
+      Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
+
+  dwarf::RegisterLocations VerifyLocs2;
+  VerifyLocs2.setRegisterLocation(
+      Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
+  VerifyLocs2.setRegisterLocation(
+      Reg2, dwarf::UnwindLocation::createAtCFAPlusOffset(-16));
+
+  dwarf::RegisterLocations VerifyLocs3;
+  VerifyLocs3.setRegisterLocation(
+      Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
+  VerifyLocs3.setRegisterLocation(
+      Reg2, dwarf::UnwindLocation::createAtCFAPlusOffset(-16));
+  VerifyLocs3.setRegisterLocation(
+      Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(-24));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 5u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs1);
+
+  EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
+  EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs2);
+
+  EXPECT_EQ(Rows[2].getAddress(), 0x1008u);
+  EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs3);
+
+  EXPECT_EQ(Rows[3].getAddress(), 0x100Cu);
+  EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs2);
+
+  EXPECT_EQ(Rows[4].getAddress(), 0x1010u);
+  EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs1);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_undefined) {
+  // Test that DW_CFA_undefined works as expected when parsed in the state
+  // machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that encodes the
+  // follwing rows:
+  // 0x1000: CFA=reg12+32: Reg1=undefined
+  // Then we verify that all registers are correct in all generated rows.
+  constexpr uint32_t Reg1 = 14;
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_undefined, Reg1}),
+                    Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+  VerifyLocs.setRegisterLocation(Reg1,
+                                 dwarf::UnwindLocation::createUndefined());
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 1u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_same_value) {
+  // Test that DW_CFA_same_value works as expected when parsed in the state
+  // machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that encodes the
+  // follwing rows:
+  // 0x1000: CFA=reg12+32: Reg1=same
+  // Then we verify that all registers are correct in all generated rows.
+  constexpr uint32_t Reg1 = 14;
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_same_value, Reg1}),
+                    Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+  VerifyLocs.setRegisterLocation(Reg1, dwarf::UnwindLocation::createSame());
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 1u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_register) {
+  // Test that DW_CFA_register works as expected when parsed in the state
+  // machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that encodes the
+  // follwing rows:
+  // 0x1000: CFA=reg12+32: Reg1=same
+  // Then we verify that all registers are correct in all generated rows.
+  constexpr uint8_t Reg = 13;
+  constexpr uint8_t InReg = 14;
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_register, Reg, InReg}),
+                    Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+  VerifyLocs.setRegisterLocation(
+      Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 1u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_expression) {
+  // Test that DW_CFA_expression works as expected when parsed in the state
+  // machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that encodes the
+  // follwing rows:
+  // 0x1000: CFA=reg12+32: Reg1=DWARFExpr(DW_OP_reg12)
+  // Then we verify that all registers are correct in all generated rows.
+  constexpr uint8_t Reg = 13;
+  constexpr uint8_t AddrSize = 8;
+  std::vector<uint8_t> CFIBytes = {dwarf::DW_CFA_expression, Reg, 1,
+                                   dwarf::DW_OP_reg12};
+
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, CFIBytes), Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+
+  std::vector<uint8_t> ExprBytes = {dwarf::DW_OP_reg12};
+  DataExtractor ExprData(ExprBytes, true, AddrSize);
+  DWARFExpression Expr(ExprData, AddrSize);
+  VerifyLocs.setRegisterLocation(
+      Reg, dwarf::UnwindLocation::createAtDWARFExpression(Expr));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 1u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_expression) {
+  // Test that DW_CFA_val_expression works as expected when parsed in the state
+  // machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that encodes the
+  // follwing rows:
+  // 0x1000: CFA=reg12+32: Reg1=DWARFExpr(DW_OP_reg12)
+  // Then we verify that all registers are correct in all generated rows.
+  constexpr uint8_t Reg = 13;
+  constexpr uint8_t AddrSize = 8;
+  std::vector<uint8_t> CFIBytes = {dwarf::DW_CFA_val_expression, Reg, 1,
+                                   dwarf::DW_OP_reg12};
+
+  EXPECT_THAT_ERROR(parseCFI(TestFDE, CFIBytes), Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+
+  std::vector<uint8_t> ExprBytes = {dwarf::DW_OP_reg12};
+  DataExtractor ExprData(ExprBytes, true, AddrSize);
+  DWARFExpression Expr(ExprData, AddrSize);
+  VerifyLocs.setRegisterLocation(
+      Reg, dwarf::UnwindLocation::createIsDWARFExpression(Expr));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 1u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+}
+
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa) {
+  // Test that DW_CFA_def_cfa, DW_CFA_def_cfa_sf, DW_CFA_def_cfa_register,
+  // DW_CFA_def_cfa_offset, and DW_CFA_def_cfa_offset_sf works as expected when
+  // parsed in the state machine.
+  dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
+                                 /*Offset=*/0x0,
+                                 /*Length=*/0xff);
+
+  dwarf::FDE TestFDE(/*IsDWARF64=*/true,
+                     /*Offset=*/0x3333abcdabcd,
+                     /*Length=*/0x4444abcdabcd,
+                     /*CIEPointer=*/0x1111abcdabcd,
+                     /*InitialLocation=*/0x1000,
+                     /*AddressRange=*/0x1000,
+                     /*Cie=*/&TestCIE,
+                     /*LSDAAddress=*/None,
+                     /*Arch=*/Triple::x86_64);
+
+  // Make a CIE that has a valid CFA definition and a single register unwind
+  // rule for register that we will verify is in all of the pushed rows.
+  constexpr uint8_t CFAReg1 = 12;
+  constexpr uint8_t CFAOff1 = 32;
+  constexpr uint8_t CFAReg2 = 13;
+  constexpr uint8_t CFAOff2 = 48;
+  constexpr uint8_t Reg = 13;
+  constexpr uint8_t InReg = 14;
+
+  EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg1, CFAOff1,
+                                       dwarf::DW_CFA_register, Reg, InReg}),
+                    Succeeded());
+
+  // Make a FDE with DWARF call frame instruction opcodes that use all of the
+  // DW_CFA_def_cfa* opcodes. This will verify that all opcodes that should
+  // create a row are correctly working.
+  EXPECT_THAT_ERROR(
+      parseCFI(
+          TestFDE,
+          {
+              dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_register,
+              CFAReg2, dwarf::DW_CFA_advance_loc | 4,
+              dwarf::DW_CFA_def_cfa_offset, CFAOff2,
+              dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_offset_sf,
+              0x7c, // -4 SLEB to make offset = 32 (CFAOff1)
+              dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_sf, CFAReg1,
+              0x7a, // -6 SLEB to make CFA offset 48 (CFAOff2)
+          }),
+      Succeeded());
+
+  // Create locations that we expect the UnwindRow objects to contain after
+  // parsing the DWARF call frame instructions.
+  dwarf::RegisterLocations VerifyLocs;
+  VerifyLocs.setRegisterLocation(
+      Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
+
+  // Verify we catch state machine error.
+  Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
+  EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
+  const dwarf::UnwindTable &Rows = RowsOrErr.get();
+  EXPECT_EQ(Rows.size(), 5u);
+  EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
+  EXPECT_EQ(
+      Rows[0].getCFAValue(),
+      dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg1, CFAOff1));
+  EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
+
+  EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
+  EXPECT_EQ(
+      Rows[1].getCFAValue(),
+      dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff1));
+  EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs);
+
+  EXPECT_EQ(Rows[2].getAddress(), 0x1008u);
+  EXPECT_EQ(
+      Rows[2].getCFAValue(),
+      dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff2));
+  EXPECT_EQ(Rows[2].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs);
+
+  EXPECT_EQ(Rows[3].getAddress(), 0x100cu);
+  EXPECT_EQ(
+      Rows[3].getCFAValue(),
+      dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff1));
+  EXPECT_EQ(Rows[3].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs);
+
+  EXPECT_EQ(Rows[4].getAddress(), 0x1010u);
+  EXPECT_EQ(
+      Rows[4].getCFAValue(),
+      dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg1, CFAOff2));
+  EXPECT_EQ(Rows[4].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs);
+}
+
 } // end anonymous namespace


        


More information about the llvm-commits mailing list