[llvm] 167e7af - Implement DW_CFA_LLVM_* for Heterogeneous Debugging

via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 13 20:22:09 PDT 2021


Author: RamNalamothu
Date: 2021-06-14T08:51:50+05:30
New Revision: 167e7afcd52bc1438d60320ec1d1bc53b8eae4a3

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

LOG: Implement DW_CFA_LLVM_* for Heterogeneous Debugging

Add support in MC/MIR for writing/parsing, and DebugInfo.

This is part of the Extensions for Heterogeneous Debugging defined at
https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html

Specifically the CFI instructions implemented here are defined at
https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html#cfa-definition-instructions

Reviewed By: clayborg

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

Added: 
    llvm/test/MC/ELF/cfi-llvm-def-cfa-aspace-errors.s
    llvm/test/MC/ELF/cfi-llvm-def-cfa-aspace.s
    llvm/test/tools/llvm-dwarfdump/X86/debug_frame_LLVM_def_cfa_aspace.s

Modified: 
    llvm/include/llvm/BinaryFormat/Dwarf.def
    llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
    llvm/include/llvm/MC/MCDwarf.h
    llvm/include/llvm/MC/MCStreamer.h
    llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
    llvm/lib/CodeGen/CFIInstrInserter.cpp
    llvm/lib/CodeGen/MIRParser/MILexer.cpp
    llvm/lib/CodeGen/MIRParser/MILexer.h
    llvm/lib/CodeGen/MIRParser/MIParser.cpp
    llvm/lib/CodeGen/MachineOperand.cpp
    llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
    llvm/lib/MC/MCAsmStreamer.cpp
    llvm/lib/MC/MCDwarf.cpp
    llvm/lib/MC/MCParser/AsmParser.cpp
    llvm/lib/MC/MCStreamer.cpp
    llvm/test/CodeGen/MIR/AArch64/cfi.mir
    llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def
index c863cf7d66b45..34f124b5779a2 100644
--- a/llvm/include/llvm/BinaryFormat/Dwarf.def
+++ b/llvm/include/llvm/BinaryFormat/Dwarf.def
@@ -1114,6 +1114,10 @@ HANDLE_DW_CFA_PRED(0x1d, MIPS_advance_loc8, SELECT_MIPS64)
 HANDLE_DW_CFA_PRED(0x2d, GNU_window_save, SELECT_SPARC)
 HANDLE_DW_CFA_PRED(0x2d, AARCH64_negate_ra_state, SELECT_AARCH64)
 HANDLE_DW_CFA_PRED(0x2e, GNU_args_size, SELECT_X86)
+// Heterogeneous Debugging Extension defined at
+// https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html#cfa-definition-instructions
+HANDLE_DW_CFA(0x30, LLVM_def_aspace_cfa)
+HANDLE_DW_CFA(0x31, LLVM_def_aspace_cfa_sf)
 
 // Apple Objective-C Property Attributes.
 // Keep this list in sync with clang's DeclObjCCommon.h

