diff --git a/llvm/include/llvm/MC/MCWinCOFFStreamer.h b/llvm/include/llvm/MC/MCWinCOFFStreamer.h --- a/llvm/include/llvm/MC/MCWinCOFFStreamer.h +++ b/llvm/include/llvm/MC/MCWinCOFFStreamer.h @@ -64,6 +64,8 @@ unsigned ByteAlignment) override; void emitIdent(StringRef IdentString) override; void EmitWinEHHandlerData(SMLoc Loc) override; + void emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, uint64_t Count) override; void finishImpl() override; /// \} @@ -73,6 +75,9 @@ void emitInstToData(const MCInst &Inst, const MCSubtargetInfo &STI) override; + void finalizeCGProfileEntry(const MCSymbolRefExpr *&S); + void finalizeCGProfile(); + private: void Error(const Twine &Msg) const; }; diff --git a/llvm/lib/MC/MCParser/COFFAsmParser.cpp b/llvm/lib/MC/MCParser/COFFAsmParser.cpp --- a/llvm/lib/MC/MCParser/COFFAsmParser.cpp +++ b/llvm/lib/MC/MCParser/COFFAsmParser.cpp @@ -70,6 +70,7 @@ addDirectiveHandler<&COFFAsmParser::ParseDirectiveLinkOnce>(".linkonce"); addDirectiveHandler<&COFFAsmParser::ParseDirectiveRVA>(".rva"); addDirectiveHandler<&COFFAsmParser::ParseDirectiveSymbolAttribute>(".weak"); + addDirectiveHandler<&COFFAsmParser::ParseDirectiveCGProfile>(".cg_profile"); // Win64 EH directives. addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveStartProc>( @@ -125,6 +126,7 @@ bool parseCOMDATType(COFF::COMDATType &Type); bool ParseDirectiveLinkOnce(StringRef, SMLoc); bool ParseDirectiveRVA(StringRef, SMLoc); + bool ParseDirectiveCGProfile(StringRef, SMLoc); // Win64 EH directives. bool ParseSEHDirectiveStartProc(StringRef, SMLoc); @@ -299,6 +301,49 @@ return false; } +/// ParseDirectiveCGProfile +/// ::= .cg_profile identifier, identifier, +bool COFFAsmParser::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); + + // Only store non-temporary symbols. + if (!FromSym->isTemporary() && !ToSym->isTemporary()) + getStreamer().emitCGProfileEntry( + MCSymbolRefExpr::create(FromSym, MCSymbolRefExpr::VK_None, getContext(), + FromLoc), + MCSymbolRefExpr::create(ToSym, MCSymbolRefExpr::VK_None, getContext(), + ToLoc), + Count); + return false; +} + bool COFFAsmParser::ParseSectionSwitch(StringRef Section, unsigned Characteristics, SectionKind Kind) { diff --git a/llvm/lib/MC/MCWinCOFFStreamer.cpp b/llvm/lib/MC/MCWinCOFFStreamer.cpp --- a/llvm/lib/MC/MCWinCOFFStreamer.cpp +++ b/llvm/lib/MC/MCWinCOFFStreamer.cpp @@ -328,7 +328,32 @@ llvm_unreachable("not implemented"); } +void MCWinCOFFStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, + uint64_t Count) { + getAssembler().CGProfile.push_back({From, To, Count}); +} + +void MCWinCOFFStreamer::finalizeCGProfileEntry(const MCSymbolRefExpr *&SRE) { + const MCSymbol *S = &SRE->getSymbol(); + bool Created; + getAssembler().registerSymbol(*S, &Created); + if (Created) { + cast(S)->setIsWeakExternal(); + cast(S)->setExternal(true); + } +} + +void MCWinCOFFStreamer::finalizeCGProfile() { + for (MCAssembler::CGProfileEntry &E : getAssembler().CGProfile) { + finalizeCGProfileEntry(E.From); + finalizeCGProfileEntry(E.To); + } +} + void MCWinCOFFStreamer::finishImpl() { + finalizeCGProfile(); + MCObjectStreamer::finishImpl(); } diff --git a/llvm/lib/MC/WinCOFFObjectWriter.cpp b/llvm/lib/MC/WinCOFFObjectWriter.cpp --- a/llvm/lib/MC/WinCOFFObjectWriter.cpp +++ b/llvm/lib/MC/WinCOFFObjectWriter.cpp @@ -154,6 +154,8 @@ MCSectionCOFF *AddrsigSection; std::vector AddrsigSyms; + MCSectionCOFF *CGProfileSection = nullptr; + WinCOFFObjectWriter(std::unique_ptr MOTW, raw_pwrite_stream &OS); @@ -674,6 +676,13 @@ Asm.registerSection(*AddrsigSection); } + if (!Asm.CGProfile.empty()) { + CGProfileSection = Asm.getContext().getCOFFSection( + ".llvm.call-graph-profile", COFF::IMAGE_SCN_LNK_REMOVE, + SectionKind::getMetadata()); + Asm.registerSection(*CGProfileSection); + } + // "Define" each section & symbol. This creates section & symbol // entries in the staging area. for (const auto &Section : Asm) @@ -1099,6 +1108,18 @@ } } + // Create the contents of the .llvm.call-graph-profile section. + if (CGProfileSection) { + auto *Frag = new MCDataFragment(CGProfileSection); + Frag->setLayoutOrder(0); + raw_svector_ostream OS(Frag->getContents()); + for (const MCAssembler::CGProfileEntry &CGPE : Asm.CGProfile) { + encodeULEB128(CGPE.From->getSymbol().getIndex(), OS); + encodeULEB128(CGPE.To->getSymbol().getIndex(), OS); + encodeULEB128(CGPE.Count, OS); + } + } + assignFileOffsets(Asm, Layout); // MS LINK expects to be able to use this timestamp to implement their diff --git a/llvm/test/MC/AsmParser/directive_cgprofile.s b/llvm/test/MC/AsmParser/directive_cgprofile.s --- a/llvm/test/MC/AsmParser/directive_cgprofile.s +++ b/llvm/test/MC/AsmParser/directive_cgprofile.s @@ -1,4 +1,5 @@ # RUN: llvm-mc -triple i386-unknown-unknown %s | FileCheck %s +# RUN: llvm-mc -triple x86_64-pc-win32 %s | FileCheck %s -check-prefix=COFF-CHECK .cg_profile a, b, 32 .cg_profile freq, a, 11 @@ -7,3 +8,7 @@ # CHECK: .cg_profile a, b, 32 # CHECK: .cg_profile freq, a, 11 # CHECK: .cg_profile freq, b, 20 + +# COFF-CHECK: .cg_profile a, b, 32 +# COFF-CHECK: .cg_profile freq, a, 11 +# COFF-CHECK: .cg_profile freq, b, 20 diff --git a/llvm/test/MC/COFF/cgprofile.s b/llvm/test/MC/COFF/cgprofile.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/COFF/cgprofile.s @@ -0,0 +1,116 @@ +# RUN: llvm-mc -filetype=obj -triple x86_64-pc-win32 %s -o - | llvm-readobj -S --symbols --sd --coff-cg-profile | FileCheck %s + + .section .test,"w" +a: + + .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: VirtualSize: +# CHECK-NEXT: VirtualAddress: +# CHECK-NEXT: RawDataSize: 9 +# CHECK-NEXT: PointerToRawData: +# CHECK-NEXT: PointerToRelocations: +# CHECK-NEXT: PointerToLineNumbers: +# CHECK-NEXT: RelocationCount: +# CHECK-NEXT: LineNumberCount: +# CHECK-NEXT: Characteristics [ (0x100800) +# CHECK-NEXT: IMAGE_SCN_ALIGN_1BYTES (0x100000) +# CHECK-NEXT: IMAGE_SCN_LNK_REMOVE (0x800) +# CHECK-NEXT: ] +# CHECK-NEXT: SectionData ( +# CHECK-NEXT: 0000: 0A0E2011 0A0B0B0C 14 +# CHECK-NEXT: ) + +# CHECK: Symbols [ +# CHECK: Name: a +# CHECK-NEXT: Value: +# CHECK-NEXT: Section: .test +# CHECK-NEXT: BaseType: +# CHECK-NEXT: ComplexType: +# CHECK-NEXT: StorageClass: Static +# CHECK-NEXT: AuxSymbolCount: +# CHECK: Name: late +# CHECK-NEXT: Value: +# CHECK-NEXT: Section: .test +# CHECK-NEXT: BaseType: +# CHECK-NEXT: ComplexType: +# CHECK-NEXT: StorageClass: External +# CHECK-NEXT: AuxSymbolCount: +# CHECK: Name: late2 +# CHECK-NEXT: Value: +# CHECK-NEXT: Section: .test +# CHECK-NEXT: BaseType: +# CHECK-NEXT: ComplexType: +# CHECK-NEXT: StorageClass: Static +# CHECK-NEXT: AuxSymbolCount: +# CHECK: Name: late3 +# CHECK-NEXT: Value: +# CHECK-NEXT: Section: .test +# CHECK-NEXT: BaseType: +# CHECK-NEXT: ComplexType: +# CHECK-NEXT: StorageClass: Static +# CHECK-NEXT: AuxSymbolCount: +# CHECK: Name: b +# CHECK-NEXT: Value: +# CHECK-NEXT: Section: IMAGE_SYM_UNDEFINED +# CHECK-NEXT: BaseType: +# CHECK-NEXT: ComplexType: +# CHECK-NEXT: StorageClass: WeakExternal +# CHECK-NEXT: AuxSymbolCount: 1 +# CHECK-NEXT: AuxWeakExternal { +# CHECK-NEXT: Linked: .weak.b.default.late +# CHECK-NEXT: Search: Alias +# CHECK-NEXT: } +# CHECK: Name: .weak.b.default.late +# CHECK-NEXT: Value: +# CHECK-NEXT: Section: IMAGE_SYM_ABSOLUTE +# CHECK-NEXT: BaseType: +# CHECK-NEXT: ComplexType: +# CHECK-NEXT: StorageClass: External +# CHECK-NEXT: AuxSymbolCount: 0 +# CHECK: Name: freq +# CHECK-NEXT: Value: +# CHECK-NEXT: Section: IMAGE_SYM_UNDEFINED +# CHECK-NEXT: BaseType: +# CHECK-NEXT: ComplexType: +# CHECK-NEXT: StorageClass: WeakExternal +# CHECK-NEXT: AuxSymbolCount: 1 +# CHECK-NEXT: AuxWeakExternal { +# CHECK-NEXT: Linked: .weak.freq.default.late +# CHECK-NEXT: Search: Alias +# CHECK-NEXT: } +# CHECK: Name: .weak.freq.default.late +# CHECK-NEXT: Value: +# CHECK-NEXT: Section: IMAGE_SYM_ABSOLUTE +# CHECK-NEXT: BaseType: +# CHECK-NEXT: ComplexType: +# CHECK-NEXT: StorageClass: External +# CHECK-NEXT: AuxSymbolCount: 0 + +# 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: ] \ No newline at end of file diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -104,8 +104,11 @@ bool GHash) override; void printStackMap() const override; void printAddrsig() override; + void printCGProfile() override; private: + unsigned decodeSymbol(const uint8_t *Cur, const uint8_t *End, + uint64_t &SymIndex, StringRef &SymName); void printSymbols() override; void printDynamicSymbols() override; void printSymbol(const SymbolRef &Sym); @@ -1961,23 +1964,75 @@ const uint8_t *Cur = AddrsigContents.bytes_begin(); const uint8_t *End = AddrsigContents.bytes_end(); while (Cur != End) { + uint64_t SymIndex; + StringRef SymName; + Cur += decodeSymbol(Cur, End, SymIndex, SymName); + W.printNumber("Sym", SymName, SymIndex); + } +} + +void COFFDumper::printCGProfile() { + object::SectionRef CGProfileSection; + for (auto Sec : Obj->sections()) { + StringRef Name; + if (Expected NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + if (Name == ".llvm.call-graph-profile") { + CGProfileSection = Sec; + break; + } + } + + if (CGProfileSection == object::SectionRef()) + return; + + StringRef CGProfileContents = + unwrapOrError(Obj->getFileName(), CGProfileSection.getContents()); + + ListScope L(W, "CGProfile"); + const uint8_t *Cur = CGProfileContents.bytes_begin(); + const uint8_t *End = CGProfileContents.bytes_end(); + while (Cur != End) { + uint64_t FromIndex, ToIndex; + StringRef From, To; + Cur += decodeSymbol(Cur, End, FromIndex, From); + Cur += decodeSymbol(Cur, End, ToIndex, To); unsigned Size; const char *Err; - uint64_t SymIndex = decodeULEB128(Cur, &Size, End, &Err); + uint64_t Weight = decodeULEB128(Cur, &Size, End, &Err); if (Err) reportError(createError(Err), Obj->getFileName()); + Cur += Size; + + DictScope D(W, "CGProfileEntry"); + W.printNumber("From", From, FromIndex); + W.printNumber("To", To, ToIndex); + W.printNumber("Weight", Weight); + } +} - Expected Sym = Obj->getSymbol(SymIndex); - if (!Sym) - reportError(Sym.takeError(), Obj->getFileName()); +unsigned COFFDumper::decodeSymbol(const uint8_t *Cur, const uint8_t *End, + uint64_t &SymIndex, StringRef &SymName) { + unsigned Size; + const char *Err; + SymIndex = decodeULEB128(Cur, &Size, End, &Err); + if (Err) + reportError(createError(Err), Obj->getFileName()); - Expected SymName = Obj->getSymbolName(*Sym); - if (!SymName) - reportError(SymName.takeError(), Obj->getFileName()); + Expected Sym = Obj->getSymbol(SymIndex); + if (!Sym) + reportError(Sym.takeError(), Obj->getFileName()); - W.printNumber("Sym", *SymName, SymIndex); - Cur += Size; - } + Expected NameOrErr = Obj->getSymbolName(*Sym); + if (!NameOrErr) + reportError(NameOrErr.takeError(), Obj->getFileName()); + + SymName = *NameOrErr; + + return Size; } void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -346,7 +346,10 @@ cl::aliasopt(HashHistogram)); // --elf-cg-profile - cl::opt CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section")); + cl::opt ELFCGProfile("elf-cg-profile", cl::desc("Display callgraph profile section")); + +// --coff-cg-profile + cl::opt COFFCGProfile("coff-cg-profile", cl::desc("Display callgraph profile section")); // -addrsig cl::opt Addrsig("addrsig", @@ -503,7 +506,7 @@ Dumper->printGroupSections(); if (opts::HashHistogram) Dumper->printHashHistograms(); - if (opts::CGProfile) + if (opts::ELFCGProfile) Dumper->printCGProfile(); if (opts::Addrsig) Dumper->printAddrsig(); @@ -525,6 +528,8 @@ Dumper->printCOFFResources(); if (opts::COFFLoadConfig) Dumper->printCOFFLoadConfig(); + if (opts::COFFCGProfile) + Dumper->printCGProfile(); if (opts::Addrsig) Dumper->printAddrsig(); if (opts::CodeView)