Index: include/llvm/MC/MCAssembler.h =================================================================== --- include/llvm/MC/MCAssembler.h +++ include/llvm/MC/MCAssembler.h @@ -54,6 +54,7 @@ FT_Org, FT_Dwarf, FT_DwarfFrame, + FT_CoffLabelDiff, FT_LEB }; @@ -538,6 +539,35 @@ } }; +class MCCoffLabelDiffFragment : public MCFragment { + virtual void anchor(); + + /// AddrDelta - The expression for the difference of the two labels. + const MCExpr *AddrDelta; + + SmallString<8> Contents; + +public: + MCCoffLabelDiffFragment(const MCExpr &AddrDelta, MCSectionData *SD = 0) + : MCFragment(FT_CoffLabelDiff, SD), AddrDelta(&AddrDelta) { + Contents.assign(4u, '\0'); + } + + /// @name Accessors + /// @{ + + const MCExpr &getAddrDelta() const { return *AddrDelta; } + + SmallString<8> &getContents() { return Contents; } + const SmallString<8> &getContents() const { return Contents; } + + /// @} + + static bool classof(const MCFragment *F) { + return F->getKind() == MCFragment::FT_CoffLabelDiff; + } +}; + // FIXME: Should this be a separate class, or just merged into MCSection? Since // we anticipate the fast path being through an MCAssembler, the only reason to // keep it out is for API abstraction. @@ -932,6 +962,8 @@ bool relaxDwarfLineAddr(MCAsmLayout &Layout, MCDwarfLineAddrFragment &DF); bool relaxDwarfCallFrameFragment(MCAsmLayout &Layout, MCDwarfCallFrameFragment &DF); + bool relaxCoffLabelDiffFragment(MCAsmLayout &Layout, + MCCoffLabelDiffFragment &LDF); /// finishLayout - Finalize a layout, including fragment lowering. void finishLayout(MCAsmLayout &Layout); Index: include/llvm/MC/MCObjectFileInfo.h =================================================================== --- include/llvm/MC/MCObjectFileInfo.h +++ include/llvm/MC/MCObjectFileInfo.h @@ -129,6 +129,8 @@ const MCSection *DwarfGnuPubNamesSection; const MCSection *DwarfGnuPubTypesSection; + const MCSection *COFFDebugInfo; + // Extra TLS Variable Data section. If the target needs to put additional // information for a TLS variable, it'll go here. const MCSection *TLSExtraDataSection; @@ -281,6 +283,10 @@ return DwarfAddrSection; } + const MCSection *getCOFFDebugInfo() const { + return COFFDebugInfo; + } + const MCSection *getTLSExtraDataSection() const { return TLSExtraDataSection; } Index: include/llvm/MC/MCStreamer.h =================================================================== --- include/llvm/MC/MCStreamer.h +++ include/llvm/MC/MCStreamer.h @@ -407,11 +407,20 @@ /// EndCOFFSymbolDef - Marks the end of the symbol definition. virtual void EndCOFFSymbolDef() = 0; + /// EmitCOFFSectionReference - Emits a COFF section number. + /// + /// @param Symbol - Symbol the section relative realocation should point to. + virtual void EmitCOFFSectionReference(MCSymbol const *Symbol); + /// EmitCOFFSecRel32 - Emits a COFF section relative relocation. /// /// @param Symbol - Symbol the section relative realocation should point to. virtual void EmitCOFFSecRel32(MCSymbol const *Symbol); + /// EmitCOFFStaticOffset - Emits statically-known difference between two + /// labels in the same section. + virtual void EmitCOFFStaticOffset(const MCSymbol *From, const MCSymbol *To); + /// EmitELFSize - Emit an ELF .size directive. /// /// This corresponds to an assembler statement such as: Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -49,11 +49,13 @@ #include "llvm/Target/TargetOptions.h" #include "llvm/Target/TargetRegisterInfo.h" #include "llvm/Transforms/Utils/GlobalStatus.h" +#include "WinCodeViewLineTables.h" using namespace llvm; static const char *const DWARFGroupName = "DWARF Emission"; -static const char *const DbgTimerName = "DWARF Debug Writer"; +static const char *const DbgTimerName = "Debug Info Emission"; static const char *const EHTimerName = "DWARF Exception Writer"; +static const char *const CodeViewLineTablesGroupName = "CodeView Line Tables"; STATISTIC(EmittedInsts, "Number of machine instrs printed"); @@ -193,8 +195,14 @@ } if (MAI->doesSupportDebugInformation()) { - DD = new DwarfDebug(this, &M); - Handlers.push_back(HandlerInfo(DD, DbgTimerName, DWARFGroupName)); + if (Triple(TM.getTargetTriple()).isOSWindows()) { + Handlers.push_back(HandlerInfo(new WinCodeViewLineTables(this), + DbgTimerName, + CodeViewLineTablesGroupName)); + } else { + DD = new DwarfDebug(this, &M); + Handlers.push_back(HandlerInfo(DD, DbgTimerName, DWARFGroupName)); + } } DwarfException *DE = 0; Index: lib/CodeGen/AsmPrinter/CMakeLists.txt =================================================================== --- lib/CodeGen/AsmPrinter/CMakeLists.txt +++ lib/CodeGen/AsmPrinter/CMakeLists.txt @@ -13,6 +13,7 @@ ErlangGCPrinter.cpp OcamlGCPrinter.cpp Win64Exception.cpp + WinCodeViewLineTables.cpp ) add_dependencies(LLVMAsmPrinter intrinsics_gen) Index: lib/CodeGen/AsmPrinter/WinCodeViewLineTables.h =================================================================== --- /dev/null +++ lib/CodeGen/AsmPrinter/WinCodeViewLineTables.h @@ -0,0 +1,133 @@ +//===-- llvm/lib/CodeGen/AsmPrinter/WinCodeViewLineTables.h ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for writing line tables info into COFF files. +// +//===----------------------------------------------------------------------===// + +#ifndef CODEGEN_ASMPRINTER_WINCODEVIEWLINETABLES_H__ +#define CODEGEN_ASMPRINTER_WINCODEVIEWLINETABLES_H__ + +#include "AsmPrinterHandler.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/LexicalScopes.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/DebugLoc.h" +#include "llvm/Target/TargetLoweringObjectFile.h" + +namespace llvm { +/// \brief Collects and handles line tables information in a CodeView format. +class WinCodeViewLineTables : public AsmPrinterHandler { + AsmPrinter *Asm; + DebugLoc PrevInstLoc; + LexicalScopes LScopes; + + struct FunctionDI { + SmallVector Instrs; + MCSymbol *End; + } *CurFn; + + typedef DenseMap FnDebugInfoTy; + FnDebugInfoTy FnDebugInfo; + // Store the functions we've visited in a vector so we can maintain a stable + // order while emitting subsections. + SmallVector VisitedFunctions; + + struct InstrInfoTy { + StringRef Filename; + unsigned LineNumber; + + InstrInfoTy() : LineNumber(0) {} + + InstrInfoTy(StringRef Filename, unsigned LineNumber) + : Filename(Filename), LineNumber(LineNumber) {} + }; + DenseMap InstrInfo; + + struct FileNameRegistryTy { + SmallVector Filenames; + struct PerFileInfo { + size_t FilenameID, StartOffset; + }; + StringMap Infos; + size_t LastOffset; + + FileNameRegistryTy() { + clear(); + } + + void add(StringRef Filename) { + if (Infos.count(Filename)) + return; + size_t OldSize = Infos.size(); + Infos[Filename].FilenameID = OldSize; + Infos[Filename].StartOffset = LastOffset; + LastOffset += Filename.size() + 1; + Filenames.push_back(Filename); + } + + void clear() { + LastOffset = 1; + Infos.clear(); + Filenames.clear(); + } + } FileNameRegistry; + + // TODO: we can probably get rid of this map if MDNode holds the fullpath + // readily available. + typedef DenseMap, char *> + DirAndFilenameToFilepathMapTy; + DirAndFilenameToFilepathMapTy DirAndFilenameToFilepathMap; + StringRef getFullFilename(const MDNode *S); + + void maybeRecordLocation(DebugLoc DL, const MachineFunction *MF); + + void clear() { + assert(CurFn == 0); + FileNameRegistry.clear(); + InstrInfo.clear(); + } + +public: + WinCodeViewLineTables(AsmPrinter *Asm); + + ~WinCodeViewLineTables() { + for (DirAndFilenameToFilepathMapTy::iterator + I = DirAndFilenameToFilepathMap.begin(), + E = DirAndFilenameToFilepathMap.end(); + I != E; ++I) + free(I->second); + } + + virtual void setSymbolSize(const llvm::MCSymbol *, uint64_t) {} + + /// \brief Emit the COFF section that holds the line table information. + virtual void endModule(); + + /// \brief Gather pre-function debug information. + virtual void beginFunction(const MachineFunction *MF); + + /// \brief Gather post-function debug information. + virtual void endFunction(const MachineFunction *); + + /// \brief Process beginning of an instruction. + virtual void beginInstruction(const MachineInstr *MI); + + /// \brief Process end of an instruction. + virtual void endInstruction() {} +}; +} // End of namespace llvm + +#endif Index: lib/CodeGen/AsmPrinter/WinCodeViewLineTables.cpp =================================================================== --- /dev/null +++ lib/CodeGen/AsmPrinter/WinCodeViewLineTables.cpp @@ -0,0 +1,291 @@ +//===-- llvm/lib/CodeGen/AsmPrinter/WinCodeViewLineTables.cpp --*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for writing line tables info into COFF files. +// +//===----------------------------------------------------------------------===// + +#include "WinCodeViewLineTables.h" +#include "llvm/MC/MCSymbol.h" + +namespace llvm { + +StringRef WinCodeViewLineTables::getFullFilename(const MDNode *S) { + assert(S); + DIDescriptor D(S); + assert((D.isCompileUnit() || D.isFile() || D.isSubprogram() || + D.isLexicalBlockFile() || D.isLexicalBlock()) && + "Unexpected scope info"); + + DIScope Scope(S); + // TODO: can/shall we just put the full filepath in the metadata instead? + // This will require a change to Clang. + StringRef Dir = Scope.getDirectory(), Fn = Scope.getFilename(); + char *&Result = DirAndFilenameToFilepathMap[std::make_pair(Dir.data(), Fn.data())]; + if (Result != 0) + return Result; + + // filename may start with "./", which we don't want in the middle of a + // filepath. Just drop it! + // TODO: probably want to handle more complex cases with "./"? + std::string filename(Fn.data()); + if (filename.substr(0, 2) == "./") + filename = filename.substr(2); + // TODO: might want to replace "/" with "\" too... + std::string filepath = (Dir + Twine("\\") + filename).str(); + Result = strdup(filepath.c_str()); + + return StringRef(Result); +} + +void WinCodeViewLineTables::maybeRecordLocation(DebugLoc DL, + const MachineFunction *MF) { + const MDNode *Scope = DL.getScope(MF->getFunction()->getContext()); + if (!Scope) + return; + StringRef Filename = getFullFilename(Scope); + + // Skip this instruction if it has the same file:line as the previous one. + assert(CurFn); + if (!CurFn->Instrs.empty()) { + const InstrInfoTy &LastInstr = InstrInfo[CurFn->Instrs.back()]; + if (LastInstr.Filename == Filename && LastInstr.LineNumber == DL.getLine()) + return; + } + FileNameRegistry.add(Filename); + + MCSymbol *MCL = Asm->MMI->getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitLabel(MCL); + CurFn->Instrs.push_back(MCL); + InstrInfo[MCL] = InstrInfoTy(Filename, DL.getLine()); +} + +WinCodeViewLineTables::WinCodeViewLineTables(AsmPrinter *Asm) + : Asm(Asm), CurFn(0) { + // Tell MMI that we have debug info. + Asm->MMI->setDebugInfoAvailability(true); +} + +void WinCodeViewLineTables::endModule() { + if (FnDebugInfo.empty()) + return; + + Asm->OutStreamer.SwitchSection(Asm->getObjFileLowering().getCOFFDebugInfo()); + + Asm->EmitInt32(4); // Starting magic number. + + // The COFF .debug$S section consists of several subsections, each starting + // with a 4-byte control code (e.g. 0xF1, 0xF2, etc) and then a 4-byte length + // of the payload followed by the payload itself. The subsections are 4-byte + // aligned. + + // For each function there is a separate F2 subsection + // which holds the PC to file:line table. + for (size_t I = 0, E = VisitedFunctions.size(); I != E; ++I) { + const Function *GV = VisitedFunctions[I]; + const MCSymbol *Fn = Asm->getSymbol(GV); + const FunctionDI &FDI = FnDebugInfo[GV]; + assert(Fn); + assert(FDI.Instrs.size() > 0); + + // PC are grouped into segments sharing the same filename. + // Pre-calculate the lengths of these segments for convenience. + DenseMap FilenameSegmentLengths; + size_t LastSegmentEnd = 0; + StringRef PrevFilename = InstrInfo[FDI.Instrs[0]].Filename; + for (size_t J = 1, F = FDI.Instrs.size(); J != F; ++J) { + if (PrevFilename == InstrInfo[FDI.Instrs[J]].Filename) + continue; + FilenameSegmentLengths[LastSegmentEnd] = J - LastSegmentEnd; + LastSegmentEnd = J; + PrevFilename = InstrInfo[FDI.Instrs[J]].Filename; + } + FilenameSegmentLengths[LastSegmentEnd] = FDI.Instrs.size() - LastSegmentEnd; + + // Emit the control code of the subsection followed by the payload size. + Asm->OutStreamer.AddComment( + "Linetable subsection for " + Twine(Fn->getName())); + Asm->EmitInt32(0xF2); + MCSymbol *SubsectionBegin = Asm->MMI->getContext().CreateTempSymbol(), + *SubsectionEnd = Asm->MMI->getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitCOFFStaticOffset(SubsectionBegin, SubsectionEnd); + Asm->OutStreamer.EmitLabel(SubsectionBegin); + + // Identify the function this subsection is for. + Asm->OutStreamer.EmitCOFFSecRel32(Fn); + Asm->OutStreamer.EmitCOFFSectionReference(Fn); + + // Length of the function's code, in bytes. + Asm->OutStreamer.EmitCOFFStaticOffset(Fn, FDI.End); + + // PC-to-linenumber LUT: + MCSymbol *FileSegmentEnd = 0; + for (size_t J = 0, F = FDI.Instrs.size(); J != F; ++J) { + MCSymbol *Instr = FDI.Instrs[J]; + assert(InstrInfo.count(Instr)); + + if (FilenameSegmentLengths.count(J)) { + if (FileSegmentEnd) + Asm->OutStreamer.EmitLabel(FileSegmentEnd); + StringRef CurFilename = InstrInfo[FDI.Instrs[J]].Filename; + assert(FileNameRegistry.Infos.count(CurFilename)); + size_t IDInF3 = FileNameRegistry.Infos[CurFilename].FilenameID; + // Each segment starts with the offset of the filename in F3. + Asm->OutStreamer.AddComment( + "Segment for file '" + Twine(CurFilename) + "' begins"); + MCSymbol *FileSegmentBegin = Asm->MMI->getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitLabel(FileSegmentBegin); + Asm->EmitInt32(8 * IDInF3); + + // Number of PC records in the LUT. + size_t SegmentLength = FilenameSegmentLengths[J]; + Asm->EmitInt32(SegmentLength); + + // Full size of the segment for this filename, including the prev two + // records. + FileSegmentEnd = Asm->MMI->getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitCOFFStaticOffset(FileSegmentBegin, FileSegmentEnd); + } + + // The first PC with the given linenumber and the linenumber itself. + Asm->OutStreamer.EmitCOFFStaticOffset(Fn, Instr); + Asm->EmitInt32(InstrInfo[Instr].LineNumber); + } + if (FileSegmentEnd) + Asm->OutStreamer.EmitLabel(FileSegmentEnd); + Asm->OutStreamer.EmitLabel(SubsectionEnd); + } + + // A F4 subsection holds a file index to offset in string table table. + Asm->OutStreamer.AddComment("File index to string table offset subsection"); + Asm->EmitInt32(0xF4); + size_t NumFilenames = FileNameRegistry.Infos.size(); + Asm->EmitInt32(8 * NumFilenames); + for (size_t I = 0, E = FileNameRegistry.Filenames.size(); I != E; ++I) { + StringRef Filename = FileNameRegistry.Filenames[I]; + // For each unique filename, just write it's offset in the string table. + Asm->EmitInt32(FileNameRegistry.Infos[Filename].StartOffset); + // The function name offset is not followed by any additional data. + Asm->EmitInt32(0); + } + + // A F3 subsection holds the string table. + Asm->OutStreamer.AddComment("String table"); + Asm->EmitInt32(0xF3); + Asm->EmitInt32(FileNameRegistry.LastOffset); + // The payload starts with a null character. + Asm->EmitInt8(0); + + for (size_t I = 0, E = FileNameRegistry.Filenames.size(); I != E; ++I) { + // Just emit unique filenames one by one, separated by a null character. + Asm->OutStreamer.EmitBytes(FileNameRegistry.Filenames[I]); + Asm->EmitInt8(0); + } + + // No more subsections. Fill with zeros to align the end of the section by 4. + Asm->OutStreamer.EmitFill((-FileNameRegistry.LastOffset) % 4, 0); + + clear(); +} + +// TODO: share with DwardDebug.cpp? +// Get MDNode for DebugLoc's scope. +static MDNode *getScopeNode(DebugLoc DL, const LLVMContext &Ctx) { + if (MDNode *InlinedAt = DL.getInlinedAt(Ctx)) + return getScopeNode(DebugLoc::getFromDILocation(InlinedAt), Ctx); + return DL.getScope(Ctx); +} + +// TODO: share with DwardDebug.cpp? +// Walk up the scope chain of given debug loc and find line number info +// for the function. +static DebugLoc getFnDebugLoc(DebugLoc DL, const LLVMContext &Ctx) { + const MDNode *Scope = getScopeNode(DL, Ctx); + DISubprogram SP = getDISubprogram(Scope); + if (SP.isSubprogram()) { + // Check for number of operands since the compatibility is + // cheap here. + if (SP->getNumOperands() > 19) // TODO: do we need it? + return DebugLoc::get(SP.getScopeLineNumber(), 0, SP); + else + return DebugLoc::get(SP.getLineNumber(), 0, SP); + } + + return DebugLoc(); +} + +void WinCodeViewLineTables::beginFunction(const MachineFunction *MF) { + assert(!CurFn && "Can't process two functions at once!"); + + if (!Asm->MMI->hasDebugInfo()) + return; + + // Grab the lexical scopes for the function, if we don't have any of those + // then we're not going to be able to do anything. + LScopes.initialize(*MF); + if (LScopes.empty()) + return; + + const Function *GV = MF->getFunction(); + assert(FnDebugInfo.count(GV) == false); + VisitedFunctions.push_back(GV); + CurFn = &FnDebugInfo[GV]; + + // Find the end of the function prolog. + // TODO: is there a simpler a way to do this? Can we just search for the first + // instruction of the function, not the last of the prolog? + DebugLoc PrologEndLoc; + for (MachineFunction::const_iterator I = MF->begin(), E = MF->end(); + I != E && PrologEndLoc.isUnknown(); ++I) { + for (MachineBasicBlock::const_iterator II = I->begin(), IE = I->end(); + II != IE && PrologEndLoc.isUnknown(); ++II) { + const MachineInstr *MI = II; + if (MI->isDebugValue()) + continue; + + // First known non-DBG_VALUE and non-frame setup location marks + // the beginning of the function body. + // TODO: do I need the first subcondition? + if (!MI->getFlag(MachineInstr::FrameSetup) && + (PrologEndLoc.isUnknown() && !MI->getDebugLoc().isUnknown())) + PrologEndLoc = MI->getDebugLoc(); + } + } + // Record beginning of function. + if (!PrologEndLoc.isUnknown()) { + DebugLoc FnStartDL = + getFnDebugLoc(PrologEndLoc, MF->getFunction()->getContext()); + maybeRecordLocation(FnStartDL, MF); + } +} + +void WinCodeViewLineTables::endFunction(const MachineFunction *) { + if (!CurFn) // We haven't created any debug info for this function. + return; + + if (CurFn->Instrs.empty()) + llvm_unreachable("Can this ever happen?"); + + // Define end label for subprogram. + MCSymbol *FunctionEndSym = Asm->OutStreamer.getContext().CreateTempSymbol(); + Asm->OutStreamer.EmitLabel(FunctionEndSym); + CurFn->End = FunctionEndSym; + CurFn = 0; +} + +void WinCodeViewLineTables::beginInstruction(const MachineInstr *MI) { + // Ignore DBG_VALUE locations. + if (MI->isDebugValue()) + return; + DebugLoc DL = MI->getDebugLoc(); + if (DL == PrevInstLoc || DL.isUnknown()) + return; + maybeRecordLocation(DL, Asm->MF); +} +} Index: lib/MC/MCAsmStreamer.cpp =================================================================== --- lib/MC/MCAsmStreamer.cpp +++ lib/MC/MCAsmStreamer.cpp @@ -160,7 +160,9 @@ virtual void EmitCOFFSymbolStorageClass(int StorageClass); virtual void EmitCOFFSymbolType(int Type); virtual void EndCOFFSymbolDef(); + virtual void EmitCOFFSectionReference(MCSymbol const *Symbol); virtual void EmitCOFFSecRel32(MCSymbol const *Symbol); + virtual void EmitCOFFStaticOffset(const MCSymbol *From, const MCSymbol *To); virtual void EmitELFSize(MCSymbol *Symbol, const MCExpr *Value); virtual void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment); @@ -505,8 +507,19 @@ EmitEOL(); } +void MCAsmStreamer::EmitCOFFSectionReference(MCSymbol const *Symbol) { + OS << "\t.secref\t" << *Symbol; + EmitEOL(); +} + void MCAsmStreamer::EmitCOFFSecRel32(MCSymbol const *Symbol) { - OS << "\t.secrel32\t" << *Symbol << '\n'; + OS << "\t.secrel32\t" << *Symbol; + EmitEOL(); +} + +void MCAsmStreamer::EmitCOFFStaticOffset(const MCSymbol *From, + const MCSymbol *To) { + OS << "\t.offset from " << *From << " to " << *To; EmitEOL(); } Index: lib/MC/MCAssembler.cpp =================================================================== --- lib/MC/MCAssembler.cpp +++ lib/MC/MCAssembler.cpp @@ -469,6 +469,8 @@ return cast(F).getContents().size(); case MCFragment::FT_DwarfFrame: return cast(F).getContents().size(); + case MCFragment::FT_CoffLabelDiff: + return cast(F).getContents().size(); } llvm_unreachable("invalid fragment kind"); @@ -676,6 +678,11 @@ OW->WriteBytes(CF.getContents().str()); break; } + case MCFragment::FT_CoffLabelDiff: { + const MCCoffLabelDiffFragment &LDF = cast(F); + OW->WriteBytes(LDF.getContents().str()); + break; + } } assert(OW->getStream().tell() - Start == FragmentSize && @@ -937,6 +944,25 @@ return OldSize != Data.size(); } +bool MCAssembler::relaxCoffLabelDiffFragment(MCAsmLayout &Layout, + MCCoffLabelDiffFragment &LDF) { + MCContext &Context = Layout.getAssembler().getContext(); + int64_t AddrDelta = 0; + uint64_t OldSize = LDF.getContents().size(); + bool IsAbs = LDF.getAddrDelta().EvaluateAsAbsolute(AddrDelta, Layout); + (void)IsAbs; + assert(IsAbs); + SmallString<8> &Data = LDF.getContents(); + Data.clear(); + raw_svector_ostream OSE(Data); + OSE << uint8_t( AddrDelta & 0xff); + OSE << uint8_t((AddrDelta >> 8) & 0xff); + OSE << uint8_t((AddrDelta >> 16) & 0xff); + OSE << uint8_t((AddrDelta >> 24) & 0xff); + OSE.flush(); + return OldSize != Data.size(); +} + bool MCAssembler::layoutSectionOnce(MCAsmLayout &Layout, MCSectionData &SD) { // Holds the first fragment which needed relaxing during this layout. It will // remain NULL if none were relaxed. @@ -965,6 +991,10 @@ relaxDwarfCallFrameFragment(Layout, *cast(I)); break; + case MCFragment::FT_CoffLabelDiff: + RelaxedFrag = + relaxCoffLabelDiffFragment(Layout, *cast(I)); + break; case MCFragment::FT_LEB: RelaxedFrag = relaxLEB(Layout, *cast(I)); break; @@ -1027,6 +1057,7 @@ case MCFragment::FT_Org: OS << "MCOrgFragment"; break; case MCFragment::FT_Dwarf: OS << "MCDwarfFragment"; break; case MCFragment::FT_DwarfFrame: OS << "MCDwarfCallFrameFragment"; break; + case MCFragment::FT_CoffLabelDiff: OS << "MCCoffLabelDiffFragment"; break; case MCFragment::FT_LEB: OS << "MCLEBFragment"; break; } @@ -1114,6 +1145,12 @@ OS << " AddrDelta:" << CF->getAddrDelta(); break; } + case MCFragment::FT_CoffLabelDiff: { + const MCCoffLabelDiffFragment *LDF = cast(this); + OS << "\n "; + OS << " AddrDelta:" << LDF->getAddrDelta(); + break; + } case MCFragment::FT_LEB: { const MCLEBFragment *LF = cast(this); OS << "\n "; @@ -1185,3 +1222,4 @@ void MCLEBFragment::anchor() { } void MCDwarfLineAddrFragment::anchor() { } void MCDwarfCallFrameFragment::anchor() { } +void MCCoffLabelDiffFragment::anchor() { } \ No newline at end of file Index: lib/MC/MCObjectFileInfo.cpp =================================================================== --- lib/MC/MCObjectFileInfo.cpp +++ lib/MC/MCObjectFileInfo.cpp @@ -581,6 +581,13 @@ SectionKind::getReadOnly()); // Debug info. + COFFDebugInfo = + Ctx->getCOFFSection(".debug$S", + COFF::IMAGE_SCN_MEM_DISCARDABLE | + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ, + SectionKind::getMetadata()); + DwarfAbbrevSection = Ctx->getCOFFSection(".debug_abbrev", COFF::IMAGE_SCN_MEM_DISCARDABLE | Index: lib/MC/MCStreamer.cpp =================================================================== --- lib/MC/MCStreamer.cpp +++ lib/MC/MCStreamer.cpp @@ -566,10 +566,19 @@ EmitLabel(CurFrame->PrologEnd); } +void MCStreamer::EmitCOFFSectionReference(MCSymbol const *Symbol) { + llvm_unreachable("This file format doesn't support this directive"); +} + void MCStreamer::EmitCOFFSecRel32(MCSymbol const *Symbol) { llvm_unreachable("This file format doesn't support this directive"); } +void MCStreamer::EmitCOFFStaticOffset(const MCSymbol *From, + const MCSymbol *To) { + llvm_unreachable("This file format doesn't support this directive"); +} + /// EmitRawText - If this file is backed by an assembly streamer, this dumps /// the specified string in the output .s file. This capability is /// indicated by the hasRawTextSupport() predicate. Index: lib/MC/WinCOFFStreamer.cpp =================================================================== --- lib/MC/WinCOFFStreamer.cpp +++ lib/MC/WinCOFFStreamer.cpp @@ -61,7 +61,9 @@ virtual void EmitCOFFSymbolStorageClass(int StorageClass); virtual void EmitCOFFSymbolType(int Type); virtual void EndCOFFSymbolDef(); + virtual void EmitCOFFSectionReference(MCSymbol const *Symbol); virtual void EmitCOFFSecRel32(MCSymbol const *Symbol); + virtual void EmitCOFFStaticOffset(const MCSymbol *From, const MCSymbol *To); virtual void EmitELFSize(MCSymbol *Symbol, const MCExpr *Value); virtual void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment); @@ -249,6 +251,17 @@ CurSymbol = NULL; } +void WinCOFFStreamer::EmitCOFFSectionReference(MCSymbol const *Symbol) +{ + MCDataFragment *DF = getOrCreateDataFragment(); + + DF->getFixups().push_back( + MCFixup::Create(DF->getContents().size(), + MCSymbolRefExpr::Create (Symbol, getContext ()), + FK_SecRel_2)); + DF->getContents().resize(DF->getContents().size() + 4, 0); +} + void WinCOFFStreamer::EmitCOFFSecRel32(MCSymbol const *Symbol) { MCDataFragment *DF = getOrCreateDataFragment(); @@ -260,6 +273,18 @@ DF->getContents().resize(DF->getContents().size() + 4, 0); } +void WinCOFFStreamer::EmitCOFFStaticOffset(const MCSymbol *From, + const MCSymbol *To) { + const MCExpr *AddrDelta = BuildSymbolDiff(getContext(), To, From); + int64_t Res; + if (AddrDelta->EvaluateAsAbsolute(Res, getAssembler())) { + EmitIntValue(Res, 4); + return; + } + AddrDelta = ForceExpAbs(AddrDelta); + insert(new MCCoffLabelDiffFragment(*AddrDelta)); +} + void WinCOFFStreamer::EmitELFSize(MCSymbol *Symbol, const MCExpr *Value) { llvm_unreachable("not implemented"); } Index: lib/Target/X86/MCTargetDesc/X86WinCOFFObjectWriter.cpp =================================================================== --- lib/Target/X86/MCTargetDesc/X86WinCOFFObjectWriter.cpp +++ lib/Target/X86/MCTargetDesc/X86WinCOFFObjectWriter.cpp @@ -65,6 +65,9 @@ if (Is64Bit) return COFF::IMAGE_REL_AMD64_ADDR64; llvm_unreachable("unsupported relocation type"); + case FK_SecRel_2: + return Is64Bit ? COFF::IMAGE_REL_AMD64_SECTION + : COFF::IMAGE_REL_I386_SECTION; case FK_SecRel_4: return Is64Bit ? COFF::IMAGE_REL_AMD64_SECREL : COFF::IMAGE_REL_I386_SECREL; default: Index: test/DebugInfo/X86/CodeView/asm.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/CodeView/asm.ll @@ -0,0 +1,35 @@ +; For now, just make sure we don't crash +; RUN: llc -mtriple=i686-pc-win32 -filetype=obj -o %t -O0 < %s + +; Function Attrs: nounwind +define void @f() #0 { +entry: + call void asm sideeffect inteldialect ".align 4", "~{dirflag},~{fpsr},~{flags}"() #2, !dbg !11 + call void bitcast (void (...)* @g to void ()*)(), !dbg !12 + ret void, !dbg !13 +} + +declare void @g(...) #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!9} +!llvm.ident = !{!10} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.4 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""} ; [ DW_TAG_compile_unit ] [D:\/] [DW_LANG_C99] +!1 = metadata !{metadata !"", metadata !"D:\5C"} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"f", metadata !"f", metadata !"", i32 3, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 0, i1 false, void ()* @f, null, null, metadata !2, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [f] +!5 = metadata !{metadata !"test.c", metadata !"D:\5C"} +!6 = metadata !{i32 786473, metadata !5} ; [ DW_TAG_file_type ] [D:\/test.c] +!7 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!8 = metadata !{null} +!9 = metadata !{i32 2, metadata !"Dwarf Version", i32 4} +!10 = metadata !{metadata !"clang version 3.4 "} +!11 = metadata !{i32 4, i32 0, metadata !4, null} +!12 = metadata !{i32 5, i32 0, metadata !4, null} +!13 = metadata !{i32 6, i32 0, metadata !4, null} Index: test/DebugInfo/X86/CodeView/multifile.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/CodeView/multifile.ll @@ -0,0 +1,194 @@ +; RUN: llc -mtriple=i686-pc-win32 -filetype=asm -O0 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-pc-win32 -filetype=asm -O0 < %s | FileCheck --check-prefix=X64 %s + +; CHECK: _f: +; CHECK-NEXT: [[PROLOG:.*]]:{{$}} +; CHECK: [[CALL_LINE_1:.*]]:{{$}} +; CHECK-NEXT: calll _g +; CHECK-NEXT: [[CALL_LINE_2:.*]]:{{$}} +; CHECK-NEXT: calll _g +; CHECK-NEXT: [[CALL_LINE_3:.*]]:{{$}} +; CHECK-NEXT: calll _g +; CHECK-NEXT: [[RETURN_STMT:.*]]: +; CHECK-NEXT: ret +; CHECK-NEXT: [[END_OF_F:.*]]: +; +; CHECK: .section .debug$S,"rn" +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 242 +; CHECK-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; CHECK-NEXT: [[F2_START]]: +; CHECK-NEXT: .secrel32 _f +; CHECK-NEXT: .secref _f +; CHECK-NEXT: .offset from _f to [[END_OF_F]] +; Segment for file 'D:\\input.c' begins +; CHECK-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; CHECK-NEXT: .offset from _f to [[PROLOG]] +; CHECK-NEXT: .long 3 +; CHECK-NEXT: [[FILE_SEGMENT_END]]: +; Segment for file 'D:\\one.c' begins +; CHECK-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; CHECK-NEXT: .offset from _f to [[CALL_LINE_1]] +; CHECK-NEXT: .long 1 +; CHECK-NEXT: [[FILE_SEGMENT_END]]: +; Segment for file 'D:\\two.c' begins +; CHECK-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; CHECK-NEXT: .long 16 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; CHECK-NEXT: .offset from _f to [[CALL_LINE_2]] +; CHECK-NEXT: .long 2 +; CHECK-NEXT: [[FILE_SEGMENT_END]]: +; A new segment for file 'D:\\one.c' begins +; CHECK-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; CHECK-NEXT: .offset from _f to [[CALL_LINE_3]] +; CHECK-NEXT: .long 7 +; CHECK-NEXT: .offset from _f to [[RETURN_STMT]] +; CHECK-NEXT: .long 8 +; CHECK-NEXT: [[FILE_SEGMENT_END]]: +; CHECK-NEXT: [[F2_END]]: +; File index to string table offset subsection +; CHECK-NEXT: .long 244 +; CHECK-NEXT: .long 24 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 13 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 23 +; CHECK-NEXT: .long 0 +; String table +; CHECK-NEXT: .long 243 +; CHECK-NEXT: .long 33 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "D:\\\\input.c" +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "D:\\\\one.c" +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "D:\\\\two.c" +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .zero 3 + +; X64: f: +; X64-NEXT: [[START:.*]]:{{$}} +; X64: [[PROLOG_AND_CALL_1:.*]]:{{$}} +; X64-NEXT: subq $40, %rsp +; X64-NEXT: callq g +; X64-NEXT: [[CALL_LINE_2:.*]]:{{$}} +; X64-NEXT: callq g +; X64-NEXT: [[CALL_LINE_3:.*]]:{{$}} +; X64-NEXT: callq g +; X64-NEXT: [[EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_F:.*]]: +; +; X64: .section .debug$S,"rn" +; X64-NEXT: .long 4 +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 f +; X64-NEXT: .secref f +; X64-NEXT: .offset from f to [[END_OF_F]] +; Segment for file 'D:\\input.c' begins +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 1 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[START]] +; X64-NEXT: .long 3 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; Segment for file 'D:\\one.c' begins +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 8 +; X64-NEXT: .long 1 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[PROLOG_AND_CALL_1]] +; X64-NEXT: .long 1 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; Segment for file 'D:\\two.c' begins +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 16 +; X64-NEXT: .long 1 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[CALL_LINE_2]] +; X64-NEXT: .long 2 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; A new segment for file 'D:\\one.c' begins +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 8 +; X64-NEXT: .long 2 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[CALL_LINE_3]] +; X64-NEXT: .long 7 +; X64-NEXT: .offset from f to [[EPILOG_AND_RET]] +; X64-NEXT: .long 8 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; File index to string table offset subsection +; X64-NEXT: .long 244 +; X64-NEXT: .long 24 +; X64-NEXT: .long 1 +; X64-NEXT: .long 0 +; X64-NEXT: .long 13 +; X64-NEXT: .long 0 +; X64-NEXT: .long 23 +; X64-NEXT: .long 0 +; String table +; X64-NEXT: .long 243 +; X64-NEXT: .long 33 +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\\\input.c" +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\\\one.c" +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\\\two.c" +; X64-NEXT: .byte 0 +; X64-NEXT: .zero 3 + +define void @f() #0 { +entry: + call void bitcast (void (...)* @g to void ()*)(), !dbg !12 + call void bitcast (void (...)* @g to void ()*)(), !dbg !15 + call void bitcast (void (...)* @g to void ()*)(), !dbg !18 + ret void, !dbg !19 +} + +declare void @g(...) #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!9, !10} +!llvm.ident = !{!11} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""} ; [ DW_TAG_compile_unit ] [D:\/] [DW_LANG_C99] +!1 = metadata !{metadata !"", metadata !"D:\5C"} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"f", metadata !"f", metadata !"", i32 3, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 0, i1 false, void ()* @f, null, null, metadata !2, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [f] +!5 = metadata !{metadata !"input.c", metadata !"D:\5C"} +!6 = metadata !{i32 786473, metadata !5} ; [ DW_TAG_file_type ] [D:\/input.c] +!7 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!8 = metadata !{null} +!9 = metadata !{i32 2, metadata !"Dwarf Version", i32 4} +!10 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!11 = metadata !{metadata !"clang version 3.5 "} +!12 = metadata !{i32 1, i32 0, metadata !13, null} +!13 = metadata !{i32 786443, metadata !14, metadata !4} ; [ DW_TAG_lexical_block ] [D:\/one.c] +!14 = metadata !{metadata !"one.c", metadata !"D:\5C"} +!15 = metadata !{i32 2, i32 0, metadata !16, null} +!16 = metadata !{i32 786443, metadata !17, metadata !4} ; [ DW_TAG_lexical_block ] [D:\/two.c] +!17 = metadata !{metadata !"two.c", metadata !"D:\5C"} +!18 = metadata !{i32 7, i32 0, metadata !13, null} +!19 = metadata !{i32 8, i32 0, metadata !13, null} ; [ DW_TAG_imported_declaration ] Index: test/DebugInfo/X86/CodeView/multifunction.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/CodeView/multifunction.ll @@ -0,0 +1,270 @@ +; RUN: llc -mtriple=i686-pc-win32 -filetype=asm -O0 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-pc-win32 -filetype=asm -O0 < %s | FileCheck --check-prefix=X64 %s + +; CHECK: _x: +; CHECK-NEXT: [[X_PROLOG:.*]]:{{$}} +; CHECK: [[X_CALL:.*]]:{{$}} +; CHECK-NEXT: calll _z +; CHECK-NEXT: [[X_RETURN:.*]]: +; CHECK-NEXT: ret +; CHECK-NEXT: [[END_OF_X:.*]]: +; +; CHECK: _y: +; CHECK-NEXT: [[Y_PROLOG:.*]]:{{$}} +; CHECK: [[Y_CALL:.*]]:{{$}} +; CHECK-NEXT: calll _z +; CHECK-NEXT: [[Y_RETURN:.*]]: +; CHECK-NEXT: ret +; CHECK-NEXT: [[END_OF_Y:.*]]: +; +; CHECK: _f: +; CHECK-NEXT: [[F_PROLOG:.*]]:{{$}} +; CHECK: [[F_CALLS_X:.*]]:{{$}} +; CHECK-NEXT: calll _x +; CHECK-NEXT: [[F_CALLS_Y:.*]]: +; CHECK-NEXT: calll _y +; CHECK-NEXT: [[F_CALLS_Z:.*]]: +; CHECK-NEXT: calll _z +; CHECK-NEXT: [[F_RETURN:.*]]: +; CHECK-NEXT: ret +; CHECK-NEXT: [[END_OF_F:.*]]: +; +; CHECK: .section .debug$S,"rn" +; CHECK-NEXT: .long 4 +; Line table subsection for x +; CHECK-NEXT: .long 242 +; CHECK-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; CHECK-NEXT: [[F2_START]]: +; CHECK-NEXT: .secrel32 _x +; CHECK-NEXT: .secref _x +; CHECK-NEXT: .offset from _x to [[END_OF_X]] +; CHECK-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; CHECK-NEXT: .offset from _x to [[X_PROLOG]] +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .offset from _x to [[X_CALL]] +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .offset from _x to [[X_RETURN]] +; CHECK-NEXT: .long 5 +; CHECK-NEXT: [[FILE_SEGMENT_END]]: +; CHECK-NEXT: [[F2_END]]: +; Line table subsection for y +; CHECK-NEXT: .long 242 +; CHECK-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; CHECK-NEXT: [[F2_START]]: +; CHECK-NEXT: .secrel32 _y +; CHECK-NEXT: .secref _y +; CHECK-NEXT: .offset from _y to [[END_OF_Y]] +; CHECK-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; CHECK-NEXT: .offset from _y to [[Y_PROLOG]] +; CHECK-NEXT: .long 7 +; CHECK-NEXT: .offset from _y to [[Y_CALL]] +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .offset from _y to [[Y_RETURN]] +; CHECK-NEXT: .long 9 +; CHECK-NEXT: [[FILE_SEGMENT_END]]: +; CHECK-NEXT: [[F2_END]]: +; Line table subsection for f +; CHECK-NEXT: .long 242 +; CHECK-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; CHECK-NEXT: [[F2_START]]: +; CHECK-NEXT: .secrel32 _f +; CHECK-NEXT: .secref _f +; CHECK-NEXT: .offset from _f to [[END_OF_F]] +; CHECK-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 5 +; CHECK-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; CHECK-NEXT: .offset from _f to [[F_PROLOG]] +; CHECK-NEXT: .long 11 +; CHECK-NEXT: .offset from _f to [[F_CALLS_X]] +; CHECK-NEXT: .long 12 +; CHECK-NEXT: .offset from _f to [[F_CALLS_Y]] +; CHECK-NEXT: .long 13 +; CHECK-NEXT: .offset from _f to [[F_CALLS_Z]] +; CHECK-NEXT: .long 14 +; CHECK-NEXT: .offset from _f to [[F_RETURN]] +; CHECK-NEXT: .long 15 +; CHECK-NEXT: [[FILE_SEGMENT_END]]: +; CHECK-NEXT: [[F2_END]]: +; File index to string table offset subsection +; CHECK-NEXT: .long 244 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 0 +; String table +; CHECK-NEXT: .long 243 +; CHECK-NEXT: .long 14 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "D:\\\\source.c" +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .zero 2 + +; X64: x: +; X64-NEXT: [[X_START:.*]]:{{$}} +; X64: [[X_PROLOG_AND_CALL:.*]]:{{$}} +; X64-NEXT: subq $40, %rsp +; X64-NEXT: callq z +; X64-NEXT: [[X_EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_X:.*]]: +; +; X64: y: +; X64-NEXT: [[Y_START:.*]]:{{$}} +; X64: [[Y_PROLOG_AND_CALL:.*]]:{{$}} +; X64-NEXT: subq $40, %rsp +; X64-NEXT: callq z +; X64-NEXT: [[Y_EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_Y:.*]]: +; +; X64: f: +; X64-NEXT: [[F_START:.*]]:{{$}} +; X64: [[F_PROLOG_AND_CALL_X:.*]]:{{$}} +; X64-NEXT: subq $40, %rsp +; X64-NEXT: callq x +; X64-NEXT: [[F_CALLS_Y:.*]]: +; X64-NEXT: callq y +; X64-NEXT: [[F_CALLS_Z:.*]]: +; X64-NEXT: callq z +; X64-NEXT: [[F_EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_F:.*]]: +; +; X64: .section .debug$S,"rn" +; X64-NEXT: .long 4 +; Line table subsection for x +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 x +; X64-NEXT: .secref x +; X64-NEXT: .offset from x to [[END_OF_X]] +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 3 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from x to [[X_START]] +; X64-NEXT: .long 3 +; X64-NEXT: .offset from x to [[X_PROLOG_AND_CALL]] +; X64-NEXT: .long 4 +; X64-NEXT: .offset from x to [[X_EPILOG_AND_RET]] +; X64-NEXT: .long 5 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; Line table subsection for y +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 y +; X64-NEXT: .secref y +; X64-NEXT: .offset from y to [[END_OF_Y]] +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 3 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from y to [[Y_START]] +; X64-NEXT: .long 7 +; X64-NEXT: .offset from y to [[Y_PROLOG_AND_CALL]] +; X64-NEXT: .long 8 +; X64-NEXT: .offset from y to [[Y_EPILOG_AND_RET]] +; X64-NEXT: .long 9 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; Line table subsection for f +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 f +; X64-NEXT: .secref f +; X64-NEXT: .offset from f to [[END_OF_F]] +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 5 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[F_START]] +; X64-NEXT: .long 11 +; X64-NEXT: .offset from f to [[F_PROLOG_AND_CALL_X]] +; X64-NEXT: .long 12 +; X64-NEXT: .offset from f to [[F_CALLS_Y]] +; X64-NEXT: .long 13 +; X64-NEXT: .offset from f to [[F_CALLS_Z]] +; X64-NEXT: .long 14 +; X64-NEXT: .offset from f to [[F_EPILOG_AND_RET]] +; X64-NEXT: .long 15 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; File index to string table offset subsection +; X64-NEXT: .long 244 +; X64-NEXT: .long 8 +; X64-NEXT: .long 1 +; X64-NEXT: .long 0 +; String table +; X64-NEXT: .long 243 +; X64-NEXT: .long 14 +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\\\source.c" +; X64-NEXT: .byte 0 +; X64-NEXT: .zero 2 + +define void @x() #0 { +entry: + call void bitcast (void (...)* @z to void ()*)(), !dbg !14 + ret void, !dbg !15 +} + +declare void @z(...) #1 + +; Function Attrs: nounwind +define void @y() #0 { +entry: + call void bitcast (void (...)* @z to void ()*)(), !dbg !16 + ret void, !dbg !17 +} + +; Function Attrs: nounwind +define void @f() #0 { +entry: + call void @x(), !dbg !18 + call void @y(), !dbg !19 + call void bitcast (void (...)* @z to void ()*)(), !dbg !20 + ret void, !dbg !21 +} + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!11, !12} +!llvm.ident = !{!13} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""} ; [ DW_TAG_compile_unit ] [D:\/] [DW_LANG_C99] +!1 = metadata !{metadata !"", metadata !"D:\5C"} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4, metadata !9, metadata !10} +!4 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"x", metadata !"x", metadata !"", i32 3, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 0, i1 false, void ()* @x, null, null, metadata !2, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [x] +!5 = metadata !{metadata !"source.c", metadata !"D:\5C"} +!6 = metadata !{i32 786473, metadata !5} ; [ DW_TAG_file_type ] [D:\/source.c] +!7 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!8 = metadata !{null} +!9 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"y", metadata !"y", metadata !"", i32 7, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 0, i1 false, void ()* @y, null, null, metadata !2, i32 7} ; [ DW_TAG_subprogram ] [line 7] [def] [y] +!10 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"f", metadata !"f", metadata !"", i32 11, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 0, i1 false, void ()* @f, null, null, metadata !2, i32 11} ; [ DW_TAG_subprogram ] [line 11] [def] [f] +!11 = metadata !{i32 2, metadata !"Dwarf Version", i32 4} +!12 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!13 = metadata !{metadata !"clang version 3.5 "} +!14 = metadata !{i32 4, i32 0, metadata !4, null} +!15 = metadata !{i32 5, i32 0, metadata !4, null} +!16 = metadata !{i32 8, i32 0, metadata !9, null} ; [ DW_TAG_imported_declaration ] +!17 = metadata !{i32 9, i32 0, metadata !9, null} +!18 = metadata !{i32 12, i32 0, metadata !10, null} +!19 = metadata !{i32 13, i32 0, metadata !10, null} +!20 = metadata !{i32 14, i32 0, metadata !10, null} +!21 = metadata !{i32 15, i32 0, metadata !10, null} Index: test/DebugInfo/X86/CodeView/simple.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/CodeView/simple.ll @@ -0,0 +1,118 @@ +; RUN: llc -mtriple=i686-pc-win32 -filetype=asm -O0 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-pc-win32 -filetype=asm -O0 < %s | FileCheck --check-prefix=X64 %s + +; CHECK: _f: +; CHECK-NEXT: [[PROLOG:.*]]:{{$}} +; CHECK: [[CALL_LINE:.*]]:{{$}} +; CHECK-NEXT: calll _g +; CHECK-NEXT: [[RETURN_STMT:.*]]: +; CHECK-NEXT: ret +; CHECK-NEXT: [[END_OF_F:.*]]: +; +; CHECK: .section .debug$S,"rn" +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long 242 +; CHECK-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; CHECK-NEXT: [[F2_START]]: +; CHECK-NEXT: .secrel32 _f +; CHECK-NEXT: .secref _f +; CHECK-NEXT: .offset from _f to [[END_OF_F]] +; CHECK-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; CHECK-NEXT: .offset from _f to [[PROLOG]] +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .offset from _f to [[CALL_LINE]] +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .offset from _f to [[RETURN_STMT]] +; CHECK-NEXT: .long 5 +; CHECK-NEXT: [[FILE_SEGMENT_END]]: +; CHECK-NEXT: [[F2_END]]: +; File index to string table offset subsection +; CHECK-NEXT: .long 244 +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 0 +; String table +; CHECK-NEXT: .long 243 +; CHECK-NEXT: .long 12 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .ascii "D:\\\\test.c" +; CHECK-NEXT: .byte 0 +; No extra padding needed +; CHECK-NOT: .zero + +; X64: f: +; X64-NEXT: [[START:.*]]:{{$}} +; X64: [[PROLOG_AND_CALL:.*]]:{{$}} +; X64-NEXT: subq $40, %rsp +; X64-NEXT: callq g +; X64-NEXT: [[EPILOG_AND_RET:.*]]: +; X64-NEXT: addq $40, %rsp +; X64-NEXT: ret +; X64-NEXT: [[END_OF_F:.*]]: +; +; X64: .section .debug$S,"rn" +; X64-NEXT: .long 4 +; X64-NEXT: .long 242 +; X64-NEXT: .offset from [[F2_START:.*]] to [[F2_END:.*]] +; X64-NEXT: [[F2_START]]: +; X64-NEXT: .secrel32 f +; X64-NEXT: .secref f +; X64-NEXT: .offset from f to [[END_OF_F]] +; X64-NEXT: [[FILE_SEGMENT_START:[^:]*]]: +; X64-NEXT: .long 0 +; X64-NEXT: .long 3 +; X64-NEXT: .offset from [[FILE_SEGMENT_START]] to [[FILE_SEGMENT_END:.*]] +; X64-NEXT: .offset from f to [[START]] +; X64-NEXT: .long 3 +; X64-NEXT: .offset from f to [[PROLOG_AND_CALL]] +; X64-NEXT: .long 4 +; X64-NEXT: .offset from f to [[EPILOG_AND_RET]] +; X64-NEXT: .long 5 +; X64-NEXT: [[FILE_SEGMENT_END]]: +; X64-NEXT: [[F2_END]]: +; File index to string table offset subsection +; X64-NEXT: .long 244 +; X64-NEXT: .long 8 +; X64-NEXT: .long 1 +; X64-NEXT: .long 0 +; String table +; X64-NEXT: .long 243 +; X64-NEXT: .long 12 +; X64-NEXT: .byte 0 +; X64-NEXT: .ascii "D:\\\\test.c" +; X64-NEXT: .byte 0 +; No extra padding needed +; X64-NOT: .zero + +define void @f() #0 { +entry: + call void bitcast (void (...)* @g to void ()*)(), !dbg !12 + ret void, !dbg !13 +} + +declare void @g(...) #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!9, !10} +!llvm.ident = !{!11} + +!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""} ; [ DW_TAG_compile_unit ] [D:\/] [DW_LANG_C99] +!1 = metadata !{metadata !"", metadata !"D:\5C"} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4} +!4 = metadata !{i32 786478, metadata !5, metadata !6, metadata !"f", metadata !"f", metadata !"", i32 3, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 0, i1 false, void ()* @f, null, null, metadata !2, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [f] +!5 = metadata !{metadata !"test.c", metadata !"D:\5C"} +!6 = metadata !{i32 786473, metadata !5} ; [ DW_TAG_file_type ] [D:\/test.c] +!7 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ] +!8 = metadata !{null} +!9 = metadata !{i32 2, metadata !"Dwarf Version", i32 4} +!10 = metadata !{i32 1, metadata !"Debug Info Version", i32 1} +!11 = metadata !{metadata !"clang version 3.5 "} +!12 = metadata !{i32 4, i32 0, metadata !4, null} +!13 = metadata !{i32 5, i32 0, metadata !4, null}