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
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
/// 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))
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
// 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),
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.
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
/// 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
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
OT_SignedFactDataOffset,
OT_UnsignedFactDataOffset,
OT_Register,
+ OT_AddressSpace,
OT_Expression
};
/// 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,
OpRememberState,
OpRestoreState,
OpOffset,
+ OpLLVMDefAspaceCfa,
OpDefCfaRegister,
OpDefCfaOffset,
OpDefCfa,
int Offset;
unsigned Register2;
};
+ unsigned AddressSpace;
std::vector<char> Values;
std::string Comment;
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)
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.
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,
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;
}
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;
}
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);
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;
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
.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)
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,
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);
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))
lex();
int Offset;
Register Reg;
+ unsigned AddressSpace;
unsigned CFIIndex;
switch (Kind) {
case MIToken::kw_cfi_same_value:
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;
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:
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())
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) {
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);
// 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:
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>";
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);
case OT_Address:
case OT_Register:
+ case OT_AddressSpace:
return Operand;
case OT_FactoredCodeOffset: {
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);
case OT_Address:
case OT_Register:
+ case OT_AddressSpace:
return createStringError(
errc::invalid_argument,
"op[%" PRIu32 "] has OperandType %s which produces an unsigned result, "
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);
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)
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);
#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.
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];
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 << " ";
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;
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()) {
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 =
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,
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);
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:
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) {
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;
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();
}
}
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 =
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
--- /dev/null
+// 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
--- /dev/null
+# 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
--- /dev/null
+# 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
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,
};
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.
"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.
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