[llvm] 0f85393 - [MachO] Port call graph profile section and directive
Nico Weber via llvm-commits
llvm-commits at lists.llvm.org
Wed Jan 12 06:22:47 PST 2022
Author: Leonard Grey
Date: 2022-01-12T09:22:26-05:00
New Revision: 0f853930042f11f5ee489bd56b48a227d057c37e
URL: https://github.com/llvm/llvm-project/commit/0f853930042f11f5ee489bd56b48a227d057c37e
DIFF: https://github.com/llvm/llvm-project/commit/0f853930042f11f5ee489bd56b48a227d057c37e.diff
LOG: [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
Added:
llvm/test/MC/MachO/cgprofile.ll
llvm/test/MC/MachO/cgprofile.s
Modified:
llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
llvm/lib/MC/MCMachOStreamer.cpp
llvm/lib/MC/MCParser/DarwinAsmParser.cpp
llvm/lib/MC/MachObjectWriter.cpp
llvm/tools/llvm-readobj/MachODumper.cpp
llvm/tools/llvm-readobj/llvm-readobj.cpp
Removed:
################################################################################
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 20ef01e10c20..ce350034d073 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -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())
diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp
index 3edf7a3f49e6..88aeeb980738 100644
--- a/llvm/lib/MC/MCMachOStreamer.cpp
+++ b/llvm/lib/MC/MCMachOStreamer.cpp
@@ -116,8 +116,16 @@ class MCMachOStreamer : public MCObjectStreamer {
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,
diff --git a/llvm/lib/MC/MCParser/DarwinAsmParser.cpp b/llvm/lib/MC/MCParser/DarwinAsmParser.cpp
index 3bc13012c019..91aa2de92627 100644
--- a/llvm/lib/MC/MCParser/DarwinAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/DarwinAsmParser.cpp
@@ -195,6 +195,8 @@ class DarwinAsmParser : public MCAsmParserExtension {
addDirectiveHandler<&DarwinAsmParser::parseMacOSXVersionMin>(
".macosx_version_min");
addDirectiveHandler<&DarwinAsmParser::parseBuildVersion>(".build_version");
+ addDirectiveHandler<&DarwinAsmParser::parseDirectiveCGProfile>(
+ ".cg_profile");
LastVersionDirective = SMLoc();
}
@@ -467,6 +469,7 @@ class DarwinAsmParser : public MCAsmParserExtension {
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 {
diff --git a/llvm/lib/MC/MachObjectWriter.cpp b/llvm/lib/MC/MachObjectWriter.cpp
index 16941b1cb727..5105c5d8164d 100644
--- a/llvm/lib/MC/MachObjectWriter.cpp
+++ b/llvm/lib/MC/MachObjectWriter.cpp
@@ -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();
diff --git a/llvm/test/MC/MachO/cgprofile.ll b/llvm/test/MC/MachO/cgprofile.ll
new file mode 100644
index 000000000000..aa8564bd54a7
--- /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
index 000000000000..6d356b3103cb
--- /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
index 945b16b8db86..599b0355917e 100644
--- 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 @@ class MachODumper : public ObjDumper {
void printRelocations() override;
void printUnwindInfo() override;
void printStackMap() const override;
+ void printCGProfile() override;
void printNeededLibraries() override;
@@ -49,6 +51,8 @@ class MachODumper : public ObjDumper {
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");
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index eea486abe0a1..543b0de82cdf 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -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();
More information about the llvm-commits
mailing list