diff  --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
index db6a13824a66b..536583e20640b 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h
@@ -46,11 +46,12 @@ class UnwindLocation {
     ///   reg = CFA + offset
     ///   reg = defef(CFA + offset)
     CFAPlusOffset,
-    /// Register it in or at a register plus offset:
-    ///   reg = reg + offset
-    ///   reg = deref(reg + offset)
+    /// Register or CFA is in or at a register plus offset, optionally in
+    /// an address space:
+    ///   reg = reg + offset [in addrspace]
+    ///   reg = deref(reg + offset [in addrspace])
     RegPlusOffset,
-    /// Register value is in or at a value found by evaluating a DWARF
+    /// Register or CFA value is in or at a value found by evaluating a DWARF
     /// expression:
     ///   reg = eval(dwarf_expr)
     ///   reg = deref(eval(dwarf_expr))
@@ -64,6 +65,8 @@ class UnwindLocation {
   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<uint32_t> AddrSpace; /// The address space for Kind == RegPlusOffset
+                                /// for CFA.
   Optional<DWARFExpression> Expr; /// The DWARF expression for Kind ==
                                   /// DWARFExpression.
   bool Dereference; /// If true, the resulting location must be dereferenced
@@ -72,10 +75,12 @@ class UnwindLocation {
   // Constructors are private to force people to use the create static
   // functions.
   UnwindLocation(Location K)
-      : Kind(K), RegNum(InvalidRegisterNumber), Offset(0), Dereference(false) {}
+      : Kind(K), RegNum(InvalidRegisterNumber), Offset(0), AddrSpace(None),
+        Dereference(false) {}
 
-  UnwindLocation(Location K, uint32_t Reg, int32_t Off, bool Deref)
-      : Kind(K), RegNum(Reg), Offset(Off), Dereference(Deref) {}
+  UnwindLocation(Location K, uint32_t Reg, int32_t Off, Optional<uint32_t> AS,
+                 bool Deref)
+      : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {}
 
   UnwindLocation(DWARFExpression E, bool Deref)
       : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E),
@@ -101,14 +106,19 @@ class UnwindLocation {
   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.
+  /// (Deref == true) a regiser plus an offset and, optionally, in the specified
+  /// address space (used mostly for the CFA).
   ///
   /// The CFA is usually defined using this rule by using the stack pointer or
   /// frame pointer as the register, with an offset that accounts for all
   /// spilled registers and all local variables in a function, and Deref ==
   /// false.
-  static UnwindLocation createIsRegisterPlusOffset(uint32_t Reg, int32_t Off);
-  static UnwindLocation createAtRegisterPlusOffset(uint32_t Reg, int32_t Off);
+  static UnwindLocation
+  createIsRegisterPlusOffset(uint32_t Reg, int32_t Off,
+                             Optional<uint32_t> AddrSpace = None);
+  static UnwindLocation
+  createAtRegisterPlusOffset(uint32_t Reg, int32_t Off,
+                             Optional<uint32_t> AddrSpace = None);
   /// 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.
@@ -119,13 +129,17 @@ class UnwindLocation {
   Location getLocation() const { return Kind; }
   uint32_t getRegister() const { return RegNum; }
   int32_t getOffset() const { return Offset; }
+  uint32_t getAddressSpace() const {
+    assert(Kind == RegPlusOffset && AddrSpace.hasValue());
+    return *AddrSpace;
+  }
   int32_t getConstant() const { return Offset; }
   /// Some opcodes will modify the CFA location's register only, so we need
   /// to be able to modify the CFA register when evaluating DWARF Call Frame
   /// Information opcodes.
   void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; }
   /// Some opcodes will modify the CFA location's offset only, so we need
-  /// to be able to modify the CFA register when evaluating DWARF Call Frame
+  /// to be able to modify the CFA offset when evaluating DWARF Call Frame
   /// Information opcodes.
   void setOffset(int32_t NewOffset) { Offset = NewOffset; }
   /// Some opcodes modify a constant value and we need to be able to update
@@ -388,7 +402,8 @@ raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows);
 /// manual, "6.4.1 Structure of Call Frame Information".
 class CFIProgram {
 public:
-  typedef SmallVector<uint64_t, 2> Operands;
+  static constexpr size_t MaxOperands = 3;
+  typedef SmallVector<uint64_t, MaxOperands> Operands;
 
   /// An instruction consists of a DWARF CFI opcode and an optional sequence of
   /// operands. If it refers to an expression, then this expression has its own
@@ -467,6 +482,15 @@ class CFIProgram {
     Instructions.back().Ops.push_back(Operand2);
   }
 
+  /// Add a new instruction that has three operands.
+  void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2,
+                      uint64_t Operand3) {
+    Instructions.push_back(Instruction(Opcode));
+    Instructions.back().Ops.push_back(Operand1);
+    Instructions.back().Ops.push_back(Operand2);
+    Instructions.back().Ops.push_back(Operand3);
+  }
+
   /// Types of operands to CFI instructions
   /// In DWARF, this type is implicitly tied to a CFI instruction opcode and
   /// thus this type doesn't need to be explictly written to the file (this is
@@ -482,6 +506,7 @@ class CFIProgram {
     OT_SignedFactDataOffset,
     OT_UnsignedFactDataOffset,
     OT_Register,
+    OT_AddressSpace,
     OT_Expression
   };
 
@@ -490,7 +515,7 @@ class CFIProgram {
 
   /// Retrieve the array describing the types of operands according to the enum
   /// above. This is indexed by opcode.
-  static ArrayRef<OperandType[2]> getOperandTypes();
+  static ArrayRef<OperandType[MaxOperands]> getOperandTypes();
 
   /// Print \p Opcode's operand number \p OperandIdx which has value \p Operand.
   void printOperand(raw_ostream &OS, DIDumpOptions DumpOpts,

diff  --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h
index 625e83a37da91..fd94d6758f22d 100644
--- a/llvm/include/llvm/MC/MCDwarf.h
+++ b/llvm/include/llvm/MC/MCDwarf.h
@@ -445,6 +445,7 @@ class MCCFIInstruction {
     OpRememberState,
     OpRestoreState,
     OpOffset,
+    OpLLVMDefAspaceCfa,
     OpDefCfaRegister,
     OpDefCfaOffset,
     OpDefCfa,
@@ -467,6 +468,7 @@ class MCCFIInstruction {
     int Offset;
     unsigned Register2;
   };
+  unsigned AddressSpace;
   std::vector<char> Values;
   std::string Comment;
 
@@ -474,7 +476,7 @@ class MCCFIInstruction {
                    StringRef Comment = "")
       : Operation(Op), Label(L), Register(R), Offset(O),
         Values(V.begin(), V.end()), Comment(Comment) {
-    assert(Op != OpRegister);
+    assert(Op != OpRegister && Op != OpLLVMDefAspaceCfa);
   }
 
   MCCFIInstruction(OpType Op, MCSymbol *L, unsigned R1, unsigned R2)
@@ -482,6 +484,11 @@ class MCCFIInstruction {
     assert(Op == OpRegister);
   }
 
+  MCCFIInstruction(OpType Op, MCSymbol *L, unsigned R, int O, unsigned AS)
+      : Operation(Op), Label(L), Register(R), Offset(O), AddressSpace(AS) {
+    assert(Op == OpLLVMDefAspaceCfa);
+  }
+
 public:
   /// .cfi_def_cfa defines a rule for computing CFA as: take address from
   /// Register and add Offset to it.
@@ -510,6 +517,17 @@ class MCCFIInstruction {
     return MCCFIInstruction(OpAdjustCfaOffset, L, 0, Adjustment, "");
   }
 
+  // FIXME: Update the remaining docs to use the new proposal wording.
+  /// .cfi_llvm_def_aspace_cfa defines the rule for computing the CFA to
+  /// be the result of evaluating the DWARF operation expression
+  /// `DW_OP_constu AS; DW_OP_aspace_bregx R, B` as a location description.
+  static MCCFIInstruction createLLVMDefAspaceCfa(MCSymbol *L, unsigned Register,
+                                                 int Offset,
+                                                 unsigned AddressSpace) {
+    return MCCFIInstruction(OpLLVMDefAspaceCfa, L, Register, Offset,
+                            AddressSpace);
+  }
+
   /// .cfi_offset Previous value of Register is saved at offset Offset
   /// from CFA.
   static MCCFIInstruction createOffset(MCSymbol *L, unsigned Register,
@@ -590,7 +608,8 @@ class MCCFIInstruction {
     assert(Operation == OpDefCfa || Operation == OpOffset ||
            Operation == OpRestore || Operation == OpUndefined ||
            Operation == OpSameValue || Operation == OpDefCfaRegister ||
-           Operation == OpRelOffset || Operation == OpRegister);
+           Operation == OpRelOffset || Operation == OpRegister ||
+           Operation == OpLLVMDefAspaceCfa);
     return Register;
   }
 
@@ -599,10 +618,16 @@ class MCCFIInstruction {
     return Register2;
   }
 
+  unsigned getAddressSpace() const {
+    assert(Operation == OpLLVMDefAspaceCfa);
+    return AddressSpace;
+  }
+
   int getOffset() const {
     assert(Operation == OpDefCfa || Operation == OpOffset ||
            Operation == OpRelOffset || Operation == OpDefCfaOffset ||
-           Operation == OpAdjustCfaOffset || Operation == OpGnuArgsSize);
+           Operation == OpAdjustCfaOffset || Operation == OpGnuArgsSize ||
+           Operation == OpLLVMDefAspaceCfa);
     return Offset;
   }
 

diff  --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h
index f4ebadbd33d8c..386726654c53e 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -979,6 +979,8 @@ class MCStreamer {
   virtual void emitCFIDefCfa(int64_t Register, int64_t Offset);
   virtual void emitCFIDefCfaOffset(int64_t Offset);
   virtual void emitCFIDefCfaRegister(int64_t Register);
+  virtual void emitCFILLVMDefAspaceCfa(int64_t Register, int64_t Offset,
+                                       int64_t AddressSpace);
   virtual void emitCFIOffset(int64_t Register, int64_t Offset);
   virtual void emitCFIPersonality(const MCSymbol *Sym, unsigned Encoding);
   virtual void emitCFILsda(const MCSymbol *Sym, unsigned Encoding);

diff  --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
index da754019e0d58..fc127f4cf9da6 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
@@ -245,6 +245,10 @@ void AsmPrinter::emitCFIInstruction(const MCCFIInstruction &Inst) const {
   case MCCFIInstruction::OpDefCfaRegister:
     OutStreamer->emitCFIDefCfaRegister(Inst.getRegister());
     break;
+  case MCCFIInstruction::OpLLVMDefAspaceCfa:
+    OutStreamer->emitCFILLVMDefAspaceCfa(Inst.getRegister(), Inst.getOffset(),
+                                         Inst.getAddressSpace());
+    break;
   case MCCFIInstruction::OpOffset:
     OutStreamer->emitCFIOffset(Inst.getRegister(), Inst.getOffset());
     break;

diff  --git a/llvm/lib/CodeGen/CFIInstrInserter.cpp b/llvm/lib/CodeGen/CFIInstrInserter.cpp
index c6244419d6d16..1c2e3f9984496 100644
--- a/llvm/lib/CodeGen/CFIInstrInserter.cpp
+++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp
@@ -219,6 +219,14 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
       case MCCFIInstruction::OpRestore:
         CSRRestored.set(CFI.getRegister());
         break;
+      case MCCFIInstruction::OpLLVMDefAspaceCfa:
+        // TODO: Add support for handling cfi_def_aspace_cfa.
+#ifndef NDEBUG
+        report_fatal_error(
+            "Support for cfi_llvm_def_aspace_cfa not implemented! Value of CFA "
+            "may be incorrect!\n");
+#endif
+        break;
       case MCCFIInstruction::OpRememberState:
         // TODO: Add support for handling cfi_remember_state.
 #ifndef NDEBUG

diff  --git a/llvm/lib/CodeGen/MIRParser/MILexer.cpp b/llvm/lib/CodeGen/MIRParser/MILexer.cpp
index 1a890b0ead0b3..e6866f04986f0 100644
--- a/llvm/lib/CodeGen/MIRParser/MILexer.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MILexer.cpp
@@ -226,6 +226,7 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) {
       .Case("adjust_cfa_offset", MIToken::kw_cfi_adjust_cfa_offset)
       .Case("escape", MIToken::kw_cfi_escape)
       .Case("def_cfa", MIToken::kw_cfi_def_cfa)
+      .Case("llvm_def_aspace_cfa", MIToken::kw_cfi_llvm_def_aspace_cfa)
       .Case("remember_state", MIToken::kw_cfi_remember_state)
       .Case("restore", MIToken::kw_cfi_restore)
       .Case("restore_state", MIToken::kw_cfi_restore_state)

diff  --git a/llvm/lib/CodeGen/MIRParser/MILexer.h b/llvm/lib/CodeGen/MIRParser/MILexer.h
index 82f89971609de..5d7ea8fb66284 100644
--- a/llvm/lib/CodeGen/MIRParser/MILexer.h
+++ b/llvm/lib/CodeGen/MIRParser/MILexer.h
@@ -83,6 +83,7 @@ struct MIToken {
     kw_cfi_adjust_cfa_offset,
     kw_cfi_escape,
     kw_cfi_def_cfa,
+    kw_cfi_llvm_def_aspace_cfa,
     kw_cfi_register,
     kw_cfi_remember_state,
     kw_cfi_restore,

diff  --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index 5742080e11c64..62aad55ba1602 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -472,6 +472,7 @@ class MIParser {
   bool parseMetadataOperand(MachineOperand &Dest);
   bool parseCFIOffset(int &Offset);
   bool parseCFIRegister(Register &Reg);
+  bool parseCFIAddressSpace(unsigned &AddressSpace);
   bool parseCFIEscapeValues(std::string& Values);
   bool parseCFIOperand(MachineOperand &Dest);
   bool parseIRBlock(BasicBlock *&BB, const Function &F);
@@ -2207,6 +2208,16 @@ bool MIParser::parseCFIRegister(Register &Reg) {
   return false;
 }
 
+bool MIParser::parseCFIAddressSpace(unsigned &AddressSpace) {
+  if (Token.isNot(MIToken::IntegerLiteral))
+    return error("expected a cfi address space literal");
+  if (Token.integerValue().isSigned())
+    return error("expected an unsigned integer (cfi address space)");
+  AddressSpace = Token.integerValue().getZExtValue();
+  lex();
+  return false;
+}
+
 bool MIParser::parseCFIEscapeValues(std::string &Values) {
   do {
     if (Token.isNot(MIToken::HexLiteral))
@@ -2227,6 +2238,7 @@ bool MIParser::parseCFIOperand(MachineOperand &Dest) {
   lex();
   int Offset;
   Register Reg;
+  unsigned AddressSpace;
   unsigned CFIIndex;
   switch (Kind) {
   case MIToken::kw_cfi_same_value:
@@ -2273,6 +2285,14 @@ bool MIParser::parseCFIOperand(MachineOperand &Dest) {
     CFIIndex =
         MF.addFrameInst(MCCFIInstruction::cfiDefCfa(nullptr, Reg, Offset));
     break;
+  case MIToken::kw_cfi_llvm_def_aspace_cfa:
+    if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) ||
+        parseCFIOffset(Offset) || expectAndConsume(MIToken::comma) ||
+        parseCFIAddressSpace(AddressSpace))
+      return true;
+    CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMDefAspaceCfa(
+        nullptr, Reg, Offset, AddressSpace));
+    break;
   case MIToken::kw_cfi_remember_state:
     CFIIndex = MF.addFrameInst(MCCFIInstruction::createRememberState(nullptr));
     break;
@@ -2620,6 +2640,7 @@ bool MIParser::parseMachineOperand(const unsigned OpCode, const unsigned OpIdx,
   case MIToken::kw_cfi_adjust_cfa_offset:
   case MIToken::kw_cfi_escape:
   case MIToken::kw_cfi_def_cfa:
+  case MIToken::kw_cfi_llvm_def_aspace_cfa:
   case MIToken::kw_cfi_register:
   case MIToken::kw_cfi_remember_state:
   case MIToken::kw_cfi_restore:

diff  --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp
index 766915cd0871a..c0ae0d27f4291 100644
--- a/llvm/lib/CodeGen/MachineOperand.cpp
+++ b/llvm/lib/CodeGen/MachineOperand.cpp
@@ -653,6 +653,14 @@ static void printCFI(raw_ostream &OS, const MCCFIInstruction &CFI,
     printCFIRegister(CFI.getRegister(), OS, TRI);
     OS << ", " << CFI.getOffset();
     break;
+  case MCCFIInstruction::OpLLVMDefAspaceCfa:
+    OS << "llvm_def_aspace_cfa ";
+    if (MCSymbol *Label = CFI.getLabel())
+      MachineOperand::printSymbol(OS, *Label);
+    printCFIRegister(CFI.getRegister(), OS, TRI);
+    OS << ", " << CFI.getOffset();
+    OS << ", " << CFI.getAddressSpace();
+    break;
   case MCCFIInstruction::OpRelOffset:
     OS << "rel_offset ";
     if (MCSymbol *Label = CFI.getLabel())

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
index 1e087d6dedab2..92a461dbd9411 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
@@ -48,24 +48,27 @@ UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; }
 UnwindLocation UnwindLocation::createSame() { return {Same}; }
 
 UnwindLocation UnwindLocation::createIsConstant(int32_t Value) {
-  return {Constant, InvalidRegisterNumber, Value, false};
+  return {Constant, InvalidRegisterNumber, Value, None, false};
 }
 
 UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) {
-  return {CFAPlusOffset, InvalidRegisterNumber, Offset, false};
+  return {CFAPlusOffset, InvalidRegisterNumber, Offset, None, false};
 }
 
 UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) {
-  return {CFAPlusOffset, InvalidRegisterNumber, Offset, true};
+  return {CFAPlusOffset, InvalidRegisterNumber, Offset, None, true};
 }
 
-UnwindLocation UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum,
-                                                          int32_t Offset) {
-  return {RegPlusOffset, RegNum, Offset, false};
+UnwindLocation
+UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
+                                           Optional<uint32_t> AddrSpace) {
+  return {RegPlusOffset, RegNum, Offset, AddrSpace, false};
 }
-UnwindLocation UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum,
-                                                          int32_t Offset) {
-  return {RegPlusOffset, RegNum, Offset, true};
+
+UnwindLocation
+UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset,
+                                           Optional<uint32_t> AddrSpace) {
+  return {RegPlusOffset, RegNum, Offset, AddrSpace, true};
 }
 
 UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) {
@@ -100,11 +103,13 @@ void UnwindLocation::dump(raw_ostream &OS, const MCRegisterInfo *MRI,
     break;
   case RegPlusOffset:
     printRegister(OS, MRI, IsEH, RegNum);
-    if (Offset == 0)
+    if (Offset == 0 && !AddrSpace)
       break;
-    if (Offset > 0)
+    if (Offset >= 0)
       OS << "+";
     OS << Offset;
+    if (AddrSpace)
+      OS << " in addrspace" << *AddrSpace;
     break;
   case DWARFExpr:
     Expr->print(OS, DIDumpOptions(), MRI, nullptr, IsEH);
@@ -313,6 +318,16 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
       // Operands: SLEB128
       addInstruction(Opcode, Data.getSLEB128(C));
       break;
+    case DW_CFA_LLVM_def_aspace_cfa:
+    case DW_CFA_LLVM_def_aspace_cfa_sf: {
+      auto RegNum = Data.getULEB128(C);
+      auto CfaOffset = Opcode == DW_CFA_LLVM_def_aspace_cfa
+                           ? Data.getULEB128(C)
+                           : Data.getSLEB128(C);
+      auto AddressSpace = Data.getULEB128(C);
+      addInstruction(Opcode, RegNum, CfaOffset, AddressSpace);
+      break;
+    }
     case DW_CFA_offset_extended:
     case DW_CFA_register:
     case DW_CFA_def_cfa:
@@ -390,6 +405,7 @@ const char *CFIProgram::operandTypeString(CFIProgram::OperandType OT) {
     ENUM_TO_CSTR(OT_SignedFactDataOffset);
     ENUM_TO_CSTR(OT_UnsignedFactDataOffset);
     ENUM_TO_CSTR(OT_Register);
+    ENUM_TO_CSTR(OT_AddressSpace);
     ENUM_TO_CSTR(OT_Expression);
   }
   return "<unknown CFIProgram::OperandType>";
@@ -398,7 +414,7 @@ const char *CFIProgram::operandTypeString(CFIProgram::OperandType OT) {
 llvm::Expected<uint64_t>
 CFIProgram::Instruction::getOperandAsUnsigned(const CFIProgram &CFIP,
                                               uint32_t OperandIdx) const {
-  if (OperandIdx >= 2)
+  if (OperandIdx >= MaxOperands)
     return createStringError(errc::invalid_argument,
                              "operand index %" PRIu32 " is not valid",
                              OperandIdx);
@@ -423,6 +439,7 @@ CFIProgram::Instruction::getOperandAsUnsigned(const CFIProgram &CFIP,
 
   case OT_Address:
   case OT_Register:
+  case OT_AddressSpace:
     return Operand;
 
   case OT_FactoredCodeOffset: {
@@ -442,7 +459,7 @@ CFIProgram::Instruction::getOperandAsUnsigned(const CFIProgram &CFIP,
 llvm::Expected<int64_t>
 CFIProgram::Instruction::getOperandAsSigned(const CFIProgram &CFIP,
                                             uint32_t OperandIdx) const {
-  if (OperandIdx >= 2)
+  if (OperandIdx >= MaxOperands)
     return createStringError(errc::invalid_argument,
                              "operand index %" PRIu32 " is not valid",
                              OperandIdx);
@@ -458,6 +475,7 @@ CFIProgram::Instruction::getOperandAsSigned(const CFIProgram &CFIP,
 
   case OT_Address:
   case OT_Register:
+  case OT_AddressSpace:
     return createStringError(
         errc::invalid_argument,
         "op[%" PRIu32 "] has OperandType %s which produces an unsigned result, "
@@ -745,6 +763,23 @@ Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
       break;
     }
 
+    case dwarf::DW_CFA_LLVM_def_aspace_cfa:
+    case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: {
+      llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
+      if (!RegNum)
+        return RegNum.takeError();
+      llvm::Expected<int64_t> Offset = Inst.getOperandAsSigned(CFIP, 1);
+      if (!Offset)
+        return Offset.takeError();
+      llvm::Expected<uint32_t> CFAAddrSpace =
+          Inst.getOperandAsUnsigned(CFIP, 2);
+      if (!CFAAddrSpace)
+        return CFAAddrSpace.takeError();
+      Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset(
+          *RegNum, *Offset, *CFAAddrSpace);
+      break;
+    }
+
     case dwarf::DW_CFA_def_cfa_expression:
       Row.getCFAValue() =
           UnwindLocation::createIsDWARFExpression(*Inst.Expression);
@@ -754,19 +789,23 @@ Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
   return Error::success();
 }
 
-ArrayRef<CFIProgram::OperandType[2]> CFIProgram::getOperandTypes() {
-  static OperandType OpTypes[DW_CFA_restore+1][2];
+ArrayRef<CFIProgram::OperandType[CFIProgram::MaxOperands]>
+CFIProgram::getOperandTypes() {
+  static OperandType OpTypes[DW_CFA_restore + 1][MaxOperands];
   static bool Initialized = false;
   if (Initialized) {
-    return ArrayRef<OperandType[2]>(&OpTypes[0], DW_CFA_restore+1);
+    return ArrayRef<OperandType[MaxOperands]>(&OpTypes[0], DW_CFA_restore + 1);
   }
   Initialized = true;
 
-#define DECLARE_OP2(OP, OPTYPE0, OPTYPE1)       \
-  do {                                          \
-    OpTypes[OP][0] = OPTYPE0;                   \
-    OpTypes[OP][1] = OPTYPE1;                   \
+#define DECLARE_OP3(OP, OPTYPE0, OPTYPE1, OPTYPE2)                             \
+  do {                                                                         \
+    OpTypes[OP][0] = OPTYPE0;                                                  \
+    OpTypes[OP][1] = OPTYPE1;                                                  \
+    OpTypes[OP][2] = OPTYPE2;                                                  \
   } while (false)
+#define DECLARE_OP2(OP, OPTYPE0, OPTYPE1)                                      \
+  DECLARE_OP3(OP, OPTYPE0, OPTYPE1, OT_None)
 #define DECLARE_OP1(OP, OPTYPE0) DECLARE_OP2(OP, OPTYPE0, OT_None)
 #define DECLARE_OP0(OP) DECLARE_OP1(OP, OT_None)
 
@@ -779,6 +818,10 @@ ArrayRef<CFIProgram::OperandType[2]> CFIProgram::getOperandTypes() {
   DECLARE_OP2(DW_CFA_def_cfa, OT_Register, OT_Offset);
   DECLARE_OP2(DW_CFA_def_cfa_sf, OT_Register, OT_SignedFactDataOffset);
   DECLARE_OP1(DW_CFA_def_cfa_register, OT_Register);
+  DECLARE_OP3(DW_CFA_LLVM_def_aspace_cfa, OT_Register, OT_Offset,
+              OT_AddressSpace);
+  DECLARE_OP3(DW_CFA_LLVM_def_aspace_cfa_sf, OT_Register,
+              OT_SignedFactDataOffset, OT_AddressSpace);
   DECLARE_OP1(DW_CFA_def_cfa_offset, OT_Offset);
   DECLARE_OP1(DW_CFA_def_cfa_offset_sf, OT_SignedFactDataOffset);
   DECLARE_OP1(DW_CFA_def_cfa_expression, OT_Expression);
@@ -804,7 +847,7 @@ ArrayRef<CFIProgram::OperandType[2]> CFIProgram::getOperandTypes() {
 #undef DECLARE_OP1
 #undef DECLARE_OP2
 
-  return ArrayRef<OperandType[2]>(&OpTypes[0], DW_CFA_restore+1);
+  return ArrayRef<OperandType[MaxOperands]>(&OpTypes[0], DW_CFA_restore + 1);
 }
 
 /// Print \p Opcode's operand number \p OperandIdx which has value \p Operand.
@@ -812,7 +855,7 @@ void CFIProgram::printOperand(raw_ostream &OS, DIDumpOptions DumpOpts,
                               const MCRegisterInfo *MRI, bool IsEH,
                               const Instruction &Instr, unsigned OperandIdx,
                               uint64_t Operand) const {
-  assert(OperandIdx < 2);
+  assert(OperandIdx < MaxOperands);
   uint8_t Opcode = Instr.Opcode;
   OperandType Type = getOperandTypes()[Opcode][OperandIdx];
 
@@ -859,6 +902,9 @@ void CFIProgram::printOperand(raw_ostream &OS, DIDumpOptions DumpOpts,
     OS << ' ';
     printRegister(OS, MRI, IsEH, Operand);
     break;
+  case OT_AddressSpace:
+    OS << format(" in addrspace%" PRId64, Operand);
+    break;
   case OT_Expression:
     assert(Instr.Expression && "missing DWARFExpression object");
     OS << " ";

diff  --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 2da51f8df3918..f9800a3e72255 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -315,6 +315,8 @@ class MCAsmStreamer final : public MCStreamer {
   void emitCFIDefCfa(int64_t Register, int64_t Offset) override;
   void emitCFIDefCfaOffset(int64_t Offset) override;
   void emitCFIDefCfaRegister(int64_t Register) override;
+  void emitCFILLVMDefAspaceCfa(int64_t Register, int64_t Offset,
+                               int64_t AddressSpace) override;
   void emitCFIOffset(int64_t Register, int64_t Offset) override;
   void emitCFIPersonality(const MCSymbol *Sym, unsigned Encoding) override;
   void emitCFILsda(const MCSymbol *Sym, unsigned Encoding) override;
@@ -1810,6 +1812,16 @@ void MCAsmStreamer::emitCFIDefCfaOffset(int64_t Offset) {
   EmitEOL();
 }
 
+void MCAsmStreamer::emitCFILLVMDefAspaceCfa(int64_t Register, int64_t Offset,
+                                            int64_t AddressSpace) {
+  MCStreamer::emitCFILLVMDefAspaceCfa(Register, Offset, AddressSpace);
+  OS << "\t.cfi_llvm_def_aspace_cfa ";
+  EmitRegisterName(Register);
+  OS << ", " << Offset;
+  OS << ", " << AddressSpace;
+  EmitEOL();
+}
+
 static void PrintCFIEscape(llvm::formatted_raw_ostream &OS, StringRef Values) {
   OS << "\t.cfi_escape ";
   if (!Values.empty()) {

diff  --git a/llvm/lib/MC/MCDwarf.cpp b/llvm/lib/MC/MCDwarf.cpp
index 51e3ac96601c3..217584edecec6 100644
--- a/llvm/lib/MC/MCDwarf.cpp
+++ b/llvm/lib/MC/MCDwarf.cpp
@@ -1405,6 +1405,19 @@ void FrameEmitterImpl::emitCFIInstruction(const MCCFIInstruction &Instr) {
 
     return;
   }
+  // TODO: Implement `_sf` variants if/when they need to be emitted.
+  case MCCFIInstruction::OpLLVMDefAspaceCfa: {
+    unsigned Reg = Instr.getRegister();
+    if (!IsEH)
+      Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg);
+    Streamer.emitIntValue(dwarf::DW_CFA_LLVM_def_aspace_cfa, 1);
+    Streamer.emitULEB128IntValue(Reg);
+    CFAOffset = Instr.getOffset();
+    Streamer.emitULEB128IntValue(CFAOffset);
+    Streamer.emitULEB128IntValue(Instr.getAddressSpace());
+
+    return;
+  }
   case MCCFIInstruction::OpOffset:
   case MCCFIInstruction::OpRelOffset: {
     const bool IsRelative =

diff  --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp
index 2383a02fb8e10..8e4752c4b10d3 100644
--- a/llvm/lib/MC/MCParser/AsmParser.cpp
+++ b/llvm/lib/MC/MCParser/AsmParser.cpp
@@ -499,6 +499,7 @@ class AsmParser : public MCAsmParser {
     DK_CFI_DEF_CFA_OFFSET,
     DK_CFI_ADJUST_CFA_OFFSET,
     DK_CFI_DEF_CFA_REGISTER,
+    DK_CFI_LLVM_DEF_ASPACE_CFA,
     DK_CFI_OFFSET,
     DK_CFI_REL_OFFSET,
     DK_CFI_PERSONALITY,
@@ -600,6 +601,7 @@ class AsmParser : public MCAsmParser {
   bool parseDirectiveCFIDefCfa(SMLoc DirectiveLoc);
   bool parseDirectiveCFIAdjustCfaOffset();
   bool parseDirectiveCFIDefCfaRegister(SMLoc DirectiveLoc);
+  bool parseDirectiveCFILLVMDefAspaceCfa(SMLoc DirectiveLoc);
   bool parseDirectiveCFIOffset(SMLoc DirectiveLoc);
   bool parseDirectiveCFIRelOffset(SMLoc DirectiveLoc);
   bool parseDirectiveCFIPersonalityOrLsda(bool IsPersonality);
@@ -2186,6 +2188,8 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info,
       return parseDirectiveCFIAdjustCfaOffset();
     case DK_CFI_DEF_CFA_REGISTER:
       return parseDirectiveCFIDefCfaRegister(IDLoc);
+    case DK_CFI_LLVM_DEF_ASPACE_CFA:
+      return parseDirectiveCFILLVMDefAspaceCfa(IDLoc);
     case DK_CFI_OFFSET:
       return parseDirectiveCFIOffset(IDLoc);
     case DK_CFI_REL_OFFSET:
@@ -4260,6 +4264,19 @@ bool AsmParser::parseDirectiveCFIDefCfaRegister(SMLoc DirectiveLoc) {
   return false;
 }
 
+/// parseDirectiveCFILLVMDefAspaceCfa
+/// ::= .cfi_llvm_def_aspace_cfa register, offset, address_space
+bool AsmParser::parseDirectiveCFILLVMDefAspaceCfa(SMLoc DirectiveLoc) {
+  int64_t Register = 0, Offset = 0, AddressSpace = 0;
+  if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseComma() ||
+      parseAbsoluteExpression(Offset) || parseComma() ||
+      parseAbsoluteExpression(AddressSpace) || parseEOL())
+    return true;
+
+  getStreamer().emitCFILLVMDefAspaceCfa(Register, Offset, AddressSpace);
+  return false;
+}
+
 /// parseDirectiveCFIOffset
 /// ::= .cfi_offset register, offset
 bool AsmParser::parseDirectiveCFIOffset(SMLoc DirectiveLoc) {
@@ -5497,6 +5514,7 @@ void AsmParser::initializeDirectiveKindMap() {
   DirectiveKindMap[".cfi_def_cfa_offset"] = DK_CFI_DEF_CFA_OFFSET;
   DirectiveKindMap[".cfi_adjust_cfa_offset"] = DK_CFI_ADJUST_CFA_OFFSET;
   DirectiveKindMap[".cfi_def_cfa_register"] = DK_CFI_DEF_CFA_REGISTER;
+  DirectiveKindMap[".cfi_llvm_def_aspace_cfa"] = DK_CFI_LLVM_DEF_ASPACE_CFA;
   DirectiveKindMap[".cfi_offset"] = DK_CFI_OFFSET;
   DirectiveKindMap[".cfi_rel_offset"] = DK_CFI_REL_OFFSET;
   DirectiveKindMap[".cfi_personality"] = DK_CFI_PERSONALITY;

diff  --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp
index 5d06240b46801..2a1998fd9c4c9 100644
--- a/llvm/lib/MC/MCStreamer.cpp
+++ b/llvm/lib/MC/MCStreamer.cpp
@@ -444,7 +444,8 @@ void MCStreamer::emitCFIStartProc(bool IsSimple, SMLoc Loc) {
   if (MAI) {
     for (const MCCFIInstruction& Inst : MAI->getInitialFrameState()) {
       if (Inst.getOperation() == MCCFIInstruction::OpDefCfa ||
-          Inst.getOperation() == MCCFIInstruction::OpDefCfaRegister) {
+          Inst.getOperation() == MCCFIInstruction::OpDefCfaRegister ||
+          Inst.getOperation() == MCCFIInstruction::OpLLVMDefAspaceCfa) {
         Frame.CurrentCfaRegister = Inst.getRegister();
       }
     }
@@ -517,6 +518,18 @@ void MCStreamer::emitCFIDefCfaRegister(int64_t Register) {
   CurFrame->CurrentCfaRegister = static_cast<unsigned>(Register);
 }
 
+void MCStreamer::emitCFILLVMDefAspaceCfa(int64_t Register, int64_t Offset,
+                                         int64_t AddressSpace) {
+  MCSymbol *Label = emitCFILabel();
+  MCCFIInstruction Instruction = MCCFIInstruction::createLLVMDefAspaceCfa(
+      Label, Register, Offset, AddressSpace);
+  MCDwarfFrameInfo *CurFrame = getCurrentDwarfFrameInfo();
+  if (!CurFrame)
+    return;
+  CurFrame->Instructions.push_back(Instruction);
+  CurFrame->CurrentCfaRegister = static_cast<unsigned>(Register);
+}
+
 void MCStreamer::emitCFIOffset(int64_t Register, int64_t Offset) {
   MCSymbol *Label = emitCFILabel();
   MCCFIInstruction Instruction =

diff  --git a/llvm/test/CodeGen/MIR/AArch64/cfi.mir b/llvm/test/CodeGen/MIR/AArch64/cfi.mir
index 5768bcafe479e..efa053699b491 100644
--- a/llvm/test/CodeGen/MIR/AArch64/cfi.mir
+++ b/llvm/test/CodeGen/MIR/AArch64/cfi.mir
@@ -23,6 +23,8 @@ body: |
     frame-setup CFI_INSTRUCTION def_cfa_register $w29
     ; CHECK: CFI_INSTRUCTION def_cfa_offset -8
     frame-setup CFI_INSTRUCTION def_cfa_offset -8
+    ; CHECK: CFI_INSTRUCTION llvm_def_aspace_cfa $w29, 16, 6
+    frame-setup CFI_INSTRUCTION llvm_def_aspace_cfa $w29, 16, 6
     ; CHECK: CFI_INSTRUCTION offset $w30, -8
     frame-setup CFI_INSTRUCTION offset $w30, -8
     ; CHECK: CFI_INSTRUCTION rel_offset $w30, -8

diff  --git a/llvm/test/MC/ELF/cfi-llvm-def-cfa-aspace-errors.s b/llvm/test/MC/ELF/cfi-llvm-def-cfa-aspace-errors.s
new file mode 100644
index 0000000000000..fcd73127e9b8a
--- /dev/null
+++ b/llvm/test/MC/ELF/cfi-llvm-def-cfa-aspace-errors.s
@@ -0,0 +1,25 @@
+// RUN: not llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o /dev/null 2>&1 | FileCheck %s
+
+// Check that we diagnose malformed .cfi_llvm_def_aspace_cfa directives.
+
+.cfi_startproc
+
+// CHECK: [[#@LINE+1]]:{{[0-9]+}}: error: invalid register name
+.cfi_llvm_def_aspace_cfa foo
+
+// CHECK: [[#@LINE+1]]:{{[0-9]+}}: error: expected comma
+.cfi_llvm_def_aspace_cfa %rcx .
+
+// CHECK: [[#@LINE+1]]:{{[0-9]+}}: error: expected absolute expression
+.cfi_llvm_def_aspace_cfa %rcx, .+1
+
+// CHECK: [[#@LINE+1]]:{{[0-9]+}}: error: expected comma
+.cfi_llvm_def_aspace_cfa %rcx, 1 .
+
+// CHECK: [[#@LINE+1]]:{{[0-9]+}}: error: expected absolute expression
+.cfi_llvm_def_aspace_cfa %rcx, 1, .+1
+
+// CHECK: [[#@LINE+1]]:{{[0-9]+}}: error: expected newline
+.cfi_llvm_def_aspace_cfa %rcx, 1, 1,
+
+.cfi_endproc

diff  --git a/llvm/test/MC/ELF/cfi-llvm-def-cfa-aspace.s b/llvm/test/MC/ELF/cfi-llvm-def-cfa-aspace.s
new file mode 100644
index 0000000000000..5541c6f4074d8
--- /dev/null
+++ b/llvm/test/MC/ELF/cfi-llvm-def-cfa-aspace.s
@@ -0,0 +1,30 @@
+# RUN: llvm-mc -filetype=asm -triple x86_64-pc-linux-gnu %s -o - | FileCheck --check-prefix=ASM %s
+# RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o %t
+# RUN: llvm-readelf -S -r -x .eh_frame %t | FileCheck --check-prefix=READELF %s
+
+f:
+	.cfi_startproc
+	nop
+	.cfi_llvm_def_aspace_cfa %rcx, 0, 6
+	nop
+	.cfi_endproc
+
+# ASM: f:
+# ASM-NEXT: .cfi_startproc
+# ASM-NEXT: nop
+# ASM-NEXT: .cfi_llvm_def_aspace_cfa %rcx, 0, 6
+# ASM-NEXT: nop
+# ASM-NEXT: .cfi_endproc
+
+# READELF: Section Headers:
+# READELF: Name              Type            Address          Off    Size   ES Flg Lk Inf Al
+# READELF: .eh_frame         X86_64_UNWIND   0000000000000000 000048 000030 00   A  0   0  8
+
+# READELF: Relocation section '.rela.eh_frame' at offset 0xc0 contains 1 entries:
+# READELF-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# READELF-NEXT: 0000000000000020  0000000100000002 R_X86_64_PC32          0000000000000000 .text + 0
+
+# READELF: Hex dump of section '.eh_frame':
+# READELF-NEXT: 0x00000000 14000000 00000000 017a5200 01781001
+# READELF-NEXT: 0x00000010 1b0c0708 90010000 14000000 1c000000
+# READELF-NEXT: 0x00000020 00000000 02000000 00413002 00060000

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_frame_LLVM_def_cfa_aspace.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_frame_LLVM_def_cfa_aspace.s
new file mode 100644
index 0000000000000..a837b2ef1e045
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_frame_LLVM_def_cfa_aspace.s
@@ -0,0 +1,16 @@
+# RUN: llvm-mc %s -filetype=obj -triple=i686-pc-linux -o %t
+# RUN: llvm-dwarfdump -v %t | FileCheck %s
+
+# CHECK:      .eh_frame contents:
+# CHECK:          FDE
+# CHECK-NEXT:     Format:
+# CHECK-NEXT:     DW_CFA_LLVM_def_aspace_cfa: EDX +0 in addrspace6
+# CHECK-NEXT:     DW_CFA_nop:
+
+.text
+.globl foo
+.type  foo, at function
+foo:
+ .cfi_startproc
+ .cfi_llvm_def_aspace_cfa %edx, 0, 6
+ .cfi_endproc

diff  --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
index 94a93ae0e685d..83cffe6a9e6dd 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
@@ -162,6 +162,8 @@ TEST(DWARFDebugFrame, InvalidCFIOpcodesTest) {
       dwarf::DW_CFA_offset_extended_sf,
       dwarf::DW_CFA_def_cfa_sf,
       dwarf::DW_CFA_def_cfa_offset_sf,
+      dwarf::DW_CFA_LLVM_def_aspace_cfa,
+      dwarf::DW_CFA_LLVM_def_aspace_cfa_sf,
       dwarf::DW_CFA_val_offset,
       dwarf::DW_CFA_val_offset_sf,
       dwarf::DW_CFA_val_expression,
@@ -268,7 +270,8 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
   };
 
   for (uint8_t Inst : {dwarf::DW_CFA_offset_extended, dwarf::DW_CFA_register,
-                       dwarf::DW_CFA_def_cfa, dwarf::DW_CFA_val_offset})
+                       dwarf::DW_CFA_def_cfa, dwarf::DW_CFA_LLVM_def_aspace_cfa,
+                       dwarf::DW_CFA_val_offset})
     CheckOp_ULEB128_ULEB128(Inst);
 
   // A test for an instruction with two operands: ULEB128, SLEB128.
@@ -284,8 +287,9 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
                           "malformed sleb128, extends past end"));
   };
 
-  for (uint8_t Inst : {dwarf::DW_CFA_offset_extended_sf,
-                       dwarf::DW_CFA_def_cfa_sf, dwarf::DW_CFA_val_offset_sf})
+  for (uint8_t Inst :
+       {dwarf::DW_CFA_offset_extended_sf, dwarf::DW_CFA_def_cfa_sf,
+        dwarf::DW_CFA_LLVM_def_aspace_cfa_sf, dwarf::DW_CFA_val_offset_sf})
     CheckOp_ULEB128_SLEB128(Inst);
 
   // Unable to read a truncated DW_CFA_def_cfa_expression instruction.
@@ -1545,4 +1549,102 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa) {
   EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs);
 }
 
+TEST(DWARFDebugFrame, UnwindTable_DW_CFA_LLVM_def_aspace_cfa) {
+  // Test that DW_CFA_LLVM_def_aspace_cfa, DW_CFA_LLVM_def_aspace_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;
+  constexpr uint8_t AddrSpace = 2;
+
+  EXPECT_THAT_ERROR(
+      parseCFI(TestCIE, {dwarf::DW_CFA_LLVM_def_aspace_cfa, CFAReg1, CFAOff1,
+                         AddrSpace, 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,
+                                                              AddrSpace));
+  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,
+                                                              AddrSpace));
+  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,
+                                                              AddrSpace));
+  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,
+                                                              AddrSpace));
+  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,
+                                                              AddrSpace));
+  EXPECT_EQ(Rows[4].getRegisterLocations().size(), 1u);
+  EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs);
+}
+
 } // end anonymous namespace


        


More information about the llvm-commits mailing list