diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -914,6 +914,10 @@ 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 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -238,6 +238,7 @@ /// The address will be valid when parsing the instructions in a FDE. If /// invalid, this object represents the initial instructions of a CIE. Optional Address; ///< Address for row in FDE, invalid for CIE. + uint32_t CFAAddressSpace = 0; /// The address space for the CFA address. UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). RegisterLocations RegLocs; ///< How to unwind all registers in this list. @@ -253,12 +254,24 @@ /// address with a call to \see hasAddress(). uint64_t getAddress() const { return *Address; } + /// Get the address space for address of this row. + /// + /// This represents the address space the address of this row is located in. + uint8_t getCFAAddressSpace() const { return CFAAddressSpace; } + /// 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; } + /// Set the address space for address of this row. + /// + /// Capture the address space of the CFA address of this row, if any. + void setCFAAddressSpace(uint8_t NewCFAAddrSpace) { + CFAAddressSpace = NewCFAAddrSpace; + } + /// Offset the address for this UnwindRow. /// /// The address represents the first address for which the CFAValue and @@ -388,7 +401,7 @@ /// manual, "6.4.1 Structure of Call Frame Information". class CFIProgram { public: - typedef SmallVector Operands; + 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 +480,15 @@ 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 +504,7 @@ OT_SignedFactDataOffset, OT_UnsignedFactDataOffset, OT_Register, + OT_AddressSpace, OT_Expression }; @@ -490,7 +513,7 @@ /// 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/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h --- a/llvm/include/llvm/MC/MCDwarf.h +++ b/llvm/include/llvm/MC/MCDwarf.h @@ -445,6 +445,7 @@ OpRememberState, OpRestoreState, OpOffset, + OpLLVMDefAspaceCfa, OpDefCfaRegister, OpDefCfaOffset, OpDefCfa, @@ -467,6 +468,7 @@ int Offset; unsigned Register2; }; + unsigned AddressSpace; std::vector Values; std::string Comment; @@ -474,7 +476,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -981,6 +981,8 @@ 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 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp @@ -257,6 +257,10 @@ 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 --- a/llvm/lib/CodeGen/CFIInstrInserter.cpp +++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp @@ -220,6 +220,14 @@ 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.h b/llvm/lib/CodeGen/MIRParser/MILexer.h --- a/llvm/lib/CodeGen/MIRParser/MILexer.h +++ b/llvm/lib/CodeGen/MIRParser/MILexer.h @@ -83,6 +83,7 @@ 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/MILexer.cpp b/llvm/lib/CodeGen/MIRParser/MILexer.cpp --- a/llvm/lib/CodeGen/MIRParser/MILexer.cpp +++ b/llvm/lib/CodeGen/MIRParser/MILexer.cpp @@ -226,6 +226,7 @@ .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/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp --- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp +++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp @@ -472,6 +472,7 @@ 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); @@ -2205,6 +2206,16 @@ return false; } +bool MIParser::parseCFIAddressSpace(unsigned &AddressSpace) { + if (Token.isNot(MIToken::IntegerLiteral)) + return error("expected a cfi address space"); + 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)) @@ -2225,6 +2236,7 @@ lex(); int Offset; Register Reg; + unsigned AddressSpace; unsigned CFIIndex; switch (Kind) { case MIToken::kw_cfi_same_value: @@ -2271,6 +2283,14 @@ 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; @@ -2618,6 +2638,7 @@ 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 --- a/llvm/lib/CodeGen/MachineOperand.cpp +++ b/llvm/lib/CodeGen/MachineOperand.cpp @@ -653,6 +653,14 @@ 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 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -172,6 +172,8 @@ OS << format("0x%" PRIx64 ": ", *Address); OS << "CFA="; CFAValue.dump(OS, MRI, IsEH); + if (CFAAddressSpace != 0) + OS << " as" << CFAAddressSpace; if (RegLocs.hasLocations()) { OS << ": "; RegLocs.dump(OS, MRI, IsEH); @@ -305,6 +307,16 @@ // 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: @@ -382,6 +394,7 @@ 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 ""; @@ -390,7 +403,7 @@ llvm::Expected CFIProgram::Instruction::getOperandAsUnsigned(const CFIProgram &CFIP, uint32_t OperandIdx) const { - if (OperandIdx >= 2) + if (OperandIdx >= 3) return createStringError(errc::invalid_argument, "operand index %" PRIu32 " is not valid", OperandIdx); @@ -415,6 +428,7 @@ case OT_Address: case OT_Register: + case OT_AddressSpace: return Operand; case OT_FactoredCodeOffset: { @@ -434,7 +448,7 @@ llvm::Expected CFIProgram::Instruction::getOperandAsSigned(const CFIProgram &CFIP, uint32_t OperandIdx) const { - if (OperandIdx >= 2) + if (OperandIdx >= 3) return createStringError(errc::invalid_argument, "operand index %" PRIu32 " is not valid", OperandIdx); @@ -450,6 +464,7 @@ case OT_Address: case OT_Register: + case OT_AddressSpace: return createStringError( errc::invalid_argument, "op[%" PRIu32 "] has OperandType %s which produces an unsigned result, " @@ -737,6 +752,24 @@ 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); + Row.setCFAAddressSpace(*CFAAddrSpace); + break; + } + case dwarf::DW_CFA_def_cfa_expression: Row.getCFAValue() = UnwindLocation::createIsDWARFExpression(*Inst.Expression); @@ -746,19 +779,22 @@ return Error::success(); } -ArrayRef CFIProgram::getOperandTypes() { - static OperandType OpTypes[DW_CFA_restore+1][2]; +ArrayRef CFIProgram::getOperandTypes() { + static OperandType OpTypes[DW_CFA_restore + 1][3]; 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) @@ -771,6 +807,10 @@ 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); @@ -796,7 +836,7 @@ #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. @@ -804,7 +844,7 @@ const MCRegisterInfo *MRI, bool IsEH, const Instruction &Instr, unsigned OperandIdx, uint64_t Operand) const { - assert(OperandIdx < 2); + assert(OperandIdx < 3); uint8_t Opcode = Instr.Opcode; OperandType Type = getOperandTypes()[Opcode][OperandIdx]; @@ -851,6 +891,9 @@ OS << ' '; printRegister(OS, MRI, IsEH, Operand); break; + case OT_AddressSpace: + OS << format(" as%" 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 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -308,6 +308,8 @@ 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; @@ -1724,6 +1726,19 @@ 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; + // FIXME: When going from ASM->ASM a lot of these directives seem to add too + // much whitespace; maybe something isn't consuming whitespace eagerly enough + // while parsing? + 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 --- a/llvm/lib/MC/MCDwarf.cpp +++ b/llvm/lib/MC/MCDwarf.cpp @@ -1437,6 +1437,16 @@ return; } + // TODO: Implement `_sf` variants if/when they need to be emitted. + case MCCFIInstruction::OpLLVMDefAspaceCfa: { + Streamer.emitIntValue(dwarf::DW_CFA_LLVM_def_aspace_cfa, 1); + Streamer.emitULEB128IntValue(Instr.getRegister()); + 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 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -483,6 +483,7 @@ 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, @@ -583,6 +584,7 @@ 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); @@ -2119,6 +2121,8 @@ 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: @@ -4210,6 +4214,21 @@ 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) || + parseToken(AsmToken::Comma, "unexpected token in directive") || + parseAbsoluteExpression(Offset) || + parseToken(AsmToken::Comma, "unexpected token in directive") || + parseAbsoluteExpression(AddressSpace)) + return true; + + getStreamer().emitCFILLVMDefAspaceCfa(Register, Offset, AddressSpace); + return false; +} + /// parseDirectiveCFIOffset /// ::= .cfi_offset register, offset bool AsmParser::parseDirectiveCFIOffset(SMLoc DirectiveLoc) { @@ -5482,6 +5501,7 @@ 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 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -519,6 +519,18 @@ 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/llvm/test/CodeGen/MIR/AArch64/cfi.mir b/llvm/test/CodeGen/MIR/AArch64/cfi.mir --- a/llvm/test/CodeGen/MIR/AArch64/cfi.mir +++ b/llvm/test/CodeGen/MIR/AArch64/cfi.mir @@ -23,6 +23,8 @@ 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/DebugInfo/AMDGPU/cfi.ll b/llvm/test/DebugInfo/AMDGPU/cfi.ll --- a/llvm/test/DebugInfo/AMDGPU/cfi.ll +++ b/llvm/test/DebugInfo/AMDGPU/cfi.ll @@ -17,7 +17,6 @@ ; CHECK-NEXT: 00000010 {{[0-9]+}} 00000000 FDE cie=00000000 pc=00000000...{{[0-9]+}} ; CHECK-NEXT: Format: DWARF32 ; CHECK-EMPTY: -; CHECK-EMPTY: ; CHECK: .eh_frame contents: ; CHECK-NOT: CIE diff --git a/llvm/test/MC/ELF/cfi-llvm-def-aspace-cfa-errors.s b/llvm/test/MC/ELF/cfi-llvm-def-aspace-cfa-errors.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ELF/cfi-llvm-def-aspace-cfa-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: unexpected token in directive +.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: unexpected token in directive +.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: unexpected token at start of statement +.cfi_llvm_def_aspace_cfa %rcx, 1, 1, + +.cfi_endproc diff --git a/llvm/test/MC/ELF/cfi-llvm-def-aspace-cfa.s b/llvm/test/MC/ELF/cfi-llvm-def-aspace-cfa.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ELF/cfi-llvm-def-aspace-cfa.s @@ -0,0 +1,32 @@ +# 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 +# FIXME Why emit an extra empty line? +# ASM-EMPTY: +# 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_aspace_cfa.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_frame_LLVM_def_aspace_cfa.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_frame_LLVM_def_aspace_cfa.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 as6 +# 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/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp @@ -162,6 +162,8 @@ 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 @@ }; 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 @@ "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. @@ -1467,4 +1471,107 @@ 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].getCFAAddressSpace(), AddrSpace); + 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[0].getCFAAddressSpace(), AddrSpace); + 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[0].getCFAAddressSpace(), AddrSpace); + 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[0].getCFAAddressSpace(), AddrSpace); + 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[0].getCFAAddressSpace(), AddrSpace); + 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