diff options
-rw-r--r-- | llvm/docs/Extensions.rst | 44 | ||||
-rw-r--r-- | llvm/include/llvm/BinaryFormat/ELF.h | 1 | ||||
-rw-r--r-- | llvm/include/llvm/MC/MCAssembler.h | 7 | ||||
-rw-r--r-- | llvm/include/llvm/MC/MCELFStreamer.h | 5 | ||||
-rw-r--r-- | llvm/include/llvm/MC/MCStreamer.h | 3 | ||||
-rw-r--r-- | llvm/include/llvm/Object/ELFTypes.h | 9 | ||||
-rw-r--r-- | llvm/lib/MC/ELFObjectWriter.cpp | 20 | ||||
-rw-r--r-- | llvm/lib/MC/MCAsmStreamer.cpp | 14 | ||||
-rw-r--r-- | llvm/lib/MC/MCELFStreamer.cpp | 38 | ||||
-rw-r--r-- | llvm/lib/MC/MCParser/ELFAsmParser.cpp | 45 | ||||
-rw-r--r-- | llvm/lib/MC/MCSectionELF.cpp | 2 | ||||
-rw-r--r-- | llvm/lib/MC/MCStreamer.cpp | 4 | ||||
-rw-r--r-- | llvm/lib/Object/ELF.cpp | 1 | ||||
-rw-r--r-- | llvm/lib/ObjectYAML/ELFYAML.cpp | 1 | ||||
-rw-r--r-- | llvm/test/MC/AsmParser/directive_cgprofile.s | 9 | ||||
-rw-r--r-- | llvm/test/MC/ELF/cgprofile-error.s | 7 | ||||
-rw-r--r-- | llvm/test/MC/ELF/cgprofile.s | 100 | ||||
-rw-r--r-- | llvm/tools/llvm-readobj/ELFDumper.cpp | 52 | ||||
-rw-r--r-- | llvm/tools/llvm-readobj/ObjDumper.h | 1 | ||||
-rw-r--r-- | llvm/tools/llvm-readobj/llvm-readobj.cpp | 4 |
20 files changed, 367 insertions, 0 deletions
diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst index fd67906a9ac..6f99006adc9 100644 --- a/llvm/docs/Extensions.rst +++ b/llvm/docs/Extensions.rst @@ -285,6 +285,50 @@ The following directives are specified: 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 + +``.cg_profile`` directives are processed at the end of the file. It is an error +if either ``from`` or ``to`` are undefined temporary symbols. If either symbol +is a temporary symbol, then the section symbol is used instead. If either +symbol is undefined, then that symbol is defined as if ``.weak symbol`` has been +written at the end of the file. This forces the symbol to show up in the symbol +table. + Target Specific Behaviour ========================= diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index c48672126c4..c4ec63cb431 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -803,6 +803,7 @@ enum : unsigned { 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. diff --git a/llvm/include/llvm/MC/MCAssembler.h b/llvm/include/llvm/MC/MCAssembler.h index b4bb81f9f54..34bf180be1f 100644 --- a/llvm/include/llvm/MC/MCAssembler.h +++ b/llvm/include/llvm/MC/MCAssembler.h @@ -418,6 +418,13 @@ public: const MCLOHContainer &getLOHContainer() const { return const_cast<MCAssembler *>(this)->getLOHContainer(); } + + struct CGProfileEntry { + const MCSymbolRefExpr *From; + const MCSymbolRefExpr *To; + uint64_t Count; + }; + std::vector<CGProfileEntry> CGProfile; /// @} /// \name Backend Data Access /// @{ diff --git a/llvm/include/llvm/MC/MCELFStreamer.h b/llvm/include/llvm/MC/MCELFStreamer.h index 0b7529934c5..b1b083f8fef 100644 --- a/llvm/include/llvm/MC/MCELFStreamer.h +++ b/llvm/include/llvm/MC/MCELFStreamer.h @@ -69,6 +69,9 @@ public: void EmitValueToAlignment(unsigned, int64_t, unsigned, unsigned) override; + void emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, uint64_t Count) override; + void FinishImpl() override; void EmitBundleAlignMode(unsigned AlignPow2) override; @@ -81,6 +84,8 @@ private: void EmitInstToData(const MCInst &Inst, const MCSubtargetInfo &) override; void fixSymbolsInTLSFixups(const MCExpr *expr); + void finalizeCGProfileEntry(const MCSymbolRefExpr *&S); + void finalizeCGProfile(); /// Merge the content of the fragment \p EF into the fragment \p DF. void mergeFragment(MCDataFragment *, MCDataFragment *); diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 03009a2dabc..db4f885a7d9 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -901,6 +901,9 @@ public: SMLoc Loc = SMLoc()); virtual void EmitWinEHHandlerData(SMLoc Loc = SMLoc()); + virtual void emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *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. diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h index 521421af32a..78a1b9848cb 100644 --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -43,6 +43,7 @@ template <class ELFT> struct Elf_Chdr_Impl; template <class ELFT> struct Elf_Nhdr_Impl; template <class ELFT> class Elf_Note_Impl; template <class ELFT> class Elf_Note_Iterator_Impl; +template <class ELFT> struct Elf_CGProfile_Impl; template <endianness E, bool Is64> struct ELFType { private: @@ -72,6 +73,7 @@ public: using Nhdr = Elf_Nhdr_Impl<ELFType<E, Is64>>; using Note = Elf_Note_Impl<ELFType<E, Is64>>; using NoteIterator = Elf_Note_Iterator_Impl<ELFType<E, Is64>>; + using CGProfile = Elf_CGProfile_Impl<ELFType<E, Is64>>; using DynRange = ArrayRef<Dyn>; using ShdrRange = ArrayRef<Shdr>; using SymRange = ArrayRef<Sym>; @@ -678,6 +680,13 @@ public: } }; +template <class ELFT> 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 <class ELFT> struct Elf_Mips_RegInfo; diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp index 14300642303..f4117e75516 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -976,6 +976,7 @@ void ELFWriter::writeSection(const SectionIndexMapTy &SectionIndexMap, break; case ELF::SHT_SYMTAB_SHNDX: + case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: sh_link = SymbolTableIndex; break; @@ -1091,6 +1092,14 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { } } + 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 +1141,17 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { } } + if (CGProfileSection) { + uint64_t SecStart = W.OS.tell(); + for (const MCAssembler::CGProfileEntry &CGPE : Asm.CGProfile) { + W.write<uint32_t>(CGPE.From->getSymbol().getIndex()); + W.write<uint32_t>(CGPE.To->getSymbol().getIndex()); + W.write<uint64_t>(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); diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index d19c8c21dd0..5c7bbeafec2 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -304,6 +304,9 @@ public: SMLoc Loc) override; void EmitWinEHHandlerData(SMLoc Loc) override; + void emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, uint64_t Count) override; + void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, bool PrintSchedInfo) override; @@ -1650,6 +1653,17 @@ void MCAsmStreamer::EmitWinCFIEndProlog(SMLoc Loc) { EmitEOL(); } +void MCAsmStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, + uint64_t Count) { + OS << "\t.cg_profile "; + From->getSymbol().print(OS, MAI); + OS << ", "; + To->getSymbol().print(OS, MAI); + OS << ", " << Count; + EmitEOL(); +} + void MCAsmStreamer::AddEncodingComment(const MCInst &Inst, const MCSubtargetInfo &STI, bool PrintSchedInfo) { diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp index 3a008f9fea0..b112bd9639d 100644 --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -355,6 +355,12 @@ void MCELFStreamer::EmitValueToAlignment(unsigned ByteAlignment, ValueSize, MaxBytesToEmit); } +void MCELFStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, + uint64_t Count) { + 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, ""); @@ -447,6 +453,37 @@ void MCELFStreamer::fixSymbolsInTLSFixups(const MCExpr *expr) { } } +void MCELFStreamer::finalizeCGProfileEntry(const MCSymbolRefExpr *&SRE) { + const MCSymbol *S = &SRE->getSymbol(); + if (S->isTemporary()) { + if (!S->isInSection()) { + getContext().reportError( + SRE->getLoc(), Twine("Reference to undefined temporary symbol ") + + "`" + S->getName() + "`"); + return; + } + S = S->getSection().getBeginSymbol(); + S->setUsedInReloc(); + SRE = + MCSymbolRefExpr::create(S, SRE->getKind(), getContext(), SRE->getLoc()); + return; + } + // Not a temporary, referece it as a weak undefined. + bool Created; + getAssembler().registerSymbol(*S, &Created); + if (Created) { + cast<MCSymbolELF>(S)->setBinding(ELF::STB_WEAK); + cast<MCSymbolELF>(S)->setExternal(true); + } +} + +void MCELFStreamer::finalizeCGProfile() { + for (MCAssembler::CGProfileEntry &E : getAssembler().CGProfile) { + finalizeCGProfileEntry(E.From); + finalizeCGProfileEntry(E.To); + } +} + void MCELFStreamer::EmitInstToFragment(const MCInst &Inst, const MCSubtargetInfo &STI) { this->MCObjectStreamer::EmitInstToFragment(Inst, STI); @@ -612,6 +649,7 @@ void MCELFStreamer::FinishImpl() { MCSection *CurSection = getCurrentSectionOnly(); setSectionAlignmentForBundling(getAssembler(), CurSection); + finalizeCGProfile(); EmitFrames(nullptr); this->MCObjectStreamer::FinishImpl(); diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp index cb0616db39e..5194e8a29b8 100644 --- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp +++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp @@ -85,6 +85,7 @@ public: 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 @@ public: 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 @@ EndStmt: 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,47 @@ bool ELFAsmParser::ParseDirectiveSubsection(StringRef, SMLoc) { return false; } +/// ParseDirectiveCGProfile +/// ::= .cg_profile identifier, identifier, <number> +bool ELFAsmParser::ParseDirectiveCGProfile(StringRef, SMLoc) { + StringRef From; + SMLoc FromLoc = getLexer().getLoc(); + if (getParser().parseIdentifier(From)) + return TokError("expected identifier in directive"); + + if (getLexer().isNot(AsmToken::Comma)) + return TokError("expected a comma"); + Lex(); + + StringRef To; + SMLoc ToLoc = getLexer().getLoc(); + 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( + MCSymbolRefExpr::create(FromSym, MCSymbolRefExpr::VK_None, getContext(), + FromLoc), + MCSymbolRefExpr::create(ToSym, MCSymbolRefExpr::VK_None, getContext(), + ToLoc), + Count); + return false; +} + namespace llvm { MCAsmParserExtension *createELFAsmParser() { diff --git a/llvm/lib/MC/MCSectionELF.cpp b/llvm/lib/MC/MCSectionELF.cpp index 36cdb421655..4d77d05cc50 100644 --- a/llvm/lib/MC/MCSectionELF.cpp +++ b/llvm/lib/MC/MCSectionELF.cpp @@ -150,6 +150,8 @@ void MCSectionELF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, 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()); diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index 89d418d6743..491fec3e9ab 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -661,6 +661,10 @@ void MCStreamer::EmitWinEHHandlerData(SMLoc Loc) { getContext().reportError(Loc, "Chained unwind areas can't have handlers!"); } +void MCStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, uint64_t Count) { +} + static MCSection *getWinCFISection(MCContext &Context, unsigned *NextWinCFIID, MCSection *MainCFISec, const MCSection *TextSec) { diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp index 92a64f48924..965c38a8049 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -206,6 +206,7 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) { 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); diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp index 53664906847..455ecfb64ab 100644 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -441,6 +441,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHT>::enumeration( 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); diff --git a/llvm/test/MC/AsmParser/directive_cgprofile.s b/llvm/test/MC/AsmParser/directive_cgprofile.s new file mode 100644 index 00000000000..1db93dcbb03 --- /dev/null +++ b/llvm/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 diff --git a/llvm/test/MC/ELF/cgprofile-error.s b/llvm/test/MC/ELF/cgprofile-error.s new file mode 100644 index 00000000000..1cda8104fe5 --- /dev/null +++ b/llvm/test/MC/ELF/cgprofile-error.s @@ -0,0 +1,7 @@ +# RUN: not llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o /dev/null 2>&1 | FileCheck %s + + .cg_profile a, .L.temp, 32 + +# CHECK: cgprofile-error.s:3:18: error: Reference to undefined temporary symbol `.L.temp` +# CHECK-NEXT: .cg_profile a, .L.temp, 32 +# CHECK-NEXT: ^ diff --git a/llvm/test/MC/ELF/cgprofile.s b/llvm/test/MC/ELF/cgprofile.s new file mode 100644 index 00000000000..5b5b7fac855 --- /dev/null +++ b/llvm/test/MC/ELF/cgprofile.s @@ -0,0 +1,100 @@ +# RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -s -t -sd -elf-cg-profile | 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: .word 0 +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 05000000 20000000 00000000 +# CHECK-NEXT: 0010: 06000000 01000000 0B000000 00000000 +# CHECK-NEXT: 0020: 07000000 02000000 14000000 00000000 +# CHECK-NEXT: 0030: 04000000 05000000 2A000000 00000000 +# CHECK-NEXT: ) + +# CHECK: Symbols [ +# CHECK: Name: a +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .test +# CHECK: Name: late2 +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .test +# CHECK: Name: late3 +# 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: CGProfile [ +# CHECK-NEXT: CGProfileEntry { +# CHECK-NEXT: From: a +# CHECK-NEXT: To: b +# CHECK-NEXT: Weight: 32 +# CHECK-NEXT: } +# CHECK-NEXT: CGProfileEntry { +# CHECK-NEXT: From: freq +# CHECK-NEXT: To: a +# CHECK-NEXT: Weight: 11 +# CHECK-NEXT: } +# CHECK-NEXT: CGProfileEntry { +# CHECK-NEXT: From: late +# CHECK-NEXT: To: late2 +# CHECK-NEXT: Weight: 20 +# CHECK-NEXT: } +# CHECK-NEXT: CGProfileEntry { +# CHECK-NEXT: From: +# CHECK-NEXT: To: b +# CHECK-NEXT: Weight: 42 +# CHECK-NEXT: } +# CHECK-NEXT: ]
\ No newline at end of file diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 6ca28e273cc..993d4aaa81e 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -100,6 +100,7 @@ using namespace ELF; 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 @@ public: void printHashHistogram() override; + void printCGProfile() override; + void printNotes() override; void printELFLinkerOptions() override; @@ -210,6 +213,7 @@ private: const Elf_Hash *HashTable = nullptr; const Elf_GnuHash *GnuHashTable = nullptr; const Elf_Shdr *DotSymtabSec = nullptr; + const Elf_Shdr *DotCGProfileSec = nullptr; StringRef DynSymtabName; ArrayRef<Elf_Word> ShndxTable; @@ -257,9 +261,11 @@ public: 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<Elf_Word> getShndxTable() const { return ShndxTable; } StringRef getDynamicStringTable() const { return DynamicStringTable; } const DynRegionInfo &getDynRelRegion() const { return DynRelRegion; } @@ -319,6 +325,7 @@ public: bool IsDynamic) = 0; virtual void printProgramHeaders(const ELFFile<ELFT> *Obj) = 0; virtual void printHashHistogram(const ELFFile<ELFT> *Obj) = 0; + virtual void printCGProfile(const ELFFile<ELFT> *Obj) = 0; virtual void printNotes(const ELFFile<ELFT> *Obj) = 0; virtual void printELFLinkerOptions(const ELFFile<ELFT> *Obj) = 0; virtual void printMipsGOT(const MipsGOTParser<ELFT> &Parser) = 0; @@ -349,6 +356,7 @@ public: size_t Offset) override; void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile<ELFT> *Obj) override; + void printCGProfile(const ELFFile<ELFT> *Obj) override; void printNotes(const ELFFile<ELFT> *Obj) override; void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override; void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; @@ -410,6 +418,7 @@ public: void printDynamicRelocations(const ELFO *Obj) override; void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile<ELFT> *Obj) override; + void printCGProfile(const ELFFile<ELFT> *Obj) override; void printNotes(const ELFFile<ELFT> *Obj) override; void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override; void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; @@ -737,6 +746,16 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab, } template <typename ELFT> +StringRef ELFDumper<ELFT>::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 <typename ELFT> std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable, bool IsDynamic) const { @@ -1390,6 +1409,10 @@ ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &Writer) 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 @@ template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() { ELFDumperStyle->printHashHistogram(Obj); } +template <class ELFT> void ELFDumper<ELFT>::printCGProfile() { + ELFDumperStyle->printCGProfile(Obj); +} + template <class ELFT> void ELFDumper<ELFT>::printNotes() { ELFDumperStyle->printNotes(Obj); } @@ -2721,6 +2748,8 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { 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 @@ void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { } } +template <class ELFT> +void GNUStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) { + OS << "GNUStyle::printCGProfile not implemented\n"; +} + static std::string getGNUNoteTypeName(const uint32_t NT) { static const struct { uint32_t ID; @@ -4138,6 +4172,24 @@ void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { } template <class ELFT> +void LLVMStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) { + ListScope L(W, "CGProfile"); + if (!this->dumper()->getDotCGProfileSec()) + return; + auto CGProfile = + unwrapOrError(Obj->template getSectionContentsAsArray<Elf_CGProfile>( + 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 <class ELFT> void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { W.startLine() << "printNotes not implemented!\n"; } diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index 2bf4dfe5a40..e13646588e7 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -47,6 +47,7 @@ public: virtual void printVersionInfo() {} virtual void printGroupSections() {} virtual void printHashHistogram() {} + virtual void printCGProfile() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index 4b8a1b32471..f871dc90950 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -284,6 +284,8 @@ namespace opts { cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"), cl::aliasopt(HashHistogram)); + cl::opt<bool> CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section")); + cl::opt<OutputStyleTy> Output("elf-output-style", cl::desc("Specify ELF dump style"), cl::values(clEnumVal(LLVM, "LLVM default style"), @@ -441,6 +443,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) { Dumper->printGroupSections(); if (opts::HashHistogram) Dumper->printHashHistogram(); + if (opts::CGProfile) + Dumper->printCGProfile(); if (opts::Notes) Dumper->printNotes(); } |