diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -1188,6 +1188,7 @@ StringRef SectionVal; GetObjCImageInfo(M, VersionVal, ImageInfoFlags, SectionVal); + emitCGProfileMetadata(Streamer, M); // The section is mandatory. If we don't have it, then we don't have GC info. if (SectionVal.empty()) diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp --- a/llvm/lib/MC/MCMachOStreamer.cpp +++ b/llvm/lib/MC/MCMachOStreamer.cpp @@ -116,8 +116,16 @@ void emitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) override { getAssembler().getLOHContainer().addDirective(Kind, Args); } + void emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, uint64_t Count) override { + if (!From->getSymbol().isTemporary() && !To->getSymbol().isTemporary()) + getAssembler().CGProfile.push_back({From, To, Count}); + } void finishImpl() override; + + void finalizeCGProfileEntry(const MCSymbolRefExpr *&SRE); + void finalizeCGProfile(); }; } // end anonymous namespace. @@ -145,7 +153,8 @@ if (SegName == "__DATA" && (SecName == "__nl_symbol_ptr" || SecName == "__thread_ptr")) return true; - + if (SegName == "__LLVM" && SecName == "__cg_profile") + return true; return false; } @@ -513,9 +522,40 @@ } } + finalizeCGProfile(); + this->MCObjectStreamer::finishImpl(); } +void MCMachOStreamer::finalizeCGProfileEntry(const MCSymbolRefExpr *&SRE) { + const MCSymbol *S = &SRE->getSymbol(); + bool Created; + getAssembler().registerSymbol(*S, &Created); + if (Created) + S->setExternal(true); +} + +void MCMachOStreamer::finalizeCGProfile() { + MCAssembler &Asm = getAssembler(); + if (Asm.CGProfile.empty()) + return; + for (MCAssembler::CGProfileEntry &E : Asm.CGProfile) { + finalizeCGProfileEntry(E.From); + finalizeCGProfileEntry(E.To); + } + // We can't write the section out until symbol indices are finalized which + // doesn't happen until after section layout. We need to create the section + // and set its size now so that it's accounted for in layout. + MCSection *CGProfileSection = Asm.getContext().getMachOSection( + "__LLVM", "__cg_profile", 0, SectionKind::getMetadata()); + Asm.registerSection(*CGProfileSection); + auto *Frag = new MCDataFragment(CGProfileSection); + // For each entry, reserve space for 2 32-bit indices and a 64-bit count. + size_t SectionBytes = + Asm.CGProfile.size() * (2 * sizeof(uint32_t) + sizeof(uint64_t)); + Frag->getContents().resize(SectionBytes); +} + MCStreamer *llvm::createMachOStreamer(MCContext &Context, std::unique_ptr &&MAB, std::unique_ptr &&OW, diff --git a/llvm/lib/MC/MCParser/DarwinAsmParser.cpp b/llvm/lib/MC/MCParser/DarwinAsmParser.cpp --- a/llvm/lib/MC/MCParser/DarwinAsmParser.cpp +++ b/llvm/lib/MC/MCParser/DarwinAsmParser.cpp @@ -195,6 +195,8 @@ addDirectiveHandler<&DarwinAsmParser::parseMacOSXVersionMin>( ".macosx_version_min"); addDirectiveHandler<&DarwinAsmParser::parseBuildVersion>(".build_version"); + addDirectiveHandler<&DarwinAsmParser::parseDirectiveCGProfile>( + ".cg_profile"); LastVersionDirective = SMLoc(); } @@ -467,6 +469,7 @@ bool parseSDKVersion(VersionTuple &SDKVersion); void checkVersion(StringRef Directive, StringRef Arg, SMLoc Loc, Triple::OSType ExpectedOS); + bool parseDirectiveCGProfile(StringRef Directive, SMLoc Loc); }; } // end anonymous namespace @@ -1198,6 +1201,11 @@ return false; } +/// parseDirectiveCGProfile +/// ::= .cg_profile from, to, count +bool DarwinAsmParser::parseDirectiveCGProfile(StringRef S, SMLoc Loc) { + return MCAsmParserExtension::ParseDirectiveCGProfile(S, Loc); +} namespace llvm { diff --git a/llvm/lib/MC/MachObjectWriter.cpp b/llvm/lib/MC/MachObjectWriter.cpp --- a/llvm/lib/MC/MachObjectWriter.cpp +++ b/llvm/lib/MC/MachObjectWriter.cpp @@ -759,6 +759,23 @@ computeSymbolTable(Asm, LocalSymbolData, ExternalSymbolData, UndefinedSymbolData); + if (!Asm.CGProfile.empty()) { + MCSection *CGProfileSection = Asm.getContext().getMachOSection( + "__LLVM", "__cg_profile", 0, SectionKind::getMetadata()); + MCDataFragment *Frag = dyn_cast_or_null( + &*CGProfileSection->getFragmentList().begin()); + assert(Frag && "call graph profile section not reserved"); + Frag->getContents().set_size(0); + raw_svector_ostream OS(Frag->getContents()); + for (const MCAssembler::CGProfileEntry &CGPE : Asm.CGProfile) { + uint32_t FromIndex = CGPE.From->getSymbol().getIndex(); + uint32_t ToIndex = CGPE.To->getSymbol().getIndex(); + support::endian::write(OS, FromIndex, W.Endian); + support::endian::write(OS, ToIndex, W.Endian); + support::endian::write(OS, CGPE.Count, W.Endian); + } + } + unsigned NumSections = Asm.size(); const MCAssembler::VersionInfoType &VersionInfo = Layout.getAssembler().getVersionInfo(); diff --git a/llvm/test/MC/MachO/cgprofile.ll b/llvm/test/MC/MachO/cgprofile.ll new file mode 100644 --- /dev/null +++ b/llvm/test/MC/MachO/cgprofile.ll @@ -0,0 +1,51 @@ +; RUN: llc -filetype=asm %s -o - -mtriple x86_64-apple-darwin | FileCheck %s +; RUN: llc -filetype=obj %s -o %t -mtriple x86_64-apple-darwin +; RUN: llvm-readobj --cg-profile %t | FileCheck %s --check-prefix=OBJ + +declare void @b() + +define void @a() { + call void @b() + ret void +} + +define void @freq(i1 %cond) { + br i1 %cond, label %A, label %B +A: + call void @a(); + ret void +B: + call void @b(); + ret void +} + +!llvm.module.flags = !{!0} + +!0 = !{i32 5, !"CG Profile", !1} +!1 = !{!2, !3, !4, !5} +!2 = !{void ()* @a, void ()* @b, i64 32} +!3 = !{void (i1)* @freq, void ()* @a, i64 11} +!4 = !{void (i1)* @freq, void ()* @b, i64 20} +!5 = !{void (i1)* @freq, null, i64 20} + +; CHECK: .cg_profile _a, _b, 32 +; CHECK: .cg_profile _freq, _a, 11 +; CHECK: .cg_profile _freq, _b, 20 + +; OBJ: CGProfile [ +; OBJ: CGProfileEntry { +; OBJ: From: _a +; OBJ: To: _b +; OBJ: Weight: 32 +; OBJ: } +; OBJ: CGProfileEntry { +; OBJ: From: _freq +; OBJ: To: _a +; OBJ: Weight: 11 +; OBJ: } +; OBJ: CGProfileEntry { +; OBJ: From: _freq +; OBJ: To: _b +; OBJ: Weight: 20 +; OBJ: } +; OBJ:] diff --git a/llvm/test/MC/MachO/cgprofile.s b/llvm/test/MC/MachO/cgprofile.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/MachO/cgprofile.s @@ -0,0 +1,45 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t +# RUN: llvm-readobj -S --symbols --sd --cg-profile %t | FileCheck %s + + .section __TEXT,__text +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: __cg_profile +# CHECK-NEXT: Segment: __LLVM +# CHECK-NEXT: Address: +# CHECK-NEXT: Size: 0x30 +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 00000000 04000000 20000000 00000000 +# CHECK-NEXT: 0010: 05000000 00000000 0B000000 00000000 +# CHECK-NEXT: 0020: 03000000 01000000 14000000 00000000 +# CHECK-NEXT: ) + +# CHECK: CGProfile [ +# CHECK-NEXT: CGProfileEntry { +# CHECK-NEXT: From: a (0) +# CHECK-NEXT: To: b (4) +# CHECK-NEXT: Weight: 32 +# CHECK-NEXT: } +# CHECK-NEXT: CGProfileEntry { +# CHECK-NEXT: From: freq (5) +# CHECK-NEXT: To: a (0) +# CHECK-NEXT: Weight: 11 +# CHECK-NEXT: } +# CHECK-NEXT: CGProfileEntry { +# CHECK-NEXT: From: late (3) +# CHECK-NEXT: To: late2 (1) +# CHECK-NEXT: Weight: 20 +# CHECK-NEXT: } +# CHECK-NEXT: ] diff --git a/llvm/tools/llvm-readobj/MachODumper.cpp b/llvm/tools/llvm-readobj/MachODumper.cpp --- a/llvm/tools/llvm-readobj/MachODumper.cpp +++ b/llvm/tools/llvm-readobj/MachODumper.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Object/MachO.h" +#include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ScopedPrinter.h" @@ -34,6 +35,7 @@ void printRelocations() override; void printUnwindInfo() override; void printStackMap() const override; + void printCGProfile() override; void printNeededLibraries() override; @@ -49,6 +51,8 @@ template void printFileHeaders(const MachHeader &Header); + StringRef getSymbolName(const SymbolRef &Symbol); + void printSymbols() override; void printDynamicSymbols() override; void printSymbol(const SymbolRef &Symbol); @@ -551,10 +555,7 @@ if (IsExtern) { symbol_iterator Symbol = Reloc.getSymbol(); if (Symbol != Obj->symbol_end()) { - Expected TargetNameOrErr = Symbol->getName(); - if (!TargetNameOrErr) - reportError(TargetNameOrErr.takeError(), Obj->getFileName()); - TargetName = *TargetNameOrErr; + TargetName = getSymbolName(*Symbol); } } else if (!IsScattered) { section_iterator SecI = Obj->getRelocationSection(DR); @@ -601,6 +602,14 @@ } } +StringRef MachODumper::getSymbolName(const SymbolRef &Symbol) { + Expected SymbolNameOrErr = Symbol.getName(); + if (!SymbolNameOrErr) { + reportError(SymbolNameOrErr.takeError(), Obj->getFileName()); + } + return *SymbolNameOrErr; +} + void MachODumper::printSymbols() { ListScope Group(W, "Symbols"); @@ -614,13 +623,7 @@ } void MachODumper::printSymbol(const SymbolRef &Symbol) { - StringRef SymbolName; - Expected SymbolNameOrErr = Symbol.getName(); - if (!SymbolNameOrErr) { - // TODO: Actually report errors helpfully. - consumeError(SymbolNameOrErr.takeError()); - } else - SymbolName = *SymbolNameOrErr; + StringRef SymbolName = getSymbolName(Symbol); MachOSymbol MOSymbol; getSymbol(Obj, Symbol.getRawDataRefImpl(), MOSymbol); @@ -696,6 +699,48 @@ W, StackMapParser(StackMapContentsArray)); } +void MachODumper::printCGProfile() { + object::SectionRef CGProfileSection; + for (auto Sec : Obj->sections()) { + StringRef Name; + if (Expected NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + if (Name == "__cg_profile") { + CGProfileSection = Sec; + break; + } + } + if (CGProfileSection == object::SectionRef()) + return; + + StringRef CGProfileContents = + unwrapOrError(Obj->getFileName(), CGProfileSection.getContents()); + BinaryStreamReader Reader(CGProfileContents, Obj->isLittleEndian() + ? llvm::support::little + : llvm::support::big); + + ListScope L(W, "CGProfile"); + while (!Reader.empty()) { + uint32_t FromIndex, ToIndex; + uint64_t Count; + if (Error Err = Reader.readInteger(FromIndex)) + reportError(std::move(Err), Obj->getFileName()); + if (Error Err = Reader.readInteger(ToIndex)) + reportError(std::move(Err), Obj->getFileName()); + if (Error Err = Reader.readInteger(Count)) + reportError(std::move(Err), Obj->getFileName()); + DictScope D(W, "CGProfileEntry"); + W.printNumber("From", getSymbolName(*Obj->getSymbolByIndex(FromIndex)), + FromIndex); + W.printNumber("To", getSymbolName(*Obj->getSymbolByIndex(ToIndex)), + ToIndex); + W.printNumber("Weight", Count); + } +} + void MachODumper::printNeededLibraries() { ListScope D(W, "NeededLibraries"); 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 @@ -448,6 +448,8 @@ Dumper->printMachOVersionMin(); if (opts::MachODysymtab) Dumper->printMachODysymtab(); + if (opts::CGProfile) + Dumper->printCGProfile(); } if (opts::PrintStackMap) Dumper->printStackMap();