[MachO] Port call graph profile section and directive

This ports the `.cg_profile` assembly directive and call graph profile section
generation to MachO from COFF/ELF. Due to MachO section naming rules, the
section is called `__LLVM,__cg_profile` rather than `.llvm.call-graph-profile`
as in COFF/ELF. Support for llvm-readobj is included to facilitate testing.

Corresponding LLD change is D112164

Differential Revision: https://reviews.llvm.org/D112160
This commit is contained in:
Leonard Grey 2022-01-12 09:12:53 -05:00 committed by Nico Weber
parent 732ad8ea62
commit 0f85393004
8 changed files with 221 additions and 12 deletions

View File

@ -1188,6 +1188,7 @@ void TargetLoweringObjectFileMachO::emitModuleMetadata(MCStreamer &Streamer,
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())

View File

@ -116,8 +116,16 @@ public:
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 @@ static bool canGoAfterDWARF(const MCSectionMachO &MSec) {
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 @@ void MCMachOStreamer::finishImpl() {
}
}
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<MCAsmBackend> &&MAB,
std::unique_ptr<MCObjectWriter> &&OW,

View File

@ -195,6 +195,8 @@ public:
addDirectiveHandler<&DarwinAsmParser::parseMacOSXVersionMin>(
".macosx_version_min");
addDirectiveHandler<&DarwinAsmParser::parseBuildVersion>(".build_version");
addDirectiveHandler<&DarwinAsmParser::parseDirectiveCGProfile>(
".cg_profile");
LastVersionDirective = SMLoc();
}
@ -467,6 +469,7 @@ public:
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 @@ bool DarwinAsmParser::parseBuildVersion(StringRef Directive, SMLoc Loc) {
return false;
}
/// parseDirectiveCGProfile
/// ::= .cg_profile from, to, count
bool DarwinAsmParser::parseDirectiveCGProfile(StringRef S, SMLoc Loc) {
return MCAsmParserExtension::ParseDirectiveCGProfile(S, Loc);
}
namespace llvm {

View File

@ -759,6 +759,23 @@ uint64_t MachObjectWriter::writeObject(MCAssembler &Asm,
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<MCDataFragment>(
&*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();

View File

@ -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:]

View File

@ -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: ]

View File

@ -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 @@ public:
void printRelocations() override;
void printUnwindInfo() override;
void printStackMap() const override;
void printCGProfile() override;
void printNeededLibraries() override;
@ -49,6 +51,8 @@ private:
template<class MachHeader>
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 @@ void MachODumper::printRelocation(const MachOObjectFile *Obj,
if (IsExtern) {
symbol_iterator Symbol = Reloc.getSymbol();
if (Symbol != Obj->symbol_end()) {
Expected<StringRef> 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 @@ void MachODumper::printRelocation(const MachOObjectFile *Obj,
}
}
StringRef MachODumper::getSymbolName(const SymbolRef &Symbol) {
Expected<StringRef> 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::printDynamicSymbols() {
}
void MachODumper::printSymbol(const SymbolRef &Symbol) {
StringRef SymbolName;
Expected<StringRef> 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 @@ void MachODumper::printStackMap() const {
W, StackMapParser<support::big>(StackMapContentsArray));
}
void MachODumper::printCGProfile() {
object::SectionRef CGProfileSection;
for (auto Sec : Obj->sections()) {
StringRef Name;
if (Expected<StringRef> 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");

View File

@ -448,6 +448,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
Dumper->printMachOVersionMin();
if (opts::MachODysymtab)
Dumper->printMachODysymtab();
if (opts::CGProfile)
Dumper->printCGProfile();
}
if (opts::PrintStackMap)
Dumper->printStackMap();