diff --git a/include/llvm/BinaryFormat/Dwarf.def b/include/llvm/BinaryFormat/Dwarf.def index c863cf7d66b..34f124b5779 100644 --- a/include/llvm/BinaryFormat/Dwarf.def +++ b/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/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index db6a13824a6..536583e2064 100644 --- a/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -46,11 +46,12 @@ public: /// 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 @@ 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 AddrSpace; /// The address space for Kind == RegPlusOffset + /// for CFA. Optional Expr; /// The DWARF expression for Kind == /// DWARFExpression. bool Dereference; /// If true, the resulting location must be dereferenced @@ -72,10 +75,12 @@ private: // 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 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 @@ public: 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 AddrSpace = None); + static UnwindLocation + createAtRegisterPlusOffset(uint32_t Reg, int32_t Off, + Optional 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 @@ public: 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 Operands; + static constexpr size_t MaxOperands = 3; + typedef SmallVector 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 @@ private: 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 @@ private: OT_SignedFactDataOffset, OT_UnsignedFactDataOffset, OT_Register, + OT_AddressSpace, OT_Expression }; @@ -490,7 +515,7 @@ private: /// Retrieve the array describing the types of operands according to the enum /// above. This is indexed by opcode. - static ArrayRef getOperandTypes(); + static ArrayRef getOperandTypes(); /// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. void printOperand(raw_ostream &OS, DIDumpOptions DumpOpts, diff --git a/include/llvm/MC/MCDwarf.h b/include/llvm/MC/MCDwarf.h index 625e83a37da..fd94d6758f2 100644 --- a/include/llvm/MC/MCDwarf.h +++ b/include/llvm/MC/MCDwarf.h @@ -445,6 +445,7 @@ public: OpRememberState, OpRestoreState, OpOffset, + OpLLVMDefAspaceCfa, OpDefCfaRegister, OpDefCfaOffset, OpDefCfa, @@ -467,6 +468,7 @@ private: int Offset; unsigned Register2; }; + unsigned AddressSpace; std::vector Values; std::string Comment; @@ -474,7 +476,7 @@ private: 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 @@ private: 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 @@ public: 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 @@ public: 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 @@ public: 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/include/llvm/MC/MCStreamer.h b/include/llvm/MC/MCStreamer.h index f4ebadbd33d..386726654c5 100644 --- a/include/llvm/MC/MCStreamer.h +++ b/include/llvm/MC/MCStreamer.h @@ -979,6 +979,8 @@ public: 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/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp b/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp index da754019e0d..fc127f4cf9d 100644 --- a/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp +++ b/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/lib/CodeGen/CFIInstrInserter.cpp b/lib/CodeGen/CFIInstrInserter.cpp index c6244419d6d..1c2e3f99844 100644 --- a/lib/CodeGen/CFIInstrInserter.cpp +++ b/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/lib/CodeGen/MIRParser/MILexer.cpp b/lib/CodeGen/MIRParser/MILexer.cpp index 1a890b0ead0..e6866f04986 100644 --- a/lib/CodeGen/MIRParser/MILexer.cpp +++ b/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/lib/CodeGen/MIRParser/MILexer.h b/lib/CodeGen/MIRParser/MILexer.h index 82f89971609..5d7ea8fb662 100644 --- a/lib/CodeGen/MIRParser/MILexer.h +++ b/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/lib/CodeGen/MIRParser/MIParser.cpp b/lib/CodeGen/MIRParser/MIParser.cpp index 5742080e11c..62aad55ba16 100644 --- a/lib/CodeGen/MIRParser/MIParser.cpp +++ b/lib/CodeGen/MIRParser/MIParser.cpp @@ -472,6 +472,7 @@ public: 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/lib/CodeGen/MachineOperand.cpp b/lib/CodeGen/MachineOperand.cpp index 766915cd087..c0ae0d27f42 100644 --- a/lib/CodeGen/MachineOperand.cpp +++ b/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/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 1e087d6deda..92a461dbd94 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/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 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 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 ""; @@ -398,7 +414,7 @@ const char *CFIProgram::operandTypeString(CFIProgram::OperandType OT) { llvm::Expected 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 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 RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); + if (!Offset) + return Offset.takeError(); + llvm::Expected 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::getOperandTypes() { - static OperandType OpTypes[DW_CFA_restore+1][2]; +ArrayRef +CFIProgram::getOperandTypes() { + static OperandType OpTypes[DW_CFA_restore + 1][MaxOperands]; static bool Initialized = false; if (Initialized) { - return ArrayRef(&OpTypes[0], DW_CFA_restore+1); + return ArrayRef(&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::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::getOperandTypes() { #undef DECLARE_OP1 #undef DECLARE_OP2 - return ArrayRef(&OpTypes[0], DW_CFA_restore+1); + return ArrayRef(&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/lib/MC/MCAsmStreamer.cpp b/lib/MC/MCAsmStreamer.cpp index 2da51f8df39..f9800a3e722 100644 --- a/lib/MC/MCAsmStreamer.cpp +++ b/lib/MC/MCAsmStreamer.cpp @@ -315,6 +315,8 @@ public: 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/lib/MC/MCDwarf.cpp b/lib/MC/MCDwarf.cpp index 51e3ac96601..217584edece 100644 --- a/lib/MC/MCDwarf.cpp +++ b/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/lib/MC/MCParser/AsmParser.cpp b/lib/MC/MCParser/AsmParser.cpp index 2383a02fb8e..8e4752c4b10 100644 --- a/lib/MC/MCParser/AsmParser.cpp +++ b/lib/MC/MCParser/AsmParser.cpp @@ -499,6 +499,7 @@ private: 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 @@ private: 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/lib/MC/MCStreamer.cpp b/lib/MC/MCStreamer.cpp index 5d06240b468..2a1998fd9c4 100644 --- a/lib/MC/MCStreamer.cpp +++ b/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(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(Register); +} + void MCStreamer::emitCFIOffset(int64_t Register, int64_t Offset) { MCSymbol *Label = emitCFILabel(); MCCFIInstruction Instruction = diff --git a/test/CodeGen/MIR/AArch64/cfi.mir b/test/CodeGen/MIR/AArch64/cfi.mir index 5768bcafe47..efa053699b4 100644 --- a/test/CodeGen/MIR/AArch64/cfi.mir +++ b/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/test/MC/ELF/cfi-llvm-def-cfa-aspace-errors.s b/test/MC/ELF/cfi-llvm-def-cfa-aspace-errors.s new file mode 100644 index 00000000000..fcd73127e9b --- /dev/null +++ b/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/test/MC/ELF/cfi-llvm-def-cfa-aspace.s b/test/MC/ELF/cfi-llvm-def-cfa-aspace.s new file mode 100644 index 00000000000..5541c6f4074 --- /dev/null +++ b/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/test/tools/llvm-dwarfdump/X86/debug_frame_LLVM_def_cfa_aspace.s b/test/tools/llvm-dwarfdump/X86/debug_frame_LLVM_def_cfa_aspace.s new file mode 100644 index 00000000000..a837b2ef1e0 --- /dev/null +++ b/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,@function +foo: + .cfi_startproc + .cfi_llvm_def_aspace_cfa %edx, 0, 6 + .cfi_endproc diff --git a/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp index 94a93ae0e68..83cffe6a9e6 100644 --- a/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp +++ b/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 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