aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lld/MachO/MarkLive.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2022-07-04 19:20:19 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-04-09 14:54:45 +0000
commit255d6c9fe5a7577c6caf78004034f2555bd0cba0 (patch)
treeb5136fa6092bd88d67f3f8e83405ec6fe0144c66 /contrib/llvm-project/lld/MachO/MarkLive.cpp
parenta0b3fbe4ccb6961765d2325bb2ecae6ff2111102 (diff)
Merge llvm-project main llvmorg-15-init-15358-g53dc0f10787
This updates llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp to llvmorg-15-init-15358-g53dc0f10787. PR: 265425 MFC after: 2 weeks (cherry picked from commit 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
Diffstat (limited to 'contrib/llvm-project/lld/MachO/MarkLive.cpp')
-rw-r--r--contrib/llvm-project/lld/MachO/MarkLive.cpp295
1 files changed, 209 insertions, 86 deletions
diff --git a/contrib/llvm-project/lld/MachO/MarkLive.cpp b/contrib/llvm-project/lld/MachO/MarkLive.cpp
index 4269c8342c65..7239e30bbdb2 100644
--- a/contrib/llvm-project/lld/MachO/MarkLive.cpp
+++ b/contrib/llvm-project/lld/MachO/MarkLive.cpp
@@ -21,65 +21,228 @@ namespace macho {
using namespace llvm;
using namespace llvm::MachO;
+struct WhyLiveEntry {
+ InputSection *isec;
+ // Keep track of the entry that caused us to mark `isec` as live.
+ const WhyLiveEntry *prev;
+
+ WhyLiveEntry(InputSection *isec, const WhyLiveEntry *prev)
+ : isec(isec), prev(prev) {}
+};
+
+// Type-erased interface to MarkLiveImpl. Used for adding roots to the liveness
+// graph.
+class MarkLive {
+public:
+ virtual void enqueue(InputSection *isec, uint64_t off) = 0;
+ virtual void addSym(Symbol *s) = 0;
+ virtual void markTransitively() = 0;
+ virtual ~MarkLive() = default;
+};
+
+template <bool RecordWhyLive> class MarkLiveImpl : public MarkLive {
+public:
+ // -why_live is a rarely used option, so we don't want support for that flag
+ // to slow down the main -dead_strip code path. As such, we employ templates
+ // to avoid the usage of WhyLiveEntry in the main code path. This saves us
+ // from needless allocations and pointer indirections.
+ using WorklistEntry =
+ std::conditional_t<RecordWhyLive, WhyLiveEntry, InputSection>;
+
+ void enqueue(InputSection *isec, uint64_t off) override {
+ enqueue(isec, off, nullptr);
+ }
+ void addSym(Symbol *s) override { addSym(s, nullptr); }
+ void markTransitively() override;
+
+private:
+ void enqueue(InputSection *isec, uint64_t off, const WorklistEntry *prev);
+ void addSym(Symbol *s, const WorklistEntry *prev);
+ void printWhyLive(Symbol *s, const WorklistEntry *prev);
+ const InputSection *getInputSection(const WorklistEntry *) const;
+ WorklistEntry *makeEntry(InputSection *, const WorklistEntry *prev) const;
+
+ // We build up a worklist of sections which have been marked as live. We
+ // only push into the worklist when we discover an unmarked section, and we
+ // mark as we push, so sections never appear twice in the list. Literal
+ // sections cannot contain references to other sections, so we only store
+ // ConcatInputSections in our worklist.
+ SmallVector<WorklistEntry *, 256> worklist;
+};
+
+template <bool RecordWhyLive>
+void MarkLiveImpl<RecordWhyLive>::enqueue(
+ InputSection *isec, uint64_t off,
+ const typename MarkLiveImpl<RecordWhyLive>::WorklistEntry *prev) {
+ if (isec->isLive(off))
+ return;
+ isec->markLive(off);
+ if (auto s = dyn_cast<ConcatInputSection>(isec)) {
+ assert(!s->isCoalescedWeak());
+ worklist.push_back(makeEntry(s, prev));
+ }
+}
+
+template <bool RecordWhyLive>
+void MarkLiveImpl<RecordWhyLive>::addSym(
+ Symbol *s,
+ const typename MarkLiveImpl<RecordWhyLive>::WorklistEntry *prev) {
+ if (s->used)
+ return;
+ s->used = true;
+ printWhyLive(s, prev);
+ if (auto *d = dyn_cast<Defined>(s)) {
+ if (d->isec)
+ enqueue(d->isec, d->value, prev);
+ if (d->unwindEntry)
+ enqueue(d->unwindEntry, 0, prev);
+ }
+}
+
+static void printWhyLiveImpl(const Symbol *s, const WhyLiveEntry *prev) {
+ std::string out = toString(*s) + " from " + toString(s->getFile());
+ int indent = 2;
+ for (const WhyLiveEntry *entry = prev; entry;
+ entry = entry->prev, indent += 2) {
+ const TinyPtrVector<Defined *> &symbols = entry->isec->symbols;
+ // With .subsections_with_symbols set, most isecs will have exactly one
+ // entry in their symbols vector, so we just print the first one.
+ if (!symbols.empty())
+ out += "\n" + std::string(indent, ' ') + toString(*symbols.front()) +
+ " from " + toString(symbols.front()->getFile());
+ }
+ message(out);
+}
+
+// NOTE: if/when `constexpr if` becomes available, we can simplify a lot of
+// the partial template specializations below.
+
+template <>
+void MarkLiveImpl<true>::printWhyLive(Symbol *s, const WhyLiveEntry *prev) {
+ if (!config->whyLive.empty() && config->whyLive.match(s->getName()))
+ printWhyLiveImpl(s, prev);
+}
+
+template <>
+void MarkLiveImpl<false>::printWhyLive(Symbol *s, const InputSection *prev) {}
+
+template <>
+const InputSection *
+MarkLiveImpl<true>::getInputSection(const WhyLiveEntry *entry) const {
+ return entry->isec;
+}
+
+template <>
+const InputSection *
+MarkLiveImpl<false>::getInputSection(const InputSection *isec) const {
+ return isec;
+}
+
+template <>
+typename MarkLiveImpl<true>::WorklistEntry *MarkLiveImpl<true>::makeEntry(
+ InputSection *isec, const MarkLiveImpl<true>::WorklistEntry *prev) const {
+ if (!isec) {
+ assert(!prev);
+ return nullptr;
+ }
+ return make<WhyLiveEntry>(isec, prev);
+}
+
+template <>
+typename MarkLiveImpl<false>::WorklistEntry *MarkLiveImpl<false>::makeEntry(
+ InputSection *isec, const MarkLiveImpl<false>::WorklistEntry *prev) const {
+ return isec;
+}
+
+template <bool RecordWhyLive>
+void MarkLiveImpl<RecordWhyLive>::markTransitively() {
+ do {
+ // Mark things reachable from GC roots as live.
+ while (!worklist.empty()) {
+ WorklistEntry *entry = worklist.pop_back_val();
+ // Entries that get placed onto the worklist always contain
+ // ConcatInputSections. `WhyLiveEntry::prev` may point to entries that
+ // contain other types of InputSections (due to S_ATTR_LIVE_SUPPORT), but
+ // those entries should never be pushed onto the worklist.
+ auto *isec = cast<ConcatInputSection>(getInputSection(entry));
+ assert(isec->live && "We mark as live when pushing onto the worklist!");
+
+ // Mark all symbols listed in the relocation table for this section.
+ for (const Reloc &r : isec->relocs) {
+ if (auto *s = r.referent.dyn_cast<Symbol *>())
+ addSym(s, entry);
+ else
+ enqueue(r.referent.get<InputSection *>(), r.addend, entry);
+ }
+ for (Defined *d : getInputSection(entry)->symbols)
+ addSym(d, entry);
+ }
+
+ // S_ATTR_LIVE_SUPPORT sections are live if they point _to_ a live
+ // section. Process them in a second pass.
+ for (ConcatInputSection *isec : inputSections) {
+ // FIXME: Check if copying all S_ATTR_LIVE_SUPPORT sections into a
+ // separate vector and only walking that here is faster.
+ if (!(isec->getFlags() & S_ATTR_LIVE_SUPPORT) || isec->live)
+ continue;
+
+ for (const Reloc &r : isec->relocs) {
+ if (auto *s = r.referent.dyn_cast<Symbol *>()) {
+ if (s->isLive()) {
+ InputSection *referentIsec = nullptr;
+ if (auto *d = dyn_cast<Defined>(s))
+ referentIsec = d->isec;
+ enqueue(isec, 0, makeEntry(referentIsec, nullptr));
+ }
+ } else {
+ auto *referentIsec = r.referent.get<InputSection *>();
+ if (referentIsec->isLive(r.addend))
+ enqueue(isec, 0, makeEntry(referentIsec, nullptr));
+ }
+ }
+ }
+
+ // S_ATTR_LIVE_SUPPORT could have marked additional sections live,
+ // which in turn could mark additional S_ATTR_LIVE_SUPPORT sections live.
+ // Iterate. In practice, the second iteration won't mark additional
+ // S_ATTR_LIVE_SUPPORT sections live.
+ } while (!worklist.empty());
+}
+
// Set live bit on for each reachable chunk. Unmarked (unreachable)
// InputSections will be ignored by Writer, so they will be excluded
// from the final output.
void markLive() {
TimeTraceScope timeScope("markLive");
-
- // We build up a worklist of sections which have been marked as live. We only
- // push into the worklist when we discover an unmarked section, and we mark
- // as we push, so sections never appear twice in the list.
- // Literal sections cannot contain references to other sections, so we only
- // store ConcatInputSections in our worklist.
- SmallVector<ConcatInputSection *, 256> worklist;
-
- auto enqueue = [&](InputSection *isec, uint64_t off) {
- if (isec->isLive(off))
- return;
- isec->markLive(off);
- if (auto s = dyn_cast<ConcatInputSection>(isec)) {
- assert(!s->isCoalescedWeak());
- worklist.push_back(s);
- }
- };
-
- auto addSym = [&](Symbol *s) {
- if (s->used)
- return;
- s->used = true;
- if (auto *d = dyn_cast<Defined>(s)) {
- if (d->isec)
- enqueue(d->isec, d->value);
- if (d->unwindEntry)
- enqueue(d->unwindEntry, 0);
- }
- };
-
+ MarkLive *marker;
+ if (config->whyLive.empty())
+ marker = make<MarkLiveImpl<false>>();
+ else
+ marker = make<MarkLiveImpl<true>>();
// Add GC roots.
if (config->entry)
- addSym(config->entry);
+ marker->addSym(config->entry);
for (Symbol *sym : symtab->getSymbols()) {
if (auto *defined = dyn_cast<Defined>(sym)) {
// -exported_symbol(s_list)
if (!config->exportedSymbols.empty() &&
config->exportedSymbols.match(defined->getName())) {
- // FIXME: Instead of doing this here, maybe the Driver code doing
- // the matching should add them to explicitUndefineds? Then the
- // explicitUndefineds code below would handle this automatically.
- assert(!defined->privateExtern &&
- "should have been rejected by driver");
- addSym(defined);
+ // NOTE: Even though exporting private externs is an ill-defined
+ // operation, we are purposely not checking for privateExtern in
+ // order to follow ld64's behavior of treating all exported private
+ // extern symbols as live, irrespective of whether they are autohide.
+ marker->addSym(defined);
continue;
}
// public symbols explicitly marked .no_dead_strip
if (defined->referencedDynamically || defined->noDeadStrip) {
- addSym(defined);
+ marker->addSym(defined);
continue;
}
- // FIXME: When we implement these flags, make symbols from them GC roots:
+ // FIXME: When we implement these flags, make symbols from them GC
+ // roots:
// * -reexported_symbol(s_list)
// * -alias(-list)
// * -init
@@ -89,80 +252,40 @@ void markLive() {
bool externsAreRoots =
config->outputType != MH_EXECUTE || config->exportDynamic;
if (externsAreRoots && !defined->privateExtern) {
- addSym(defined);
+ marker->addSym(defined);
continue;
}
}
}
// -u symbols
for (Symbol *sym : config->explicitUndefineds)
- addSym(sym);
+ marker->addSym(sym);
// local symbols explicitly marked .no_dead_strip
for (const InputFile *file : inputFiles)
if (auto *objFile = dyn_cast<ObjFile>(file))
for (Symbol *sym : objFile->symbols)
if (auto *defined = dyn_cast_or_null<Defined>(sym))
if (!defined->isExternal() && defined->noDeadStrip)
- addSym(defined);
+ marker->addSym(defined);
if (auto *stubBinder =
dyn_cast_or_null<DylibSymbol>(symtab->find("dyld_stub_binder")))
- addSym(stubBinder);
+ marker->addSym(stubBinder);
for (ConcatInputSection *isec : inputSections) {
// Sections marked no_dead_strip
if (isec->getFlags() & S_ATTR_NO_DEAD_STRIP) {
- enqueue(isec, 0);
+ marker->enqueue(isec, 0);
continue;
}
// mod_init_funcs, mod_term_funcs sections
if (sectionType(isec->getFlags()) == S_MOD_INIT_FUNC_POINTERS ||
sectionType(isec->getFlags()) == S_MOD_TERM_FUNC_POINTERS) {
- enqueue(isec, 0);
+ marker->enqueue(isec, 0);
continue;
}
}
- do {
- // Mark things reachable from GC roots as live.
- while (!worklist.empty()) {
- ConcatInputSection *s = worklist.pop_back_val();
- assert(s->live && "We mark as live when pushing onto the worklist!");
-
- // Mark all symbols listed in the relocation table for this section.
- for (const Reloc &r : s->relocs) {
- if (auto *s = r.referent.dyn_cast<Symbol *>())
- addSym(s);
- else
- enqueue(r.referent.get<InputSection *>(), r.addend);
- }
- for (Defined *d : s->symbols)
- addSym(d);
- }
-
- // S_ATTR_LIVE_SUPPORT sections are live if they point _to_ a live section.
- // Process them in a second pass.
- for (ConcatInputSection *isec : inputSections) {
- // FIXME: Check if copying all S_ATTR_LIVE_SUPPORT sections into a
- // separate vector and only walking that here is faster.
- if (!(isec->getFlags() & S_ATTR_LIVE_SUPPORT) || isec->live)
- continue;
-
- for (const Reloc &r : isec->relocs) {
- bool referentLive;
- if (auto *s = r.referent.dyn_cast<Symbol *>())
- referentLive = s->isLive();
- else
- referentLive = r.referent.get<InputSection *>()->isLive(r.addend);
- if (referentLive)
- enqueue(isec, 0);
- }
- }
-
- // S_ATTR_LIVE_SUPPORT could have marked additional sections live,
- // which in turn could mark additional S_ATTR_LIVE_SUPPORT sections live.
- // Iterate. In practice, the second iteration won't mark additional
- // S_ATTR_LIVE_SUPPORT sections live.
- } while (!worklist.empty());
+ marker->markTransitively();
}
} // namespace macho