From 94c2e85bea1ab1b837a4c055ccc83d5cd32dd027 Mon Sep 17 00:00:00 2001 From: Kevin Enderby Date: Fri, 9 Dec 2011 18:09:40 +0000 Subject: The second part of support for generating dwarf for assembly source files. This generates the dwarf Compile Unit DIE and a dwarf subprogram DIE for each non-temporary label. The next part will be to get the clang driver to enable this when assembling a .s file. rdar://9275556 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@146262 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/MC/MCContext.h | 28 ++++ include/llvm/MC/MCDwarf.h | 42 ++++++ include/llvm/Support/Dwarf.h | 1 + lib/MC/MCAsmStreamer.cpp | 4 + lib/MC/MCDwarf.cpp | 339 ++++++++++++++++++++++++++++++++++++++++++ lib/MC/MCObjectStreamer.cpp | 4 + lib/MC/MCParser/AsmParser.cpp | 9 ++ test/MC/MachO/gen-dwarf.s | 113 ++++++++++++++ tools/llvm-mc/llvm-mc.cpp | 14 ++ 9 files changed, 554 insertions(+) create mode 100644 test/MC/MachO/gen-dwarf.s diff --git a/include/llvm/MC/MCContext.h b/include/llvm/MC/MCContext.h index d45b0c8ca6..455b45ea23 100644 --- a/include/llvm/MC/MCContext.h +++ b/include/llvm/MC/MCContext.h @@ -108,6 +108,16 @@ namespace llvm { /// The default initial text section that we generate dwarf debugging line /// info for when generating dwarf assembly source files. const MCSection *GenDwarfSection; + /// Symbols created for the start and end of this section. + MCSymbol *GenDwarfSectionStartSym, *GenDwarfSectionEndSym; + + /// The information gathered from labels that will have dwarf subprogram + /// entries when generating dwarf assembly source files. + std::vector MCGenDwarfSubprogramEntries; + + /// The string to embed in the debug information for the compile unit, if + /// non-empty. + StringRef DwarfDebugFlags; /// Honor temporary labels, this is useful for debugging semantic /// differences between temporary and non-temporary labels (primarily on @@ -269,6 +279,24 @@ namespace llvm { unsigned nextGenDwarfFileNumber() { return ++GenDwarfFileNumber; } const MCSection *getGenDwarfSection() { return GenDwarfSection; } void setGenDwarfSection(const MCSection *Sec) { GenDwarfSection = Sec; } + MCSymbol *getGenDwarfSectionStartSym() { return GenDwarfSectionStartSym; } + void setGenDwarfSectionStartSym(MCSymbol *Sym) { + GenDwarfSectionStartSym = Sym; + } + MCSymbol *getGenDwarfSectionEndSym() { return GenDwarfSectionEndSym; } + void setGenDwarfSectionEndSym(MCSymbol *Sym) { + GenDwarfSectionEndSym = Sym; + } + const std::vector + &getMCGenDwarfSubprogramEntries() const { + return MCGenDwarfSubprogramEntries; + } + void addMCGenDwarfSubprogramEntry(const MCGenDwarfSubprogramEntry *E) { + MCGenDwarfSubprogramEntries.push_back(E); + } + + void setDwarfDebugFlags(StringRef S) { DwarfDebugFlags = S; } + StringRef getDwarfDebugFlags() { return DwarfDebugFlags; } /// @} diff --git a/include/llvm/MC/MCDwarf.h b/include/llvm/MC/MCDwarf.h index 431e3c4da8..9c77218215 100644 --- a/include/llvm/MC/MCDwarf.h +++ b/include/llvm/MC/MCDwarf.h @@ -31,6 +31,8 @@ namespace llvm { class MCSymbol; class MCObjectStreamer; class raw_ostream; + class SourceMgr; + class SMLoc; /// MCDwarfFile - Instances of this class represent the name of the dwarf /// .file directive and its associated dwarf file number in the MC file, @@ -227,6 +229,46 @@ namespace llvm { int64_t LineDelta, uint64_t AddrDelta); }; + class MCGenDwarfInfo { + public: + // + // When generating dwarf for assembly source files this emits the Dwarf + // sections. + // + static void Emit(MCStreamer *MCOS); + }; + + // When generating dwarf for assembly source files this is the info that is + // needed to be gathered for each symbol that will have a dwarf2_subprogram. + class MCGenDwarfSubprogramEntry { + private: + // Name of the symbol without a leading underbar, if any. + StringRef Name; + // The dwarf file number this symbol is in. + unsigned FileNumber; + // The line number this symbol is at. + unsigned LineNumber; + // The low_pc for the dwarf2_subprogram is taken from this symbol. The + // high_pc is taken from the next symbol's value or the end of the section + // for the last symbol + MCSymbol *Label; + + public: + MCGenDwarfSubprogramEntry(StringRef name, unsigned fileNumber, + unsigned lineNumber, MCSymbol *label) : + Name(name), FileNumber(fileNumber), LineNumber(lineNumber), Label(label){} + + StringRef getName() const { return Name; } + unsigned getFileNumber() const { return FileNumber; } + unsigned getLineNumber() const { return LineNumber; } + MCSymbol *getLabel() const { return Label; } + + // This is called when label is created when we are generating dwarf for + // assembly source files. + static void Make(MCSymbol *Symbol, MCStreamer *MCOS, SourceMgr &SrcMgr, + SMLoc &Loc); + }; + class MCCFIInstruction { public: enum OpType { SameValue, Remember, Restore, Move, RelMove }; diff --git a/include/llvm/Support/Dwarf.h b/include/llvm/Support/Dwarf.h index 30f91874db..357f555a39 100644 --- a/include/llvm/Support/Dwarf.h +++ b/include/llvm/Support/Dwarf.h @@ -526,6 +526,7 @@ enum dwarf_constants { DW_LANG_D = 0x0013, DW_LANG_Python = 0x0014, DW_LANG_lo_user = 0x8000, + DW_LANG_Mips_Assembler = 0x8001, DW_LANG_hi_user = 0xffff, // Identifier case codes diff --git a/lib/MC/MCAsmStreamer.cpp b/lib/MC/MCAsmStreamer.cpp index d90f7b2ad2..c785c03223 100644 --- a/lib/MC/MCAsmStreamer.cpp +++ b/lib/MC/MCAsmStreamer.cpp @@ -1284,6 +1284,10 @@ void MCAsmStreamer::Finish() { if (getContext().hasDwarfFiles() && !UseLoc) MCDwarfFileTable::Emit(this); + // If we are generating dwarf for assembly source files dump out the sections. + if (getContext().getGenDwarfForAssembly()) + MCGenDwarfInfo::Emit(this); + if (!UseCFI) EmitFrames(false); } diff --git a/lib/MC/MCDwarf.cpp b/lib/MC/MCDwarf.cpp index 8157b4177a..46ab65ffeb 100644 --- a/lib/MC/MCDwarf.cpp +++ b/lib/MC/MCDwarf.cpp @@ -19,9 +19,12 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/SourceMgr.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" +#include "llvm/Config/config.h" using namespace llvm; // Given a special op, return the address skip amount (in units of @@ -423,6 +426,342 @@ void MCDwarfFile::dump() const { print(dbgs()); } +// Utility function to write a tuple for .debug_abbrev. +static void EmitAbbrev(MCStreamer *MCOS, uint64_t Name, uint64_t Form) { + MCOS->EmitULEB128IntValue(Name); + MCOS->EmitULEB128IntValue(Form); +} + +// When generating dwarf for assembly source files this emits +// the data for .debug_abbrev section which contains three DIEs. +static void EmitGenDwarfAbbrev(MCStreamer *MCOS) { + MCContext &context = MCOS->getContext(); + MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfAbbrevSection()); + + // DW_TAG_compile_unit DIE abbrev (1). + MCOS->EmitULEB128IntValue(1); + MCOS->EmitULEB128IntValue(dwarf::DW_TAG_compile_unit); + MCOS->EmitIntValue(dwarf::DW_CHILDREN_yes, 1); + EmitAbbrev(MCOS, dwarf::DW_AT_stmt_list, dwarf::DW_FORM_data4); + EmitAbbrev(MCOS, dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr); + EmitAbbrev(MCOS, dwarf::DW_AT_high_pc, dwarf::DW_FORM_addr); + EmitAbbrev(MCOS, dwarf::DW_AT_name, dwarf::DW_FORM_string); + EmitAbbrev(MCOS, dwarf::DW_AT_comp_dir, dwarf::DW_FORM_string); + StringRef DwarfDebugFlags = context.getDwarfDebugFlags(); + if (!DwarfDebugFlags.empty()) + EmitAbbrev(MCOS, dwarf::DW_AT_APPLE_flags, dwarf::DW_FORM_string); + EmitAbbrev(MCOS, dwarf::DW_AT_producer, dwarf::DW_FORM_string); + EmitAbbrev(MCOS, dwarf::DW_AT_language, dwarf::DW_FORM_data2); + EmitAbbrev(MCOS, 0, 0); + + // DW_TAG_subprogram DIE abbrev (2). + MCOS->EmitULEB128IntValue(2); + MCOS->EmitULEB128IntValue(dwarf::DW_TAG_subprogram); + MCOS->EmitIntValue(dwarf::DW_CHILDREN_yes, 1); + EmitAbbrev(MCOS, dwarf::DW_AT_name, dwarf::DW_FORM_string); + EmitAbbrev(MCOS, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data4); + EmitAbbrev(MCOS, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data4); + EmitAbbrev(MCOS, dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr); + EmitAbbrev(MCOS, dwarf::DW_AT_high_pc, dwarf::DW_FORM_addr); + EmitAbbrev(MCOS, dwarf::DW_AT_prototyped, dwarf::DW_FORM_flag); + EmitAbbrev(MCOS, 0, 0); + + // DW_TAG_unspecified_parameters DIE abbrev (3). + MCOS->EmitULEB128IntValue(3); + MCOS->EmitULEB128IntValue(dwarf::DW_TAG_unspecified_parameters); + MCOS->EmitIntValue(dwarf::DW_CHILDREN_no, 1); + EmitAbbrev(MCOS, 0, 0); + + // Terminate the abbreviations for this compilation unit. + MCOS->EmitIntValue(0, 1); +} + +// When generating dwarf for assembly source files this emits the data for +// .debug_aranges section. Which contains a header and a table of pairs of +// PointerSize'ed values for the address and size of section(s) with line table +// entries (just the default .text in our case) and a terminating pair of zeros. +static void EmitGenDwarfAranges(MCStreamer *MCOS) { + MCContext &context = MCOS->getContext(); + + // Create a symbol at the end of the section that we are creating the dwarf + // debugging info to use later in here as part of the expression to calculate + // the size of the section for the table. + MCOS->SwitchSection(context.getGenDwarfSection()); + MCSymbol *SectionEndSym = context.CreateTempSymbol(); + MCOS->EmitLabel(SectionEndSym); + context.setGenDwarfSectionEndSym(SectionEndSym); + + MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfARangesSection()); + + // This will be the length of the .debug_aranges section, first account for + // the size of each item in the header (see below where we emit these items). + int Length = 4 + 2 + 4 + 1 + 1; + + // Figure the padding after the header before the table of address and size + // pairs who's values are PointerSize'ed. + const MCAsmInfo &asmInfo = context.getAsmInfo(); + int AddrSize = asmInfo.getPointerSize(); + int Pad = 2 * AddrSize - (Length & (2 * AddrSize - 1)); + if (Pad == 2 * AddrSize) + Pad = 0; + Length += Pad; + + // Add the size of the pair of PointerSize'ed values for the address and size + // of the one default .text section we have in the table. + Length += 2 * AddrSize; + // And the pair of terminating zeros. + Length += 2 * AddrSize; + + + // Emit the header for this section. + // The 4 byte length not including the 4 byte value for the length. + MCOS->EmitIntValue(Length - 4, 4); + // The 2 byte version, which is 2. + MCOS->EmitIntValue(2, 2); + // The 4 byte offset to the compile unit in the .debug_info from the start + // of the .debug_info, it is at the start of that section so this is zero. + MCOS->EmitIntValue(0, 4); + // The 1 byte size of an address. + MCOS->EmitIntValue(AddrSize, 1); + // The 1 byte size of a segment descriptor, we use a value of zero. + MCOS->EmitIntValue(0, 1); + // Align the header with the padding if needed, before we put out the table. + for(int i = 0; i < Pad; i++) + MCOS->EmitIntValue(0, 1); + + // Now emit the table of pairs of PointerSize'ed values for the section(s) + // address and size, in our case just the one default .text section. + const MCExpr *Addr = MCSymbolRefExpr::Create( + context.getGenDwarfSectionStartSym(), MCSymbolRefExpr::VK_None, context); + const MCExpr *Size = MakeStartMinusEndExpr(*MCOS, + *context.getGenDwarfSectionStartSym(), *SectionEndSym, 0); + MCOS->EmitAbsValue(Addr, AddrSize); + MCOS->EmitAbsValue(Size, AddrSize); + + // And finally the pair of terminating zeros. + MCOS->EmitIntValue(0, AddrSize); + MCOS->EmitIntValue(0, AddrSize); +} + +// When generating dwarf for assembly source files this emits the data for +// .debug_info section which contains three parts. The header, the compile_unit +// DIE and a list of subprogram DIEs. +static void EmitGenDwarfInfo(MCStreamer *MCOS) { + MCContext &context = MCOS->getContext(); + + MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfInfoSection()); + + // Create a symbol at the start and end of this section used in here for the + // expression to calculate the length in the header. + MCSymbol *InfoStart = context.CreateTempSymbol(); + MCOS->EmitLabel(InfoStart); + MCSymbol *InfoEnd = context.CreateTempSymbol(); + + // First part: the header. + + // The 4 byte total length of the information for this compilation unit, not + // including these 4 bytes. + const MCExpr *Length = MakeStartMinusEndExpr(*MCOS, *InfoStart, *InfoEnd, 4); + MCOS->EmitAbsValue(Length, 4); + + // The 2 byte DWARF version, which is 2. + MCOS->EmitIntValue(2, 2); + + // The 4 byte offset to the debug abbrevs from the start of the .debug_abbrev, + // it is at the start of that section so this is zero. + MCOS->EmitIntValue(0, 4); + + const MCAsmInfo &asmInfo = context.getAsmInfo(); + int AddrSize = asmInfo.getPointerSize(); + // The 1 byte size of an address. + MCOS->EmitIntValue(AddrSize, 1); + + // Second part: the compile_unit DIE. + + // The DW_TAG_compile_unit DIE abbrev (1). + MCOS->EmitULEB128IntValue(1); + + // DW_AT_stmt_list, a 4 byte offset from the start of the .debug_line section, + // which is at the start of that section so this is zero. + MCOS->EmitIntValue(0, 4); + + // AT_low_pc, the first address of the default .text section. + const MCExpr *Start = MCSymbolRefExpr::Create( + context.getGenDwarfSectionStartSym(), MCSymbolRefExpr::VK_None, context); + MCOS->EmitAbsValue(Start, AddrSize); + + // AT_high_pc, the last address of the default .text section. + const MCExpr *End = MCSymbolRefExpr::Create( + context.getGenDwarfSectionEndSym(), MCSymbolRefExpr::VK_None, context); + MCOS->EmitAbsValue(End, AddrSize); + + // AT_name, the name of the source file. Reconstruct from the first directory + // and file table entries. + const std::vector &MCDwarfDirs = + context.getMCDwarfDirs(); + if (MCDwarfDirs.size() > 0) { + MCOS->EmitBytes(MCDwarfDirs[0], 0); + MCOS->EmitBytes("/", 0); + } + const std::vector &MCDwarfFiles = + MCOS->getContext().getMCDwarfFiles(); + MCOS->EmitBytes(MCDwarfFiles[1]->getName(), 0); + MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. + + // AT_comp_dir, the working directory the assembly was done in. + llvm::sys::Path CWD = llvm::sys::Path::GetCurrentDirectory(); + MCOS->EmitBytes(StringRef(CWD.c_str()), 0); + MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. + + // AT_APPLE_flags, the command line arguments of the assembler tool. + StringRef DwarfDebugFlags = context.getDwarfDebugFlags(); + if (!DwarfDebugFlags.empty()){ + MCOS->EmitBytes(DwarfDebugFlags, 0); + MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. + } + + // AT_producer, the version of the assembler tool. + MCOS->EmitBytes(StringRef("llvm-mc (based on LLVM "), 0); + MCOS->EmitBytes(StringRef(PACKAGE_VERSION), 0); + MCOS->EmitBytes(StringRef(")"), 0); + MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. + + // AT_language, a 4 byte value. We use DW_LANG_Mips_Assembler as the dwarf2 + // draft has no standard code for assembler. + MCOS->EmitIntValue(dwarf::DW_LANG_Mips_Assembler, 2); + + // Third part: the list of subprogram DIEs. + + // Loop on saved info for dwarf subprograms and create the DIEs for them. + const std::vector &Entries = + MCOS->getContext().getMCGenDwarfSubprogramEntries(); + for (std::vector::const_iterator it = + Entries.begin(), ie = Entries.end(); it != ie; + ++it) { + const MCGenDwarfSubprogramEntry *Entry = *it; + + // The DW_TAG_subprogram DIE abbrev (2). + MCOS->EmitULEB128IntValue(2); + + // AT_name, of the label without any leading underbar. + MCOS->EmitBytes(Entry->getName(), 0); + MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. + + // AT_decl_file, index into the file table. + MCOS->EmitIntValue(Entry->getFileNumber(), 4); + + // AT_decl_line, source line number. + MCOS->EmitIntValue(Entry->getLineNumber(), 4); + + // AT_low_pc, start address of the label. + const MCExpr *AT_low_pc = MCSymbolRefExpr::Create(Entry->getLabel(), + MCSymbolRefExpr::VK_None, context); + MCOS->EmitAbsValue(AT_low_pc, AddrSize); + + // AT_high_pc, end address which is the next label or end of the section. + std::vector::const_iterator next = it+1; + if (next != Entries.end()){ + const MCGenDwarfSubprogramEntry *NextEntry = *next; + const MCExpr *AT_high_pc = MCSymbolRefExpr::Create(NextEntry->getLabel(), + MCSymbolRefExpr::VK_None, context); + MCOS->EmitAbsValue(AT_high_pc, AddrSize); + } else { + MCOS->EmitAbsValue(End, AddrSize); + } + + // DW_AT_prototyped, a one byte flag value of 0 saying we have no prototype. + MCOS->EmitIntValue(0, 1); + + // The DW_TAG_unspecified_parameters DIE abbrev (3). + MCOS->EmitULEB128IntValue(3); + + // Add the NULL DIE terminating the DW_TAG_unspecified_parameters DIE's. + MCOS->EmitIntValue(0, 1); + } + // Deallocate the MCGenDwarfSubprogramEntry classes that saved away the info + // for the dwarf subprograms. + for (std::vector::const_iterator it = + Entries.begin(), ie = Entries.end(); it != ie; + ++it) { + const MCGenDwarfSubprogramEntry *Entry = *it; + delete Entry; + } + + // Add the NULL DIE terminating the Compile Unit DIE's. + MCOS->EmitIntValue(0, 1); + + // Now set the value of the symbol at the end of the info section. + MCOS->EmitLabel(InfoEnd); +} + +// +// When generating dwarf for assembly source files this emits the Dwarf +// sections. +// +void MCGenDwarfInfo::Emit(MCStreamer *MCOS) { + // Create the dwarf sections in this order (.debug_line already created). + MCContext &context = MCOS->getContext(); + MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfInfoSection()); + MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfAbbrevSection()); + MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfARangesSection()); + + // If there are no line table entries then do not emit any section contents. + if (context.getMCLineSections().empty()) + return; + + // Output the data for .debug_aranges section. + EmitGenDwarfAranges(MCOS); + + // Output the data for .debug_abbrev section. + EmitGenDwarfAbbrev(MCOS); + + // Output the data for .debug_info section. + EmitGenDwarfInfo(MCOS); +} + +// +// When generating dwarf for assembly source files this is called when symbol +// for a label is created. If this symbol is not a temporary and is in the +// section that dwarf is being generated for, save the needed info to create +// a dwarf subprogram. +// +void MCGenDwarfSubprogramEntry::Make(MCSymbol *Symbol, MCStreamer *MCOS, + SourceMgr &SrcMgr, SMLoc &Loc) { + // We won't create dwarf subprogram's for temporary symbols or symbols not in + // the default text. + if (Symbol->isTemporary()) + return; + MCContext &context = MCOS->getContext(); + if (context.getGenDwarfSection() != MCOS->getCurrentSection()) + return; + + // The dwarf subprogram's name does not have the symbol name's leading + // underbar if any. + StringRef Name = Symbol->getName(); + if (Name.startswith("_")) + Name = Name.substr(1, Name.size()-1); + + // Get the dwarf file number to be used for the dwarf subprogram. + unsigned FileNumber = context.getGenDwarfFileNumber(); + + // Finding the line number is the expensive part which is why we just don't + // pass it in as for some symbols we won't create a dwarf subprogram. + int CurBuffer = SrcMgr.FindBufferContainingLoc(Loc); + unsigned LineNumber = SrcMgr.FindLineNumber(Loc, CurBuffer); + + // We create a temporary symbol for use for the AT_high_pc and AT_low_pc + // values so that they don't have things like an ARM thumb bit from the + // original symbol. So when used they won't get a low bit set after + // relocation. + MCSymbol *Label = context.CreateTempSymbol(); + MCOS->EmitLabel(Label); + + // Create and entry for the info and add it to the other entries. + MCGenDwarfSubprogramEntry *Entry = + new MCGenDwarfSubprogramEntry(Name, FileNumber, LineNumber, Label); + MCOS->getContext().addMCGenDwarfSubprogramEntry(Entry); +} + static int getDataAlignmentFactor(MCStreamer &streamer) { MCContext &context = streamer.getContext(); const MCAsmInfo &asmInfo = context.getAsmInfo(); diff --git a/lib/MC/MCObjectStreamer.cpp b/lib/MC/MCObjectStreamer.cpp index 90c957f7be..663d0ca491 100644 --- a/lib/MC/MCObjectStreamer.cpp +++ b/lib/MC/MCObjectStreamer.cpp @@ -260,5 +260,9 @@ void MCObjectStreamer::Finish() { if (getContext().hasDwarfFiles()) MCDwarfFileTable::Emit(this); + // If we are generating dwarf for assembly source files dump out the sections. + if (getContext().getGenDwarfForAssembly()) + MCGenDwarfInfo::Emit(this); + getAssembler().Finish(); } diff --git a/lib/MC/MCParser/AsmParser.cpp b/lib/MC/MCParser/AsmParser.cpp index 7883893727..943f270a0a 100644 --- a/lib/MC/MCParser/AsmParser.cpp +++ b/lib/MC/MCParser/AsmParser.cpp @@ -468,6 +468,9 @@ bool AsmParser::Run(bool NoInitialTextSection, bool NoFinalize) { // section and generate a .file directive. if (getContext().getGenDwarfForAssembly()) { getContext().setGenDwarfSection(getStreamer().getCurrentSection()); + MCSymbol *SectionStartSym = getContext().CreateTempSymbol(); + getStreamer().EmitLabel(SectionStartSym); + getContext().setGenDwarfSectionStartSym(SectionStartSym); getStreamer().EmitDwarfFileDirective(getContext().nextGenDwarfFileNumber(), StringRef(), SrcMgr.getMemoryBuffer(CurBuffer)->getBufferIdentifier()); } @@ -1047,6 +1050,12 @@ bool AsmParser::ParseStatement() { // Emit the label. Out.EmitLabel(Sym); + // If we are generating dwarf for assembly source files then gather the + // info to make a dwarf subprogram entry for this label if needed. + if (getContext().getGenDwarfForAssembly()) + MCGenDwarfSubprogramEntry::Make(Sym, &getStreamer(), getSourceManager(), + IDLoc); + // Consume any end of statement token, if present, to avoid spurious // AddBlankLine calls(). if (Lexer.is(AsmToken::EndOfStatement)) { diff --git a/test/MC/MachO/gen-dwarf.s b/test/MC/MachO/gen-dwarf.s new file mode 100644 index 0000000000..6b851354cc --- /dev/null +++ b/test/MC/MachO/gen-dwarf.s @@ -0,0 +1,113 @@ +// RUN: llvm-mc -g -triple i386-apple-darwin10 %s -filetype=obj -o %t +// RUN: llvm-dwarfdump %t | FileCheck %s + +.globl _bar +_bar: + movl $0, %eax +L1: leave + ret +_foo: + nop +.data +_x: .long 1 + +// CHECK: file format Mach-O 32-bit i386 + +// CHECK: .debug_abbrev contents: +// CHECK: Abbrev table for offset: 0x00000000 +// CHECK: [1] DW_TAG_compile_unit DW_CHILDREN_yes +// CHECK: DW_AT_stmt_list DW_FORM_data4 +// CHECK: DW_AT_low_pc DW_FORM_addr +// CHECK: DW_AT_high_pc DW_FORM_addr +// CHECK: DW_AT_name DW_FORM_string +// CHECK: DW_AT_comp_dir DW_FORM_string +// CHECK: DW_AT_producer DW_FORM_string +// CHECK: DW_AT_language DW_FORM_data2 + +// CHECK: [2] DW_TAG_subprogram DW_CHILDREN_yes +// CHECK: DW_AT_name DW_FORM_string +// CHECK: DW_AT_decl_file DW_FORM_data4 +// CHECK: DW_AT_decl_line DW_FORM_data4 +// CHECK: DW_AT_low_pc DW_FORM_addr +// CHECK: DW_AT_high_pc DW_FORM_addr +// CHECK: DW_AT_prototyped DW_FORM_flag + +// CHECK: [3] DW_TAG_unspecified_parameters DW_CHILDREN_no + + +// CHECK: .debug_info contents: + +// We don't check the leading addresses these are at. +// CHECK: DW_TAG_compile_unit [1] * +// CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x00000000) +// CHECK: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000) +// CHECK: DW_AT_high_pc [DW_FORM_addr] (0x0000000000000008) +// We don't check the file name as it is a temp directory +// CHECK: DW_AT_name [DW_FORM_string] +// We don't check the DW_AT_comp_dir which is the current working directory +// CHECK: DW_AT_producer [DW_FORM_string] ("llvm-mc (based on LLVM 3.1svn)") +// CHECK: DW_AT_language [DW_FORM_data2] (0x8001) + +// CHECK: DW_TAG_subprogram [2] * +// CHECK: DW_AT_name [DW_FORM_string] ("bar") +// CHECK: DW_AT_decl_file [DW_FORM_data4] (0x00000001) +// CHECK: DW_AT_decl_line [DW_FORM_data4] (0x00000005) +// CHECK: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000) +// CHECK: DW_AT_high_pc [DW_FORM_addr] (0x0000000000000007) +// CHECK: DW_AT_prototyped [DW_FORM_flag] (0x00) + +// CHECK: DW_TAG_unspecified_parameters [3] + +// CHECK: NULL + +// CHECK: DW_TAG_subprogram [2] * +// CHECK: DW_AT_name [DW_FORM_string] ("foo") +// CHECK: DW_AT_decl_file [DW_FORM_data4] (0x00000001) +// CHECK: DW_AT_decl_line [DW_FORM_data4] (0x00000009) +// CHECK: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000007) +// CHECK: DW_AT_high_pc [DW_FORM_addr] (0x0000000000000008) +// CHECK: DW_AT_prototyped [DW_FORM_flag] (0x00) + +// CHECK: DW_TAG_unspecified_parameters [3] + +// CHECK: NULL + +// CHECK: NULL + +// CHECK: .debug_aranges contents: +// CHECK: Address Range Header: length = 0x0000001c, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x04, seg_size = 0x00 + +// CHECK: .debug_lines contents: +// CHECK: Line table prologue: +// We don't check the total_length as it includes lengths of temp paths +// CHECK: version: 2 +// We don't check the prologue_length as it too includes lengths of temp paths +// CHECK: min_inst_length: 1 +// CHECK: default_is_stmt: 1 +// CHECK: line_base: -5 +// CHECK: line_range: 14 +// CHECK: opcode_base: 13 +// CHECK: standard_opcode_lengths[DW_LNS_copy] = 0 +// CHECK: standard_opcode_lengths[DW_LNS_advance_pc] = 1 +// CHECK: standard_opcode_lengths[DW_LNS_advance_line] = 1 +// CHECK: standard_opcode_lengths[DW_LNS_set_file] = 1 +// CHECK: standard_opcode_lengths[DW_LNS_set_column] = 1 +// CHECK: standard_opcode_lengths[DW_LNS_negate_stmt] = 0 +// CHECK: standard_opcode_lengths[DW_LNS_set_basic_block] = 0 +// CHECK: standard_opcode_lengths[DW_LNS_const_add_pc] = 0 +// CHECK: standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 1 +// CHECK: standard_opcode_lengths[DW_LNS_set_prologue_end] = 0 +// CHECK: standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0 +// CHECK: standard_opcode_lengths[DW_LNS_set_isa] = 1 +// We don't check include_directories as it has a temp path +// CHECK: Dir Mod Time File Len File Name +// CHECK: ---- ---------- ---------- --------------------------- +// CHECK: file_names[ 1] 1 0x00000000 0x00000000 gen-dwarf.s + +// CHECK: Address Line Column File ISA Flags +// CHECK: ------------------ ------ ------ ------ --- ------------- +// CHECK: 0x0000000000000000 6 0 1 0 is_stmt +// CHECK: 0x0000000000000005 7 0 1 0 is_stmt +// CHECK: 0x0000000000000006 8 0 1 0 is_stmt +// CHECK: 0x0000000000000007 10 0 1 0 is_stmt +// CHECK: 0x0000000000000008 10 0 1 0 is_stmt end_sequence diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index 1139e36e26..4281259549 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -234,6 +234,17 @@ static tool_output_file *GetOutputStream() { return Out; } +static std::string DwarfDebugFlags; +static void setDwarfDebugFlags(int argc, char **argv) { + if (!getenv("RC_DEBUG_OPTIONS")) + return; + for (int i = 0; i < argc; i++) { + DwarfDebugFlags += argv[i]; + if (i + 1 < argc) + DwarfDebugFlags += " "; + } +} + static int AsLexInput(const char *ProgName) { OwningPtr BufferPtr; if (error_code ec = MemoryBuffer::getFileOrSTDIN(InputFilename, BufferPtr)) { @@ -382,6 +393,8 @@ static int AssembleInput(const char *ProgName) { Ctx.setAllowTemporaryLabels(false); Ctx.setGenDwarfForAssembly(GenDwarfForAssembly); + if (!DwarfDebugFlags.empty()) + Ctx.setDwarfDebugFlags(StringRef(DwarfDebugFlags)); // Package up features to be passed to target/subtarget std::string FeaturesStr; @@ -508,6 +521,7 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n"); TripleName = Triple::normalize(TripleName); + setDwarfDebugFlags(argc, argv); switch (Action) { default: -- cgit v1.2.3