mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-30 01:06:05 +00:00
[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:
parent
732ad8ea62
commit
0f85393004
@ -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())
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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();
|
||||
|
51
llvm/test/MC/MachO/cgprofile.ll
Normal file
51
llvm/test/MC/MachO/cgprofile.ll
Normal 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:]
|
45
llvm/test/MC/MachO/cgprofile.s
Normal file
45
llvm/test/MC/MachO/cgprofile.s
Normal 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: ]
|
@ -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");
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user