Index: docs/Extensions.rst =================================================================== --- docs/Extensions.rst +++ docs/Extensions.rst @@ -285,6 +285,47 @@ The paramter identifies an additional library search path to be considered when looking up libraries after the inclusion of this option. +``SHT_LLVM_CALL_GRAPH_PROFILE`` Section (Call Graph Profile) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This section is used to pass a call graph profile to the linker which can be +used to optimize the placement of sections. It contains a sequence of +(from symbol, to symbol, weight) tuples. + +It shall have a type of ``SHT_LLVM_CALL_GRAPH_PROFILE`` (0x6fff4c02), shall +have the ``SHF_EXCLUDE`` flag set, the ``sh_link`` member shall hold the section +header index of the associated symbol table, and shall have a ``sh_entsize`` of +16. It should be named ``.llvm.call-graph-profile``. + +The contents of the section shall be a sequence of ``Elf_CGProfile`` entries. + +.. code-block:: c + + typedef struct { + Elf_Word cgp_from; + Elf_Word cgp_to; + Elf_Xword cgp_weight; + } Elf_CGProfile; + +cgp_from + The symbol index of the source of the edge. + +cgp_to + The symbol index of the destination of the edge. + +cgp_weight + The weight of the edge. + +This is represented in assembly as: + +.. code-block:: gas + + .cg_profile from, to, 42 + +If this is the first usage of either of the symbols then that symbol is declared +as if ``.weak symbol`` had been written. This forces the symbol to show up in +the symbol table. + Target Specific Behaviour ========================= Index: include/llvm/BinaryFormat/ELF.h =================================================================== --- include/llvm/BinaryFormat/ELF.h +++ include/llvm/BinaryFormat/ELF.h @@ -803,6 +803,7 @@ SHT_ANDROID_RELA = 0x60000002, SHT_LLVM_ODRTAB = 0x6fff4c00, // LLVM ODR table. SHT_LLVM_LINKER_OPTIONS = 0x6fff4c01, // LLVM Linker Options. + SHT_LLVM_CALL_GRAPH_PROFILE = 0x6fff4c02, // LLVM Call Graph Profile. SHT_GNU_ATTRIBUTES = 0x6ffffff5, // Object attributes. SHT_GNU_HASH = 0x6ffffff6, // GNU-style hash table. SHT_GNU_verdef = 0x6ffffffd, // GNU version definitions. Index: include/llvm/MC/MCAssembler.h =================================================================== --- include/llvm/MC/MCAssembler.h +++ include/llvm/MC/MCAssembler.h @@ -418,6 +418,13 @@ const MCLOHContainer &getLOHContainer() const { return const_cast(this)->getLOHContainer(); } + + struct CGProfileEntry { + const MCSymbol *From; + const MCSymbol *To; + uint64_t Count; + }; + std::vector CGProfile; /// @} /// \name Backend Data Access /// @{ Index: include/llvm/MC/MCELFStreamer.h =================================================================== --- include/llvm/MC/MCELFStreamer.h +++ include/llvm/MC/MCELFStreamer.h @@ -69,6 +69,9 @@ void EmitValueToAlignment(unsigned, int64_t, unsigned, unsigned) override; + void emitCGProfileEntry(const MCSymbol *From, const MCSymbol *To, + uint64_t Count) override; + void FinishImpl() override; void EmitBundleAlignMode(unsigned AlignPow2) override; Index: include/llvm/MC/MCStreamer.h =================================================================== --- include/llvm/MC/MCStreamer.h +++ include/llvm/MC/MCStreamer.h @@ -901,6 +901,9 @@ SMLoc Loc = SMLoc()); virtual void EmitWinEHHandlerData(SMLoc Loc = SMLoc()); + virtual void emitCGProfileEntry(const MCSymbol *From, const MCSymbol *To, + uint64_t Count); + /// Get the .pdata section used for the given section. Typically the given /// section is either the main .text section or some other COMDAT .text /// section, but it may be any section containing code. Index: include/llvm/Object/ELFTypes.h =================================================================== --- include/llvm/Object/ELFTypes.h +++ include/llvm/Object/ELFTypes.h @@ -43,6 +43,7 @@ template struct Elf_Nhdr_Impl; template class Elf_Note_Impl; template class Elf_Note_Iterator_Impl; +template struct Elf_CGProfile_Impl; template struct ELFType { private: @@ -72,6 +73,7 @@ using Nhdr = Elf_Nhdr_Impl>; using Note = Elf_Note_Impl>; using NoteIterator = Elf_Note_Iterator_Impl>; + using CGProfile = Elf_CGProfile_Impl>; using DynRange = ArrayRef; using ShdrRange = ArrayRef; using SymRange = ArrayRef; @@ -678,6 +680,13 @@ } }; +template struct Elf_CGProfile_Impl { + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + Elf_Word cgp_from; + Elf_Word cgp_to; + Elf_Xword cgp_weight; +}; + // MIPS .reginfo section template struct Elf_Mips_RegInfo; Index: lib/MC/ELFObjectWriter.cpp =================================================================== --- lib/MC/ELFObjectWriter.cpp +++ lib/MC/ELFObjectWriter.cpp @@ -983,6 +983,10 @@ sh_link = SymbolTableIndex; sh_info = GroupSymbolIndex; break; + + case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: + sh_link = SymbolTableIndex; + break; } if (Section.getFlags() & ELF::SHF_LINK_ORDER) { @@ -1091,6 +1095,14 @@ } } + MCSectionELF *CGProfileSection = nullptr; + if (!Asm.CGProfile.empty()) { + CGProfileSection = Ctx.getELFSection(".llvm.call-graph-profile", + ELF::SHT_LLVM_CALL_GRAPH_PROFILE, + ELF::SHF_EXCLUDE, 16, ""); + SectionIndexMap[CGProfileSection] = addToSectionTable(CGProfileSection); + } + for (MCSectionELF *Group : Groups) { align(Group->getAlignment()); @@ -1132,6 +1144,17 @@ } } + if (CGProfileSection) { + uint64_t SecStart = W.OS.tell(); + for (const MCAssembler::CGProfileEntry &CGPE : Asm.CGProfile) { + W.write(CGPE.From->getIndex()); + W.write(CGPE.To->getIndex()); + W.write(CGPE.Count); + } + uint64_t SecEnd = W.OS.tell(); + SectionOffsets[CGProfileSection] = std::make_pair(SecStart, SecEnd); + } + { uint64_t SecStart = W.OS.tell(); const MCSectionELF *Sec = createStringTable(Ctx); Index: lib/MC/MCAsmStreamer.cpp =================================================================== --- lib/MC/MCAsmStreamer.cpp +++ lib/MC/MCAsmStreamer.cpp @@ -304,6 +304,9 @@ SMLoc Loc) override; void EmitWinEHHandlerData(SMLoc Loc) override; + void emitCGProfileEntry(const MCSymbol *From, const MCSymbol *To, + uint64_t Count) override; + void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, bool PrintSchedInfo) override; @@ -1650,6 +1653,16 @@ EmitEOL(); } +void MCAsmStreamer::emitCGProfileEntry(const MCSymbol *From, const MCSymbol *To, + uint64_t Count) { + OS << "\t.cg_profile "; + From->print(OS, MAI); + OS << ", "; + To->print(OS, MAI); + OS << ", " << Count; + EmitEOL(); +} + void MCAsmStreamer::AddEncodingComment(const MCInst &Inst, const MCSubtargetInfo &STI, bool PrintSchedInfo) { Index: lib/MC/MCELFStreamer.cpp =================================================================== --- lib/MC/MCELFStreamer.cpp +++ lib/MC/MCELFStreamer.cpp @@ -355,6 +355,22 @@ ValueSize, MaxBytesToEmit); } +void MCELFStreamer::emitCGProfileEntry(const MCSymbol *From, const MCSymbol *To, + uint64_t Count) { + bool Created; + getAssembler().registerSymbol(*From, &Created); + if (Created) { + cast(From)->setBinding(ELF::STB_WEAK); + cast(From)->setExternal(true); + } + getAssembler().registerSymbol(*To, &Created); + if (Created) { + cast(To)->setBinding(ELF::STB_WEAK); + cast(To)->setExternal(true); + } + getAssembler().CGProfile.push_back({From, To, Count}); +} + void MCELFStreamer::EmitIdent(StringRef IdentString) { MCSection *Comment = getAssembler().getContext().getELFSection( ".comment", ELF::SHT_PROGBITS, ELF::SHF_MERGE | ELF::SHF_STRINGS, 1, ""); Index: lib/MC/MCParser/ELFAsmParser.cpp =================================================================== --- lib/MC/MCParser/ELFAsmParser.cpp +++ lib/MC/MCParser/ELFAsmParser.cpp @@ -85,6 +85,7 @@ addDirectiveHandler< &ELFAsmParser::ParseDirectiveSymbolAttribute>(".hidden"); addDirectiveHandler<&ELFAsmParser::ParseDirectiveSubsection>(".subsection"); + addDirectiveHandler<&ELFAsmParser::ParseDirectiveCGProfile>(".cg_profile"); } // FIXME: Part of this logic is duplicated in the MCELFStreamer. What is @@ -149,6 +150,7 @@ bool ParseDirectiveWeakref(StringRef, SMLoc); bool ParseDirectiveSymbolAttribute(StringRef, SMLoc); bool ParseDirectiveSubsection(StringRef, SMLoc); + bool ParseDirectiveCGProfile(StringRef, SMLoc); private: bool ParseSectionName(StringRef &SectionName); @@ -610,6 +612,8 @@ Type = ELF::SHT_LLVM_ODRTAB; else if (TypeName == "llvm_linker_options") Type = ELF::SHT_LLVM_LINKER_OPTIONS; + else if (TypeName == "llvm_call_graph_profile") + Type = ELF::SHT_LLVM_CALL_GRAPH_PROFILE; else if (TypeName.getAsInteger(0, Type)) return TokError("unknown section type"); } @@ -840,6 +844,40 @@ return false; } +/// ParseDirectiveCGProfile +/// ::= .cg_profile identifier, identifier, +bool ELFAsmParser::ParseDirectiveCGProfile(StringRef, SMLoc) { + StringRef From; + if (getParser().parseIdentifier(From)) + return TokError("expected identifier in directive"); + + if (getLexer().isNot(AsmToken::Comma)) + return TokError("expected a comma"); + Lex(); + + StringRef To; + if (getParser().parseIdentifier(To)) + return TokError("expected identifier in directive"); + + if (getLexer().isNot(AsmToken::Comma)) + return TokError("expected a comma"); + Lex(); + + int64_t Count; + if (getParser().parseIntToken( + Count, "expected integer count in '.cg_profile' directive")) + return true; + + if (getLexer().isNot(AsmToken::EndOfStatement)) + return TokError("unexpected token in directive"); + + MCSymbol *FromSym = getContext().getOrCreateSymbol(From); + MCSymbol *ToSym = getContext().getOrCreateSymbol(To); + + getStreamer().emitCGProfileEntry(FromSym, ToSym, Count); + return false; +} + namespace llvm { MCAsmParserExtension *createELFAsmParser() { Index: lib/MC/MCSectionELF.cpp =================================================================== --- lib/MC/MCSectionELF.cpp +++ lib/MC/MCSectionELF.cpp @@ -150,6 +150,8 @@ OS << "llvm_odrtab"; else if (Type == ELF::SHT_LLVM_LINKER_OPTIONS) OS << "llvm_linker_options"; + else if (Type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) + OS << "llvm_call_graph_profile"; else report_fatal_error("unsupported type 0x" + Twine::utohexstr(Type) + " for section " + getSectionName()); Index: lib/MC/MCStreamer.cpp =================================================================== --- lib/MC/MCStreamer.cpp +++ lib/MC/MCStreamer.cpp @@ -661,6 +661,10 @@ getContext().reportError(Loc, "Chained unwind areas can't have handlers!"); } +void MCStreamer::emitCGProfileEntry(const MCSymbol *From, const MCSymbol *To, + uint64_t Count) { +} + static MCSection *getWinCFISection(MCContext &Context, unsigned *NextWinCFIID, MCSection *MainCFISec, const MCSection *TextSec) { Index: lib/Object/ELF.cpp =================================================================== --- lib/Object/ELF.cpp +++ lib/Object/ELF.cpp @@ -206,6 +206,7 @@ STRINGIFY_ENUM_CASE(ELF, SHT_ANDROID_RELA); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_ODRTAB); STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_LINKER_OPTIONS); + STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_CALL_GRAPH_PROFILE); STRINGIFY_ENUM_CASE(ELF, SHT_GNU_ATTRIBUTES); STRINGIFY_ENUM_CASE(ELF, SHT_GNU_HASH); STRINGIFY_ENUM_CASE(ELF, SHT_GNU_verdef); Index: lib/ObjectYAML/ELFYAML.cpp =================================================================== --- lib/ObjectYAML/ELFYAML.cpp +++ lib/ObjectYAML/ELFYAML.cpp @@ -441,6 +441,7 @@ ECase(SHT_ANDROID_RELA); ECase(SHT_LLVM_ODRTAB); ECase(SHT_LLVM_LINKER_OPTIONS); + ECase(SHT_LLVM_CALL_GRAPH_PROFILE); ECase(SHT_GNU_ATTRIBUTES); ECase(SHT_GNU_HASH); ECase(SHT_GNU_verdef); Index: test/MC/AsmParser/directive_cgprofile.s =================================================================== --- /dev/null +++ test/MC/AsmParser/directive_cgprofile.s @@ -0,0 +1,9 @@ +# RUN: llvm-mc -triple i386-unknown-unknown %s | FileCheck %s + + .cg_profile a, b, 32 + .cg_profile freq, a, 11 + .cg_profile freq, b, 20 + +# CHECK: .cg_profile a, b, 32 +# CHECK: .cg_profile freq, a, 11 +# CHECK: .cg_profile freq, b, 20 Index: test/MC/ELF/cgprofile.s =================================================================== --- /dev/null +++ test/MC/ELF/cgprofile.s @@ -0,0 +1,78 @@ +# RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -t -sd | FileCheck %s + + .section .test,"aw",@progbits +a: .word b + + .cg_profile a, b, 32 + .cg_profile freq, a, 11 + .cg_profile late, late2, 20 + .cg_profile .L.local, b, 42 + + .globl late +late: +late2: +late3: +.L.local: + +# CHECK: Name: .llvm.call-graph-profile +# CHECK-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE (0x6FFF4C02) +# CHECK-NEXT: Flags [ (0x80000000) +# CHECK-NEXT: SHF_EXCLUDE (0x80000000) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: 64 +# CHECK-NEXT: Link: 6 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 1 +# CHECK-NEXT: EntrySize: 16 +# CHECK-NEXT: SectionData ( +# CHECK-NEXT: 0000: 01000000 03000000 20000000 00000000 +# CHECK-NEXT: 0010: 04000000 01000000 0B000000 00000000 +# CHECK-NEXT: 0020: 05000000 06000000 14000000 00000000 +# CHECK-NEXT: 0030: 00000000 03000000 2A000000 00000000 +# CHECK-NEXT: ) + +# CHECK: Symbols [ +# CHECK: Name: a (69) +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .test +# CHECK: Name: late3 (71) +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .test +# CHECK: Name: b +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: Undefined +# CHECK: Name: freq +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Weak +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: Undefined +# CHECK: Name: late +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .test +# CHECK: Name: late2 (77) +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Weak +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .test Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -100,6 +100,7 @@ using Elf_Vernaux = typename ELFT::Vernaux; \ using Elf_Verdef = typename ELFT::Verdef; \ using Elf_Verdaux = typename ELFT::Verdaux; \ + using Elf_CGProfile = typename ELFT::CGProfile; \ using uintX_t = typename ELFT::uint; namespace { @@ -164,6 +165,8 @@ void printHashHistogram() override; + void printCGProfile() override; + void printNotes() override; void printELFLinkerOptions() override; @@ -210,6 +213,7 @@ const Elf_Hash *HashTable = nullptr; const Elf_GnuHash *GnuHashTable = nullptr; const Elf_Shdr *DotSymtabSec = nullptr; + const Elf_Shdr *DotCGProfileSec = nullptr; StringRef DynSymtabName; ArrayRef ShndxTable; @@ -257,9 +261,11 @@ void getSectionNameIndex(const Elf_Sym *Symbol, const Elf_Sym *FirstSym, StringRef &SectionName, unsigned &SectionIndex) const; + StringRef getStaticSymbolName(uint32_t Index) const; void printSymbolsHelper(bool IsDynamic) const; const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; } + const Elf_Shdr *getDotCGProfileSec() const { return DotCGProfileSec; } ArrayRef getShndxTable() const { return ShndxTable; } StringRef getDynamicStringTable() const { return DynamicStringTable; } const DynRegionInfo &getDynRelRegion() const { return DynRelRegion; } @@ -319,6 +325,7 @@ bool IsDynamic) = 0; virtual void printProgramHeaders(const ELFFile *Obj) = 0; virtual void printHashHistogram(const ELFFile *Obj) = 0; + virtual void printCGProfile(const ELFFile *Obj) = 0; virtual void printNotes(const ELFFile *Obj) = 0; virtual void printELFLinkerOptions(const ELFFile *Obj) = 0; virtual void printMipsGOT(const MipsGOTParser &Parser) = 0; @@ -349,6 +356,7 @@ size_t Offset) override; void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile *Obj) override; + void printCGProfile(const ELFFile *Obj) override; void printNotes(const ELFFile *Obj) override; void printELFLinkerOptions(const ELFFile *Obj) override; void printMipsGOT(const MipsGOTParser &Parser) override; @@ -410,6 +418,7 @@ void printDynamicRelocations(const ELFO *Obj) override; void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile *Obj) override; + void printCGProfile(const ELFFile *Obj) override; void printNotes(const ELFFile *Obj) override; void printELFLinkerOptions(const ELFFile *Obj) override; void printMipsGOT(const MipsGOTParser &Parser) override; @@ -736,6 +745,16 @@ return StringRef(StrTab.data() + name_offset); } +template +StringRef ELFDumper::getStaticSymbolName(uint32_t Index) const { + StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec)); + Elf_Sym_Range Syms = unwrapOrError(Obj->symbols(DotSymtabSec)); + if (Index >= Syms.size()) + reportError("Invalid symbol index"); + const Elf_Sym *Sym = &Syms[Index]; + return unwrapOrError(Sym->getName(StrTable)); +} + template std::string ELFDumper::getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable, @@ -1390,6 +1409,10 @@ reportError("Multiple SHT_GNU_verneed"); dot_gnu_version_r_sec = &Sec; break; + case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: + if (DotCGProfileSec != nullptr) + reportError("Multiple .note.llvm.cgprofile"); + DotCGProfileSec = &Sec; } } @@ -1534,6 +1557,10 @@ ELFDumperStyle->printHashHistogram(Obj); } +template void ELFDumper::printCGProfile() { + ELFDumperStyle->printCGProfile(Obj); +} + template void ELFDumper::printNotes() { ELFDumperStyle->printNotes(Obj); } @@ -2721,6 +2748,8 @@ return "LLVM_ODRTAB"; case SHT_LLVM_LINKER_OPTIONS: return "LLVM_LINKER_OPTIONS"; + case SHT_LLVM_CALL_GRAPH_PROFILE: + return "LLVM_CALL_GRAPH_PROFILE"; // FIXME: Parse processor specific GNU attributes case SHT_GNU_ATTRIBUTES: return "ATTRIBUTES"; @@ -3374,6 +3403,11 @@ } } +template +void GNUStyle::printCGProfile(const ELFFile *Obj) { + OS << "GNUStyle::printCGProfile not implemented\n"; +} + static std::string getGNUNoteTypeName(const uint32_t NT) { static const struct { uint32_t ID; @@ -4167,6 +4201,24 @@ W.startLine() << "Hash Histogram not implemented!\n"; } +template +void LLVMStyle::printCGProfile(const ELFFile *Obj) { + ListScope L(W, "CGProfile"); + if (!this->dumper()->getDotCGProfileSec()) + return; + auto CGProfile = + unwrapOrError(Obj->template getSectionContentsAsArray( + this->dumper()->getDotCGProfileSec())); + for (const Elf_CGProfile &CGPE : CGProfile) { + DictScope D(W, "CGProfileEntry"); + W.printNumber("From", this->dumper()->getStaticSymbolName(CGPE.cgp_from), + CGPE.cgp_from); + W.printNumber("To", this->dumper()->getStaticSymbolName(CGPE.cgp_to), + CGPE.cgp_to); + W.printNumber("Weight", CGPE.cgp_weight); + } +} + template void LLVMStyle::printNotes(const ELFFile *Obj) { W.startLine() << "printNotes not implemented!\n"; Index: tools/llvm-readobj/ObjDumper.h =================================================================== --- tools/llvm-readobj/ObjDumper.h +++ tools/llvm-readobj/ObjDumper.h @@ -47,6 +47,7 @@ virtual void printVersionInfo() {} virtual void printGroupSections() {} virtual void printHashHistogram() {} + virtual void printCGProfile() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} Index: tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- tools/llvm-readobj/llvm-readobj.cpp +++ tools/llvm-readobj/llvm-readobj.cpp @@ -284,6 +284,8 @@ cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"), cl::aliasopt(HashHistogram)); + cl::opt CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section")); + cl::opt Output("elf-output-style", cl::desc("Specify ELF dump style"), cl::values(clEnumVal(LLVM, "LLVM default style"), @@ -441,6 +443,8 @@ Dumper->printGroupSections(); if (opts::HashHistogram) Dumper->printHashHistogram(); + if (opts::CGProfile) + Dumper->printCGProfile(); if (opts::Notes) Dumper->printNotes(); }