diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 495f9b5105ac..1585c4716b46 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -8,7 +8,9 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/ErrorHandling.h" @@ -267,16 +269,147 @@ private: uint64_t AddressRange; CIE *LinkedCIE; }; + +/// \brief Types of operands to CF instructions. +enum OperandType { + OT_Unset, + OT_None, + OT_Address, + OT_Offset, + OT_FactoredCodeOffset, + OT_SignedFactDataOffset, + OT_UnsignedFactDataOffset, + OT_Register, + OT_Expression +}; + } // end anonymous namespace +/// \brief Initialize the array describing the types of operands. +static ArrayRef getOperandTypes() { + static OperandType OpTypes[DW_CFA_restore+1][2]; + +#define DECLARE_OP2(OP, OPTYPE0, OPTYPE1) \ + do { \ + OpTypes[OP][0] = OPTYPE0; \ + OpTypes[OP][1] = OPTYPE1; \ + } while (0) +#define DECLARE_OP1(OP, OPTYPE0) DECLARE_OP2(OP, OPTYPE0, OT_None) +#define DECLARE_OP0(OP) DECLARE_OP1(OP, OT_None) + + DECLARE_OP1(DW_CFA_set_loc, OT_Address); + DECLARE_OP1(DW_CFA_advance_loc, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_advance_loc1, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_advance_loc2, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_advance_loc4, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_MIPS_advance_loc8, OT_FactoredCodeOffset); + 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_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); + DECLARE_OP1(DW_CFA_undefined, OT_Register); + DECLARE_OP1(DW_CFA_same_value, OT_Register); + DECLARE_OP2(DW_CFA_offset, OT_Register, OT_UnsignedFactDataOffset); + DECLARE_OP2(DW_CFA_offset_extended, OT_Register, OT_UnsignedFactDataOffset); + DECLARE_OP2(DW_CFA_offset_extended_sf, OT_Register, OT_SignedFactDataOffset); + DECLARE_OP2(DW_CFA_val_offset, OT_Register, OT_UnsignedFactDataOffset); + DECLARE_OP2(DW_CFA_val_offset_sf, OT_Register, OT_SignedFactDataOffset); + DECLARE_OP2(DW_CFA_register, OT_Register, OT_Register); + DECLARE_OP2(DW_CFA_expression, OT_Register, OT_Expression); + DECLARE_OP2(DW_CFA_val_expression, OT_Register, OT_Expression); + DECLARE_OP1(DW_CFA_restore, OT_Register); + DECLARE_OP1(DW_CFA_restore_extended, OT_Register); + DECLARE_OP0(DW_CFA_remember_state); + DECLARE_OP0(DW_CFA_restore_state); + DECLARE_OP0(DW_CFA_GNU_window_save); + DECLARE_OP1(DW_CFA_GNU_args_size, OT_Offset); + DECLARE_OP0(DW_CFA_nop); + +#undef DECLARE_OP0 +#undef DECLARE_OP1 +#undef DECLARE_OP2 + return OpTypes; +} + +static ArrayRef OpTypes = getOperandTypes(); + +/// \brief Print \p Opcode's operand number \p OperandIdx which has +/// value \p Operand. +static void printOperand(raw_ostream &OS, uint8_t Opcode, unsigned OperandIdx, + uint64_t Operand, uint64_t CodeAlignmentFactor, + int64_t DataAlignmentFactor) { + assert(OperandIdx < 2); + OperandType Type = OpTypes[Opcode][OperandIdx]; + + switch (Type) { + case OT_Unset: + OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to"; + if (const char *OpcodeName = CallFrameString(Opcode)) + OS << " " << OpcodeName; + else + OS << format(" Opcode %x", Opcode); + break; + case OT_None: + break; + case OT_Address: + OS << format(" %" PRIx64, Operand); + break; + case OT_Offset: + // The offsets are all encoded in a unsigned form, but in practice + // consumers use them signed. It's most certainly legacy due to + // the lack of signed variants in the first Dwarf standards. + OS << format(" %+" PRId64, int64_t(Operand)); + break; + case OT_FactoredCodeOffset: // Always Unsigned + if (CodeAlignmentFactor) + OS << format(" %" PRId64, Operand * CodeAlignmentFactor); + else + OS << format(" %" PRId64 "*code_alignment_factor" , Operand); + break; + case OT_SignedFactDataOffset: + if (DataAlignmentFactor) + OS << format(" %" PRId64, int64_t(Operand) * DataAlignmentFactor); + else + OS << format(" %" PRId64 "*data_alignment_factor" , int64_t(Operand)); + break; + case OT_UnsignedFactDataOffset: + if (DataAlignmentFactor) + OS << format(" %" PRId64, Operand * DataAlignmentFactor); + else + OS << format(" %" PRId64 "*data_alignment_factor" , Operand); + break; + case OT_Register: + OS << format(" reg%d", Operand); + break; + case OT_Expression: + OS << " expression"; + break; + } +} + void FrameEntry::dumpInstructions(raw_ostream &OS) const { - // TODO: at the moment only instruction names are dumped. Expand this to - // dump operands as well. + uint64_t CodeAlignmentFactor = 0; + int64_t DataAlignmentFactor = 0; + const CIE *Cie = dyn_cast(this); + + if (!Cie) + Cie = cast(this)->getLinkedCIE(); + if (Cie) { + CodeAlignmentFactor = Cie->getCodeAlignmentFactor(); + DataAlignmentFactor = Cie->getDataAlignmentFactor(); + } + for (const auto &Instr : Instructions) { uint8_t Opcode = Instr.Opcode; if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK; - OS << " " << CallFrameString(Opcode) << ":\n"; + OS << " " << CallFrameString(Opcode) << ":"; + for (unsigned i = 0; i < Instr.Ops.size(); ++i) + printOperand(OS, Opcode, i, Instr.Ops[i], CodeAlignmentFactor, + DataAlignmentFactor); + OS << '\n'; } } diff --git a/llvm/test/DebugInfo/dwarfdump-debug-frame-simple.test b/llvm/test/DebugInfo/dwarfdump-debug-frame-simple.test index c2427d840e45..b1a58e6ec2a4 100644 --- a/llvm/test/DebugInfo/dwarfdump-debug-frame-simple.test +++ b/llvm/test/DebugInfo/dwarfdump-debug-frame-simple.test @@ -6,22 +6,22 @@ ; FRAMES: 00000000 00000010 ffffffff CIE ; FRAMES: Version: 1 -; FRAMES: DW_CFA_def_cfa -; FRAMES-NEXT: DW_CFA_offset -; FRAMES-NEXT: DW_CFA_nop -; FRAMES-NEXT: DW_CFA_nop +; FRAMES: DW_CFA_def_cfa: reg4 +4 +; FRAMES-NEXT: DW_CFA_offset: reg8 -4 +; FRAMES-NEXT: DW_CFA_nop: +; FRAMES-NEXT: DW_CFA_nop: ; FRAMES: 00000014 00000010 00000000 FDE cie=00000000 pc=00000000...00000022 -; FRAMES: DW_CFA_advance_loc -; FRAMES-NEXT: DW_CFA_def_cfa_offset -; FRAMES-NEXT: DW_CFA_nop +; FRAMES: DW_CFA_advance_loc: 3 +; FRAMES-NEXT: DW_CFA_def_cfa_offset: +12 +; FRAMES-NEXT: DW_CFA_nop: ; FRAMES: 00000028 00000014 00000000 FDE cie=00000000 pc=00000030...00000080 -; FRAMES: DW_CFA_advance_loc -; FRAMES-NEXT: DW_CFA_def_cfa_offset -; FRAMES-NEXT: DW_CFA_offset -; FRAMES-NEXT: DW_CFA_advance_loc -; FRAMES-NEXT: DW_CFA_def_cfa_register +; FRAMES: DW_CFA_advance_loc: 1 +; FRAMES-NEXT: DW_CFA_def_cfa_offset: +8 +; FRAMES-NEXT: DW_CFA_offset: reg5 -8 +; FRAMES-NEXT: DW_CFA_advance_loc: 2 +; FRAMES-NEXT: DW_CFA_def_cfa_register: reg5 ; FRAMES-NOT: CIE ; FRAMES-NOT: FDE