[llvm] [Orc][LibResolver] Refactor resolver internals and simplify symbol resolution. (PR #169161)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Dec 4 02:15:56 PST 2025
https://github.com/SahilPatidar updated https://github.com/llvm/llvm-project/pull/169161
>From 1d31d7a2be8a3a4ce2369746dc1e1e25073607b7 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sat, 22 Nov 2025 15:55:51 +0530
Subject: [PATCH 1/5] [Orc][LibResolver] Refactor resolver internals and
simplify symbol resolution
---
.../Orc/TargetProcess/LibraryResolver.h | 290 ++++++++++++------
.../Orc/TargetProcess/LibraryScanner.h | 10 +-
.../Orc/TargetProcess/LibraryResolver.cpp | 257 ++++++++--------
.../Orc/TargetProcess/LibraryScanner.cpp | 48 +--
4 files changed, 358 insertions(+), 247 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h
index 79cfc4832fe9a..e12bf84ed4e63 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h
@@ -24,81 +24,136 @@
namespace llvm {
namespace orc {
+class LibraryManager;
-/// Manages library metadata and state for symbol resolution.
-///
-/// Tracks libraries by load state and kind (user/system), and stores
-/// associated Bloom filters and hash maps to speed up symbol lookups.
-/// Thread-safe for concurrent access.
-class LibraryManager {
+enum class LibState : uint8_t { Unloaded = 0, Loaded = 1, Queried = 2 };
+
+class LibraryInfo {
public:
- enum class LibState : uint8_t { Unloaded = 0, Loaded = 1, Queried = 2 };
+ LibraryInfo(const LibraryInfo &) = delete;
+ LibraryInfo &operator=(const LibraryInfo &) = delete;
- class LibraryInfo {
- public:
- LibraryInfo(const LibraryInfo &) = delete;
- LibraryInfo &operator=(const LibraryInfo &) = delete;
+ LibraryInfo(std::string FilePath, LibState S, PathType K,
+ std::optional<BloomFilter> Filter = std::nullopt)
+ : FilePath(std::move(FilePath)), S(S), K(K), Filter(std::move(Filter)) {}
- LibraryInfo(std::string FilePath, LibState S, PathType K,
- std::optional<BloomFilter> Filter = std::nullopt)
- : FilePath(std::move(FilePath)), S(S), K(K), Filter(std::move(Filter)) {
- }
+ StringRef getBasePath() const { return sys::path::parent_path(FilePath); }
+ StringRef getFileName() const { return sys::path::filename(FilePath); }
- StringRef getBasePath() const { return sys::path::parent_path(FilePath); }
- StringRef getFileName() const { return sys::path::filename(FilePath); }
+ std::string getFullPath() const { return FilePath; }
- std::string getFullPath() const { return FilePath; }
+ void setFilter(BloomFilter F) {
+ std::lock_guard<std::shared_mutex> Lock(Mtx);
+ if (Filter)
+ return;
+ Filter.emplace(std::move(F));
+ }
- void setFilter(BloomFilter F) {
- std::lock_guard<std::shared_mutex> Lock(Mtx);
- if (Filter)
- return;
- Filter.emplace(std::move(F));
- }
+ void ensureFilterBuilt(const BloomFilterBuilder &FB,
+ ArrayRef<StringRef> Symbols) {
+ std::lock_guard<std::shared_mutex> Lock(Mtx);
+ if (Filter)
+ return;
+ Filter.emplace(FB.build(Symbols));
+ }
- void ensureFilterBuilt(const BloomFilterBuilder &FB,
- ArrayRef<StringRef> Symbols) {
- std::lock_guard<std::shared_mutex> Lock(Mtx);
- if (Filter)
- return;
- Filter.emplace(FB.build(Symbols));
- }
+ bool mayContain(StringRef Symbol) const {
+ assert(hasFilter());
+ std::shared_lock<std::shared_mutex> Lock(Mtx);
+ return Filter->mayContain(Symbol);
+ }
- bool mayContain(StringRef Symbol) const {
- assert(hasFilter());
- std::shared_lock<std::shared_mutex> Lock(Mtx);
- return Filter->mayContain(Symbol);
- }
+ bool hasFilter() const {
+ std::shared_lock<std::shared_mutex> Lock(Mtx);
+ return Filter.has_value();
+ }
- bool hasFilter() const {
- std::shared_lock<std::shared_mutex> Lock(Mtx);
- return Filter.has_value();
- }
+ LibState getState() const { return S.load(); }
+ PathType getKind() const { return K; }
- LibState getState() const { return S.load(); }
- PathType getKind() const { return K; }
+ void setState(LibState s) { S.store(s); }
- void setState(LibState s) { S.store(s); }
+ bool operator==(const LibraryInfo &other) const {
+ return FilePath == other.FilePath;
+ }
+
+private:
+ std::string FilePath;
+ std::atomic<LibState> S;
+ PathType K;
+ std::optional<BloomFilter> Filter;
+ mutable std::shared_mutex Mtx;
+};
- bool operator==(const LibraryInfo &other) const {
- return FilePath == other.FilePath;
+class LibraryCursor {
+public:
+ LibraryCursor(const std::vector<const LibraryInfo *> &L, LibState S)
+ : Lists(L), S(S) {}
+
+ const LibraryInfo *nextValidLib() {
+ while (Pos < Lists.size()) {
+ const LibraryInfo *Lib = Lists[Pos++];
+ if (Lib->getState() == S)
+ return Lib;
}
- private:
- std::string FilePath;
- std::atomic<LibState> S;
- PathType K;
- std::optional<BloomFilter> Filter;
- mutable std::shared_mutex Mtx;
- };
+ return nullptr;
+ }
+
+ bool hasMoreValidLib() const { return Pos < Lists.size(); }
+private:
+ const std::vector<const LibraryInfo *> &Lists;
+ LibState S;
+ size_t Pos = 0; // cursor position
+};
+
+class LibraryIndex {
+ friend LibraryManager;
+
+public:
+ LibraryCursor getCursor(PathType K, LibState S) const {
+ static std::vector<const LibraryInfo *> Empty;
+ auto It = Lists.find(K);
+ if (It == Lists.end())
+ return LibraryCursor(Empty, S);
+
+ return LibraryCursor(It->second, S);
+ }
+
+ bool hasLibLeftFor(PathType K, uint32_t Idx) const {
+ auto It = Lists.find(K);
+ if (It == Lists.end())
+ return false;
+
+ const auto &L = It->second;
+ return L.size() > Idx;
+ }
+
+private:
+ void addLibrary(const LibraryInfo *Lib) {
+ Lists[Lib->getKind()].push_back(Lib);
+ }
+
+ void clear() { Lists.clear(); }
+
+ DenseMap<PathType, std::vector<const LibraryInfo *>> Lists;
+};
+
+/// Manages library metadata and state for symbol resolution.
+///
+/// Tracks libraries by load state and kind (user/system), and stores
+/// associated Bloom filters and hash maps to speed up symbol lookups.
+/// Thread-safe for concurrent access.
+class LibraryManager {
+public:
/// A read-only view of libraries filtered by state and kind.
///
/// Lets you loop over only the libraries in a map that match a given State
/// and PathType.
class FilteredView {
public:
- using Map = StringMap<std::shared_ptr<LibraryInfo>>;
+ using Map = StringMap<std::unique_ptr<LibraryInfo>>;
using Iterator = Map::const_iterator;
class FilterIterator {
public:
@@ -111,9 +166,7 @@ class LibraryManager {
return it != other.it;
}
- const std::shared_ptr<LibraryInfo> &operator*() const {
- return it->second;
- }
+ const LibraryInfo &operator*() const { return *it->second; }
FilterIterator &operator++() {
++it;
@@ -151,7 +204,8 @@ class LibraryManager {
};
private:
- StringMap<std::shared_ptr<LibraryInfo>> Libraries;
+ LibraryIndex Index;
+ StringMap<std::unique_ptr<LibraryInfo>> Libraries;
mutable std::shared_mutex Mtx;
public:
@@ -165,9 +219,11 @@ class LibraryManager {
std::unique_lock<std::shared_mutex> Lock(Mtx);
if (Libraries.count(Path) > 0)
return false;
- Libraries.insert({std::move(Path),
- std::make_shared<LibraryInfo>(Path, LibState::Unloaded,
- Kind, std::move(Filter))});
+ std::unique_ptr<LibraryInfo> Lib = std::make_unique<LibraryInfo>(
+ Path, LibState::Unloaded, Kind, std::move(Filter));
+ const LibraryInfo *Ptr = Lib.get();
+ Libraries.insert({Path, std::move(Lib)});
+ Index.addLibrary(Ptr);
return true;
}
@@ -192,16 +248,22 @@ class LibraryManager {
It->second->setState(LibState::Loaded);
}
+ void markUnloaded(StringRef Path) {
+ std::unique_lock<std::shared_mutex> Lock(Mtx);
+ if (auto It = Libraries.find(Path); It != Libraries.end())
+ It->second->setState(LibState::Unloaded);
+ }
+
void markQueried(StringRef Path) {
std::unique_lock<std::shared_mutex> Lock(Mtx);
if (auto It = Libraries.find(Path); It != Libraries.end())
It->second->setState(LibState::Queried);
}
- std::shared_ptr<LibraryInfo> getLibrary(StringRef Path) {
+ const LibraryInfo *getLibrary(StringRef Path) const {
std::shared_lock<std::shared_mutex> Lock(Mtx);
if (auto It = Libraries.find(Path); It != Libraries.end())
- return It->second;
+ return It->second.get();
return nullptr;
}
@@ -212,7 +274,7 @@ class LibraryManager {
using LibraryFilterFn = std::function<bool(const LibraryInfo &)>;
void getLibraries(LibState S, PathType K,
- std::vector<std::shared_ptr<LibraryInfo>> &Outs,
+ std::vector<const LibraryInfo *> &Outs,
LibraryFilterFn Filter = nullptr) const {
std::shared_lock<std::shared_mutex> Lock(Mtx);
for (const auto &[_, Entry] : Libraries) {
@@ -221,12 +283,16 @@ class LibraryManager {
continue;
if (Filter && !Filter(Info))
continue;
- Outs.push_back(Entry);
+ Outs.push_back(&Info);
}
}
+ LibraryCursor getCursor(PathType K, LibState S) const {
+ return Index.getCursor(K, S);
+ }
+
void forEachLibrary(const LibraryVisitor &visitor) const {
- std::unique_lock<std::shared_mutex> Lock(Mtx);
+ std::shared_lock<std::shared_mutex> Lock(Mtx);
for (const auto &[_, entry] : Libraries) {
if (!visitor(*entry))
break;
@@ -253,23 +319,21 @@ class LibraryManager {
}
};
-using LibraryInfo = LibraryManager::LibraryInfo;
-
struct SearchPlanEntry {
- LibraryManager::LibState State; // Loaded, Queried, Unloaded
- PathType Type; // User, System
+ LibState State; // Loaded, Queried, Unloaded
+ PathType Type; // User, System
};
struct SearchPolicy {
std::vector<SearchPlanEntry> Plan;
static SearchPolicy defaultPlan() {
- return {{{LibraryManager::LibState::Loaded, PathType::User},
- {LibraryManager::LibState::Queried, PathType::User},
- {LibraryManager::LibState::Unloaded, PathType::User},
- {LibraryManager::LibState::Loaded, PathType::System},
- {LibraryManager::LibState::Queried, PathType::System},
- {LibraryManager::LibState::Unloaded, PathType::System}}};
+ return {{{LibState::Loaded, PathType::User},
+ {LibState::Queried, PathType::User},
+ {LibState::Unloaded, PathType::User},
+ {LibState::Loaded, PathType::System},
+ {LibState::Queried, PathType::System},
+ {LibState::Unloaded, PathType::System}}};
}
};
@@ -299,6 +363,63 @@ struct SearchConfig {
Options(SymbolEnumeratorOptions::defaultOptions()) {}
};
+// class SearchSession {
+// std::mutex Mtx;
+// std::condition_variable Cv;
+
+// int ActiveSearchers = 0;
+// int ActiveScanners = 0;
+
+// public:
+// // Called by each search before work begins
+// void beginSearch() {
+// std::unique_lock<std::mutex> lock(Mtx);
+
+// // Wait if scanning is happening
+// Cv.wait(lock, [&] { return ActiveScanners == 0; });
+
+// ActiveSearchers++;
+// }
+
+// // Called after search completes
+// void endSearch() {
+// std::unique_lock<std::mutex> lock(Mtx);
+// ActiveSearchers--;
+// if (ActiveSearchers == 0)
+// Cv.notify_all();
+// }
+
+// void beginScan() {
+// std::unique_lock<std::mutex> lock(Mtx);
+
+// // Block until NO searchers are active
+// Cv.wait(lock, [&] { return ActiveSearchers == 0; });
+
+// ActiveScanners++;
+// }
+
+// // Scanner finished
+// void endScan() {
+// std::unique_lock<std::mutex> lock(Mtx);
+// ActiveScanners--;
+// if (ActiveScanners == 0)
+// Cv.notify_all(); // wake pending searchers
+// }
+
+// // RAII
+// struct SearchGuard {
+// SearchSession &S;
+// SearchGuard(SearchSession &s) : S(s) { S.beginSearch(); }
+// ~SearchGuard() { S.endSearch(); }
+// };
+
+// struct ScanGuard {
+// SearchSession &S;
+// ScanGuard(SearchSession &s) : S(s) { S.beginScan(); }
+// ~ScanGuard() { S.endScan(); }
+// };
+// };
+
/// Scans libraries and resolves Symbols across user and system paths.
///
/// Supports symbol enumeration and filtering via SymbolEnumerator, and tracks
@@ -314,6 +435,8 @@ class LibraryResolver {
using OnEachSymbolFn = std::function<EnumerateResult(StringRef Sym)>;
+ static bool enumerateSymbols(object::ObjectFile *Obj, OnEachSymbolFn OnEach,
+ const SymbolEnumeratorOptions &Opts);
static bool enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
const SymbolEnumeratorOptions &Opts);
};
@@ -442,9 +565,7 @@ class LibraryResolver {
<< " ({Type : ("
<< (Lib.getKind() == PathType::User ? "User" : "System")
<< ") }, { State : "
- << (Lib.getState() == LibraryManager::LibState::Loaded
- ? "Loaded"
- : "Unloaded")
+ << (Lib.getState() == LibState::Loaded ? "Loaded" : "Unloaded")
<< "})\n";
return true;
});
@@ -456,15 +577,8 @@ class LibraryResolver {
private:
bool scanLibrariesIfNeeded(PathType K, size_t BatchSize = 0);
- void resolveSymbolsInLibrary(LibraryInfo &Lib, SymbolQuery &Q,
+ void resolveSymbolsInLibrary(LibraryInfo *Lib, SymbolQuery &Q,
const SymbolEnumeratorOptions &Opts);
- bool
- symbolExistsInLibrary(const LibraryInfo &Lib, StringRef Sym,
- std::vector<std::string> *MatchedSymbols = nullptr);
-
- bool symbolExistsInLibrary(const LibraryInfo &Lib, StringRef SymName,
- std::vector<std::string> *AllSymbols,
- const SymbolEnumeratorOptions &Opts);
std::shared_ptr<LibraryPathCache> LibPathCache;
std::shared_ptr<PathResolver> LibPathResolver;
@@ -485,8 +599,8 @@ class LibraryResolutionDriver {
create(const LibraryResolver::Setup &S);
void addScanPath(const std::string &Path, PathType Kind);
- bool markLibraryLoaded(StringRef Path);
- bool markLibraryUnLoaded(StringRef Path);
+ void markLibraryLoaded(StringRef Path);
+ void markLibraryUnLoaded(StringRef Path);
bool isLibraryLoaded(StringRef Path) const {
return LR->LibMgr.isLoaded(Path);
}
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
index 61aefbda35337..88ccfe30dd649 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
@@ -338,14 +338,14 @@ class LibraryScanHelper {
addBasePath(const std::string &P,
PathType Kind =
PathType::Unknown); // Add a canonical directory for scanning
- std::vector<std::shared_ptr<LibrarySearchPath>>
- getNextBatch(PathType Kind, size_t batchSize);
+ std::vector<const LibrarySearchPath *> getNextBatch(PathType Kind,
+ size_t batchSize);
bool leftToScan(PathType K) const;
void resetToScan();
bool isTrackedBasePath(StringRef P) const;
- std::vector<std::shared_ptr<LibrarySearchPath>> getAllUnits() const;
+ std::vector<const LibrarySearchPath *> getAllUnits() const;
SmallVector<StringRef> getSearchPaths() const {
SmallVector<StringRef> SearchPaths;
@@ -374,7 +374,7 @@ class LibraryScanHelper {
std::shared_ptr<LibraryPathCache> LibPathCache;
std::shared_ptr<PathResolver> LibPathResolver;
- StringMap<std::shared_ptr<LibrarySearchPath>>
+ StringMap<std::unique_ptr<LibrarySearchPath>>
LibSearchPaths; // key: canonical path
std::deque<StringRef> UnscannedUsr;
std::deque<StringRef> UnscannedSys;
@@ -461,7 +461,7 @@ class LibraryScanner {
void handleLibrary(StringRef P, PathType K, int level = 1);
- void scanBaseDir(std::shared_ptr<LibrarySearchPath> U);
+ void scanBaseDir(LibrarySearchPath *U);
};
using LibraryDepsInfo = LibraryScanner::LibraryDepsInfo;
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp
index 7e1d5285463c7..510598538d643 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp
@@ -57,24 +57,12 @@ void LibraryResolutionDriver::addScanPath(const std::string &Path, PathType K) {
LR->ScanHelper.addBasePath(Path, K);
}
-bool LibraryResolutionDriver::markLibraryLoaded(StringRef Path) {
- auto Lib = LR->LibMgr.getLibrary(Path);
- if (!Lib)
- return false;
-
- Lib->setState(LibraryManager::LibState::Loaded);
-
- return true;
+void LibraryResolutionDriver::markLibraryLoaded(StringRef Path) {
+ LR->LibMgr.markLoaded(Path);
}
-bool LibraryResolutionDriver::markLibraryUnLoaded(StringRef Path) {
- auto Lib = LR->LibMgr.getLibrary(Path);
- if (!Lib)
- return false;
-
- Lib->setState(LibraryManager::LibState::Unloaded);
-
- return true;
+void LibraryResolutionDriver::markLibraryUnLoaded(StringRef Path) {
+ LR->LibMgr.markUnloaded(Path);
}
void LibraryResolutionDriver::resolveSymbols(
@@ -108,25 +96,12 @@ static bool shouldIgnoreSymbol(const object::SymbolRef &Sym,
return false;
}
-bool SymbolEnumerator::enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
+bool SymbolEnumerator::enumerateSymbols(object::ObjectFile *Obj,
+ OnEachSymbolFn OnEach,
const SymbolEnumeratorOptions &Opts) {
- if (Path.empty())
+ if (!Obj)
return false;
- ObjectFileLoader ObjLoader(Path);
-
- auto ObjOrErr = ObjLoader.getObjectFile();
- if (!ObjOrErr) {
- std::string ErrMsg;
- handleAllErrors(ObjOrErr.takeError(),
- [&](const ErrorInfoBase &EIB) { ErrMsg = EIB.message(); });
- LLVM_DEBUG(dbgs() << "Failed loading object file: " << Path
- << "\nError: " << ErrMsg << "\n");
- return false;
- }
-
- object::ObjectFile *Obj = &ObjOrErr.get();
-
auto processSymbolRange =
[&](object::ObjectFile::symbol_iterator_range Range) -> EnumerateResult {
for (const auto &Sym : Range) {
@@ -180,6 +155,23 @@ bool SymbolEnumerator::enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
return true;
}
+bool SymbolEnumerator::enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
+ const SymbolEnumeratorOptions &Opts) {
+ ObjectFileLoader ObjLoader(Path);
+
+ auto ObjOrErr = ObjLoader.getObjectFile();
+ if (!ObjOrErr) {
+ std::string ErrMsg;
+ handleAllErrors(ObjOrErr.takeError(),
+ [&](const ErrorInfoBase &EIB) { ErrMsg = EIB.message(); });
+ LLVM_DEBUG(dbgs() << "Failed loading object file: " << Path
+ << "\nError: " << ErrMsg << "\n");
+ return false;
+ }
+
+ return SymbolEnumerator::enumerateSymbols(&ObjOrErr.get(), OnEach, Opts);
+}
+
class SymbolSearchContext {
public:
SymbolSearchContext(SymbolQuery &Q) : Q(Q) {}
@@ -198,80 +190,100 @@ class SymbolSearchContext {
};
void LibraryResolver::resolveSymbolsInLibrary(
- LibraryInfo &Lib, SymbolQuery &UnresolvedSymbols,
+ LibraryInfo *Lib, SymbolQuery &UnresolvedSymbols,
const SymbolEnumeratorOptions &Opts) {
LLVM_DEBUG(dbgs() << "Checking unresolved symbols "
- << " in library : " << Lib.getFileName() << "\n";);
- StringSet<> DiscoveredSymbols;
+ << " in library : " << Lib->getFileName() << "\n";);
if (!UnresolvedSymbols.hasUnresolved()) {
- LLVM_DEBUG(dbgs() << "Skipping library: " << Lib.getFullPath()
+ LLVM_DEBUG(dbgs() << "Skipping library: " << Lib->getFullPath()
<< " — unresolved symbols exist.\n";);
return;
}
- bool HasEnumerated = false;
- auto enumerateSymbolsIfNeeded = [&]() {
- if (HasEnumerated)
- return;
+ bool HadAnySym = false;
- HasEnumerated = true;
+ const auto &Unresolved = UnresolvedSymbols.getUnresolvedSymbols();
+ LLVM_DEBUG(dbgs() << "Total unresolved symbols : " << Unresolved.size()
+ << "\n";);
+ DenseSet<StringRef> CandidateSet;
+ CandidateSet.reserve(Unresolved.size());
+ for (const auto &Sym : Unresolved) {
+ LLVM_DEBUG(dbgs() << "Checking symbol '" << Sym
+ << "' in filter: " << Lib->getFullPath() << "\n";);
+ if (!Lib->hasFilter() || Lib->mayContain(Sym))
+ CandidateSet.insert(Sym);
+ }
- LLVM_DEBUG(dbgs() << "Enumerating symbols in library: " << Lib.getFullPath()
- << "\n";);
- SymbolEnumerator::enumerateSymbols(
- Lib.getFullPath(),
- [&](StringRef sym) {
- DiscoveredSymbols.insert(sym);
- return EnumerateResult::Continue;
- },
- Opts);
- };
+ if (CandidateSet.empty()) {
+ LLVM_DEBUG(dbgs() << "No symbol Exist "
+ " in library: "
+ << Lib->getFullPath() << "\n";);
+ return;
+ }
+
+ bool BuildingFilter = !Lib->hasFilter();
- if (!Lib.hasFilter()) {
- LLVM_DEBUG(dbgs() << "Building filter for library: " << Lib.getFullPath()
+ ObjectFileLoader ObjLoader(Lib->getFullPath());
+
+ auto ObjOrErr = ObjLoader.getObjectFile();
+ if (!ObjOrErr) {
+ std::string ErrMsg;
+ handleAllErrors(ObjOrErr.takeError(),
+ [&](const ErrorInfoBase &EIB) { ErrMsg = EIB.message(); });
+ LLVM_DEBUG(dbgs() << "Failed loading object file: " << Lib->getFullPath()
+ << "\nError: " << ErrMsg << "\n");
+ return;
+ }
+
+ object::ObjectFile *Obj = &ObjOrErr.get();
+
+ SmallVector<StringRef, 512> SymbolVec;
+
+ LLVM_DEBUG(dbgs() << "Enumerating symbols in library: " << Lib->getFullPath()
+ << "\n";);
+ SymbolEnumerator::enumerateSymbols(
+ Obj,
+ [&](StringRef S) {
+ // If buildingFilter, collect for filter construction.
+ if (BuildingFilter) {
+ SymbolVec.push_back(S);
+ }
+ auto It = CandidateSet.find(S);
+ if (It != CandidateSet.end()) {
+ // Resolve symbol and remove from remaining set
+ LLVM_DEBUG(dbgs() << "Symbol '" << S << "' resolved in library: "
+ << Lib->getFullPath() << "\n";);
+ UnresolvedSymbols.resolve(*It, Lib->getFullPath());
+ HadAnySym = true;
+
+ // EARLY STOP — everything matched
+ if (!BuildingFilter && !UnresolvedSymbols.hasUnresolved())
+ return EnumerateResult::Stop;
+ }
+ return EnumerateResult::Continue;
+ },
+ Opts);
+
+ if (BuildingFilter) {
+ LLVM_DEBUG(dbgs() << "Building filter for library: " << Lib->getFullPath()
<< "\n";);
- enumerateSymbolsIfNeeded();
- if (DiscoveredSymbols.empty()) {
- LLVM_DEBUG(dbgs() << " No symbols and remove library : "
- << Lib.getFullPath() << "\n";);
- LibMgr.removeLibrary(Lib.getFullPath());
+ if (SymbolVec.empty()) {
+ LLVM_DEBUG(dbgs() << " Skip : No symbols found in : "
+ << Lib->getFullPath() << "\n";);
return;
}
- SmallVector<StringRef> SymbolVec;
- SymbolVec.reserve(DiscoveredSymbols.size());
- for (const auto &KV : DiscoveredSymbols)
- SymbolVec.push_back(KV.first());
- Lib.ensureFilterBuilt(FB, SymbolVec);
+ Lib->ensureFilterBuilt(FB, SymbolVec);
LLVM_DEBUG({
- dbgs() << "DiscoveredSymbols : " << DiscoveredSymbols.size() << "\n";
- for (const auto &KV : DiscoveredSymbols)
- dbgs() << "DiscoveredSymbols : " << KV.first() << "\n";
+ dbgs() << "DiscoveredSymbols : " << SymbolVec.size() << "\n";
+ for (const auto &S : SymbolVec)
+ dbgs() << "DiscoveredSymbols : " << S << "\n";
});
}
- const auto &Unresolved = UnresolvedSymbols.getUnresolvedSymbols();
- bool HadAnySym = false;
- LLVM_DEBUG(dbgs() << "Total unresolved symbols : " << Unresolved.size()
- << "\n";);
- for (const auto &Sym : Unresolved) {
- if (Lib.mayContain(Sym)) {
- LLVM_DEBUG(dbgs() << "Checking symbol '" << Sym
- << "' in library: " << Lib.getFullPath() << "\n";);
- enumerateSymbolsIfNeeded();
- if (DiscoveredSymbols.count(Sym) > 0) {
- LLVM_DEBUG(dbgs() << " Resolved symbol: " << Sym
- << " in library: " << Lib.getFullPath() << "\n";);
- UnresolvedSymbols.resolve(Sym, Lib.getFullPath());
- HadAnySym = true;
- }
- }
- }
-
- using LibraryState = LibraryManager::LibState;
- if (HadAnySym && Lib.getState() != LibraryState::Loaded)
- Lib.setState(LibraryState::Queried);
+ if (HadAnySym && Lib->getState() != LibState::Loaded)
+ Lib->setState(LibState::Queried);
}
void LibraryResolver::searchSymbolsInLibraries(
@@ -279,30 +291,41 @@ void LibraryResolver::searchSymbolsInLibraries(
const SearchConfig &Config) {
SymbolQuery Q(SymbolList);
- using LibraryState = LibraryManager::LibState;
using LibraryType = PathType;
- auto tryResolveFrom = [&](LibraryState S, LibraryType K) {
+ auto tryResolveFrom = [&](LibState S, LibraryType K) {
LLVM_DEBUG(dbgs() << "Trying resolve from state=" << static_cast<int>(S)
<< " type=" << static_cast<int>(K) << "\n";);
SymbolSearchContext Ctx(Q);
+ LibraryCursor Cur = LibMgr.getCursor(K, S);
while (!Ctx.allResolved()) {
- std::vector<std::shared_ptr<LibraryInfo>> Libs;
- LibMgr.getLibraries(S, K, Libs, [&](const LibraryInfo &Lib) {
- return !Ctx.hasSearched(&Lib);
- });
-
- if (Libs.empty() && !scanLibrariesIfNeeded(K, scanBatchSize))
- break; // no more new libs to scan
+ const LibraryInfo *Lib = Cur.nextValidLib();
+ // Cursor not valid?
+ if (!Lib) {
+ bool NewLibAdded = false;
+
+ // Scan as long as there is paths to scan
+ while (ScanHelper.leftToScan(K)) {
+ scanLibrariesIfNeeded(K, scanBatchSize);
+
+ // NOW check any new libraries added
+ if (Cur.hasMoreValidLib()) {
+ NewLibAdded = true;
+ break;
+ }
+ }
- for (auto &Lib : Libs) {
- // can use Async here?
- resolveSymbolsInLibrary(*Lib, Ctx.query(), Config.Options);
- Ctx.markSearched(Lib.get());
+ if (!NewLibAdded)
+ break; // No new libs found
- if (Ctx.allResolved())
- return;
+ continue; // Try to resolve next library
}
+
+ // can use Async here?
+ resolveSymbolsInLibrary(const_cast<LibraryInfo *>(Lib), Ctx.query(),
+ Config.Options);
+ if (Ctx.allResolved())
+ break;
}
};
@@ -334,34 +357,4 @@ bool LibraryResolver::scanLibrariesIfNeeded(PathType PK, size_t BatchSize) {
Scanner.scanNext(PK, BatchSize);
return true;
}
-
-bool LibraryResolver::symbolExistsInLibrary(const LibraryInfo &Lib,
- StringRef SymName,
- std::vector<std::string> *AllSyms) {
- SymbolEnumeratorOptions Opts;
- return symbolExistsInLibrary(Lib, SymName, AllSyms, Opts);
-}
-
-bool LibraryResolver::symbolExistsInLibrary(
- const LibraryInfo &Lib, StringRef SymName,
- std::vector<std::string> *AllSyms, const SymbolEnumeratorOptions &Opts) {
- bool Found = false;
-
- SymbolEnumerator::enumerateSymbols(
- Lib.getFullPath(),
- [&](StringRef Sym) {
- if (AllSyms)
- AllSyms->emplace_back(Sym.str());
-
- if (Sym == SymName) {
- Found = true;
- }
-
- return EnumerateResult::Continue;
- },
- Opts);
-
- return Found;
-}
-
} // end namespace llvm::orc
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
index 96f0b66adec92..a33ed08be136e 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
@@ -233,6 +233,9 @@ bool DylibPathValidator::isSharedLibrary(StringRef Path) {
if (MagicCode == file_magic::archive)
return false;
+ if (MagicCode == file_magic::elf_shared_object)
+ return true;
+
// Universal binary handling.
#if defined(__APPLE__)
if (MagicCode == file_magic::macho_universal_binary) {
@@ -676,8 +679,8 @@ void LibraryScanHelper::addBasePath(const std::string &Path, PathType K) {
return;
}
K = K == PathType::Unknown ? classifyKind(Canon) : K;
- auto SP = std::make_shared<LibrarySearchPath>(Canon, K);
- LibSearchPaths[Canon] = SP;
+ LibSearchPaths[Canon] = std::make_unique<LibrarySearchPath>(Canon, K);
+ auto &SP = LibSearchPaths[Canon];
if (K == PathType::User) {
LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Added User path: "
@@ -690,9 +693,9 @@ void LibraryScanHelper::addBasePath(const std::string &Path, PathType K) {
}
}
-std::vector<std::shared_ptr<LibrarySearchPath>>
+std::vector<const LibrarySearchPath *>
LibraryScanHelper::getNextBatch(PathType K, size_t BatchSize) {
- std::vector<std::shared_ptr<LibrarySearchPath>> Result;
+ std::vector<const LibrarySearchPath *> Result;
auto &Queue = (K == PathType::User) ? UnscannedUsr : UnscannedSys;
std::unique_lock<std::shared_mutex> Lock(Mtx);
@@ -704,7 +707,7 @@ LibraryScanHelper::getNextBatch(PathType K, size_t BatchSize) {
auto &SP = It->second;
ScanState Expected = ScanState::NotScanned;
if (SP->State.compare_exchange_strong(Expected, ScanState::Scanning)) {
- Result.push_back(SP);
+ Result.push_back(SP.get());
}
}
Queue.pop_front();
@@ -748,13 +751,12 @@ void LibraryScanHelper::resetToScan() {
}
}
-std::vector<std::shared_ptr<LibrarySearchPath>>
-LibraryScanHelper::getAllUnits() const {
+std::vector<const LibrarySearchPath *> LibraryScanHelper::getAllUnits() const {
std::shared_lock<std::shared_mutex> Lock(Mtx);
- std::vector<std::shared_ptr<LibrarySearchPath>> Result;
+ std::vector<const LibrarySearchPath *> Result;
Result.reserve(LibSearchPaths.size());
for (const auto &[_, SP] : LibSearchPaths) {
- Result.push_back(SP);
+ Result.push_back(SP.get());
}
return Result;
}
@@ -989,26 +991,29 @@ std::optional<std::string> LibraryScanner::shouldScan(StringRef FilePath) {
return std::nullopt;
}
- // [4] Skip if it's not a shared library.
- if (!DylibPathValidator::isSharedLibrary(CanonicalPath)) {
- LLVM_DEBUG(dbgs() << " -> Skipped: not a shared library.\n";);
- return std::nullopt;
- }
-
- // [5] Skip if we've already seen this path (via cache)
- if (ScanHelper.hasSeenOrMark(CanonicalPath)) {
+ LibraryPathCache &Cache = ScanHelper.getCache();
+ // [4] Skip if we've already seen this path (via cache)
+ if (Cache.hasSeen(CanonicalPath)) {
LLVM_DEBUG(dbgs() << " -> Skipped: already seen.\n";);
return std::nullopt;
}
- // [6] Already tracked in LibraryManager?
+ // [5] Already tracked in LibraryManager?
if (LibMgr.hasLibrary(CanonicalPath)) {
LLVM_DEBUG(dbgs() << " -> Skipped: already tracked by LibraryManager.\n";);
+ return std::nullopt;
+ }
+ // [6] Skip if it's not a shared library.
+ if (!DylibPathValidator::isSharedLibrary(CanonicalPath)) {
+ LLVM_DEBUG(dbgs() << " -> Skipped: not a shared library.\n";);
return std::nullopt;
}
+ // Mark seen this path
+ Cache.markSeen(CanonicalPath);
+
// [7] Run user-defined hook (default: always true)
if (!ShouldScanCall(CanonicalPath)) {
LLVM_DEBUG(dbgs() << " -> Skipped: user-defined hook rejected.\n";);
@@ -1113,7 +1118,7 @@ void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {
}
}
-void LibraryScanner::scanBaseDir(std::shared_ptr<LibrarySearchPath> SP) {
+void LibraryScanner::scanBaseDir(LibrarySearchPath *SP) {
if (!sys::fs::is_directory(SP->BasePath) || SP->BasePath.empty()) {
LLVM_DEBUG(
dbgs() << "LibraryScanner::scanBaseDir: Invalid or empty basePath: "
@@ -1150,11 +1155,10 @@ void LibraryScanner::scanNext(PathType K, size_t BatchSize) {
<< (K == PathType::User ? "User" : "System") << "\n";);
auto SearchPaths = ScanHelper.getNextBatch(K, BatchSize);
- for (auto &SP : SearchPaths) {
+ for (const auto *SP : SearchPaths) {
LLVM_DEBUG(dbgs() << " Scanning unit with basePath: " << SP->BasePath
<< "\n";);
-
- scanBaseDir(SP);
+ scanBaseDir(const_cast<LibrarySearchPath *>(SP));
}
}
>From 4f1f103435b035e8645e540ba6bb67cfa7d02751 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sat, 22 Nov 2025 16:16:18 +0530
Subject: [PATCH 2/5] Refactor searchSymbolsInLibraries
---
.../Orc/TargetProcess/LibraryResolver.h | 1 +
.../Orc/TargetProcess/LibraryResolver.cpp | 31 +++++++++----------
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h
index e12bf84ed4e63..4990d83651a69 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h
@@ -577,6 +577,7 @@ class LibraryResolver {
private:
bool scanLibrariesIfNeeded(PathType K, size_t BatchSize = 0);
+ bool scanForNewLibraries(PathType K, LibraryCursor &Cur);
void resolveSymbolsInLibrary(LibraryInfo *Lib, SymbolQuery &Q,
const SymbolEnumeratorOptions &Opts);
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp
index 510598538d643..4e68501cbfc4a 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp
@@ -302,22 +302,8 @@ void LibraryResolver::searchSymbolsInLibraries(
const LibraryInfo *Lib = Cur.nextValidLib();
// Cursor not valid?
if (!Lib) {
- bool NewLibAdded = false;
-
- // Scan as long as there is paths to scan
- while (ScanHelper.leftToScan(K)) {
- scanLibrariesIfNeeded(K, scanBatchSize);
-
- // NOW check any new libraries added
- if (Cur.hasMoreValidLib()) {
- NewLibAdded = true;
- break;
- }
- }
-
- if (!NewLibAdded)
- break; // No new libs found
-
+ if (!scanForNewLibraries(K, Cur))
+ break; // nothing new was added
continue; // Try to resolve next library
}
@@ -346,6 +332,19 @@ void LibraryResolver::searchSymbolsInLibraries(
OnComplete(Q);
}
+bool LibraryResolver::scanForNewLibraries(PathType K, LibraryCursor &Cur) {
+ while (ScanHelper.leftToScan(K)) {
+ scanLibrariesIfNeeded(K, scanBatchSize);
+
+ // Check if scanning added new libraries
+ if (Cur.hasMoreValidLib())
+ return true;
+ }
+
+ // No new libraries were added
+ return false;
+}
+
bool LibraryResolver::scanLibrariesIfNeeded(PathType PK, size_t BatchSize) {
LLVM_DEBUG(dbgs() << "LibraryResolver::scanLibrariesIfNeeded: Scanning for "
<< (PK == PathType::User ? "User" : "System")
>From 61c039654af22861df7824bea532bfd1a089157b Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Mon, 24 Nov 2025 15:01:40 +0530
Subject: [PATCH 3/5] Refactor
---
llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
index a33ed08be136e..88725b5a60273 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
@@ -233,9 +233,6 @@ bool DylibPathValidator::isSharedLibrary(StringRef Path) {
if (MagicCode == file_magic::archive)
return false;
- if (MagicCode == file_magic::elf_shared_object)
- return true;
-
// Universal binary handling.
#if defined(__APPLE__)
if (MagicCode == file_magic::macho_universal_binary) {
>From 999bb5d2bd9dfb307bc4bc3873a16641a43c3ac4 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 26 Nov 2025 15:45:39 +0530
Subject: [PATCH 4/5] Refactor LibraryScanner
---
.../Orc/TargetProcess/LibraryResolver.h | 57 --------
.../Orc/TargetProcess/LibraryScanner.h | 4 +-
.../Orc/TargetProcess/LibraryScanner.cpp | 125 +++++++++---------
3 files changed, 64 insertions(+), 122 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h
index 4990d83651a69..b8b61bf445167 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h
@@ -363,63 +363,6 @@ struct SearchConfig {
Options(SymbolEnumeratorOptions::defaultOptions()) {}
};
-// class SearchSession {
-// std::mutex Mtx;
-// std::condition_variable Cv;
-
-// int ActiveSearchers = 0;
-// int ActiveScanners = 0;
-
-// public:
-// // Called by each search before work begins
-// void beginSearch() {
-// std::unique_lock<std::mutex> lock(Mtx);
-
-// // Wait if scanning is happening
-// Cv.wait(lock, [&] { return ActiveScanners == 0; });
-
-// ActiveSearchers++;
-// }
-
-// // Called after search completes
-// void endSearch() {
-// std::unique_lock<std::mutex> lock(Mtx);
-// ActiveSearchers--;
-// if (ActiveSearchers == 0)
-// Cv.notify_all();
-// }
-
-// void beginScan() {
-// std::unique_lock<std::mutex> lock(Mtx);
-
-// // Block until NO searchers are active
-// Cv.wait(lock, [&] { return ActiveSearchers == 0; });
-
-// ActiveScanners++;
-// }
-
-// // Scanner finished
-// void endScan() {
-// std::unique_lock<std::mutex> lock(Mtx);
-// ActiveScanners--;
-// if (ActiveScanners == 0)
-// Cv.notify_all(); // wake pending searchers
-// }
-
-// // RAII
-// struct SearchGuard {
-// SearchSession &S;
-// SearchGuard(SearchSession &s) : S(s) { S.beginSearch(); }
-// ~SearchGuard() { S.endSearch(); }
-// };
-
-// struct ScanGuard {
-// SearchSession &S;
-// ScanGuard(SearchSession &s) : S(s) { S.beginScan(); }
-// ~ScanGuard() { S.endScan(); }
-// };
-// };
-
/// Scans libraries and resolves Symbols across user and system paths.
///
/// Supports symbol enumeration and filtering via SymbolEnumerator, and tracks
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
index 88ccfe30dd649..3e6299c101a77 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
@@ -456,10 +456,10 @@ class LibraryScanner {
LibraryManager &LibMgr;
ShouldScanFn ShouldScanCall;
- std::optional<std::string> shouldScan(StringRef FilePath);
+ bool shouldScan(StringRef FilePath);
Expected<LibraryDepsInfo> extractDeps(StringRef FilePath);
- void handleLibrary(StringRef P, PathType K, int level = 1);
+ void handleLibrary(StringRef FilePath, PathType K, int level = 1);
void scanBaseDir(LibrarySearchPath *U);
};
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
index 88725b5a60273..e634ecb3ef820 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
@@ -957,88 +957,53 @@ Expected<LibraryDepsInfo> LibraryScanner::extractDeps(StringRef FilePath) {
FilePath.str().c_str());
}
-std::optional<std::string> LibraryScanner::shouldScan(StringRef FilePath) {
- std::error_code EC;
-
+bool LibraryScanner::shouldScan(StringRef FilePath) {
LLVM_DEBUG(dbgs() << "[shouldScan] Checking: " << FilePath << "\n";);
- // [1] Check file existence early
- if (!sys::fs::exists(FilePath)) {
- LLVM_DEBUG(dbgs() << " -> Skipped: file does not exist.\n";);
-
- return std::nullopt;
- }
-
- // [2] Resolve to canonical path
- auto CanonicalPathOpt = ScanHelper.resolve(FilePath, EC);
- if (EC || !CanonicalPathOpt) {
- LLVM_DEBUG(dbgs() << " -> Skipped: failed to resolve path (EC="
- << EC.message() << ").\n";);
-
- return std::nullopt;
- }
-
- const std::string &CanonicalPath = *CanonicalPathOpt;
- LLVM_DEBUG(dbgs() << " -> Canonical path: " << CanonicalPath << "\n");
-
- // [3] Check if it's a directory — skip directories
- if (sys::fs::is_directory(CanonicalPath)) {
- LLVM_DEBUG(dbgs() << " -> Skipped: path is a directory.\n";);
-
- return std::nullopt;
- }
-
LibraryPathCache &Cache = ScanHelper.getCache();
- // [4] Skip if we've already seen this path (via cache)
- if (Cache.hasSeen(CanonicalPath)) {
+ // [1] Skip if we've already seen this path (via cache)
+ if (Cache.hasSeen(FilePath)) {
LLVM_DEBUG(dbgs() << " -> Skipped: already seen.\n";);
-
- return std::nullopt;
+ return false;
}
- // [5] Already tracked in LibraryManager?
- if (LibMgr.hasLibrary(CanonicalPath)) {
+ // [2] Already tracked in LibraryManager?
+ if (LibMgr.hasLibrary(FilePath)) {
LLVM_DEBUG(dbgs() << " -> Skipped: already tracked by LibraryManager.\n";);
- return std::nullopt;
+ return false;
}
- // [6] Skip if it's not a shared library.
- if (!DylibPathValidator::isSharedLibrary(CanonicalPath)) {
+ // [3] Skip if it's not a shared library.
+ if (!DylibPathValidator::isSharedLibrary(FilePath)) {
LLVM_DEBUG(dbgs() << " -> Skipped: not a shared library.\n";);
- return std::nullopt;
+ return false;
}
// Mark seen this path
- Cache.markSeen(CanonicalPath);
+ Cache.markSeen(FilePath.str());
- // [7] Run user-defined hook (default: always true)
- if (!ShouldScanCall(CanonicalPath)) {
+ // [4] Run user-defined hook (default: always true)
+ if (!ShouldScanCall(FilePath)) {
LLVM_DEBUG(dbgs() << " -> Skipped: user-defined hook rejected.\n";);
-
- return std::nullopt;
+ return false;
}
- LLVM_DEBUG(dbgs() << " -> Accepted: ready to scan " << CanonicalPath
- << "\n";);
- return CanonicalPath;
+ LLVM_DEBUG(dbgs() << " -> Accepted: ready to scan " << FilePath << "\n";);
+ return true;
}
void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {
LLVM_DEBUG(dbgs() << "LibraryScanner::handleLibrary: Scanning: " << FilePath
<< ", level=" << level << "\n";);
- auto CanonPathOpt = shouldScan(FilePath);
- if (!CanonPathOpt) {
+ if (!shouldScan(FilePath)) {
LLVM_DEBUG(dbgs() << " Skipped (shouldScan returned false): " << FilePath
<< "\n";);
-
return;
}
- const std::string CanonicalPath = *CanonPathOpt;
- auto DepsOrErr = extractDeps(CanonicalPath);
+ auto DepsOrErr = extractDeps(FilePath);
if (!DepsOrErr) {
- LLVM_DEBUG(dbgs() << " Failed to extract deps for: " << CanonicalPath
- << "\n";);
+ LLVM_DEBUG(dbgs() << " Failed to extract deps for: " << FilePath << "\n";);
handleError(DepsOrErr.takeError());
return;
}
@@ -1058,15 +1023,15 @@ void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {
});
if (Deps.isPIE && level == 0) {
- LLVM_DEBUG(dbgs() << " Skipped PIE executable at top level: "
- << CanonicalPath << "\n";);
+ LLVM_DEBUG(dbgs() << " Skipped PIE executable at top level: " << FilePath
+ << "\n";);
return;
}
- bool Added = LibMgr.addLibrary(CanonicalPath, K);
+ bool Added = LibMgr.addLibrary(FilePath.str(), K);
if (!Added) {
- LLVM_DEBUG(dbgs() << " Already added: " << CanonicalPath << "\n";);
+ LLVM_DEBUG(dbgs() << " Already added: " << FilePath << "\n";);
return;
}
@@ -1074,7 +1039,7 @@ void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {
if (Deps.rpath.empty() && Deps.runPath.empty()) {
LLVM_DEBUG(
dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic1): "
- << CanonicalPath << "\n";);
+ << FilePath << "\n";);
return;
}
@@ -1084,20 +1049,20 @@ void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {
return std::all_of(Paths.begin(), Paths.end(), [&](StringRef P) {
LLVM_DEBUG(dbgs() << " Checking isTrackedBasePath : " << P << "\n";);
return ScanHelper.isTrackedBasePath(
- DylibResolver::resolvelinkerFlag(P, CanonicalPath));
+ DylibResolver::resolvelinkerFlag(P, FilePath));
});
};
if (allTracked(Deps.rpath) && allTracked(Deps.runPath)) {
LLVM_DEBUG(
dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic2): "
- << CanonicalPath << "\n";);
+ << FilePath << "\n";);
return;
}
DylibPathValidator Validator(ScanHelper.getPathResolver());
DylibResolver Resolver(Validator);
- Resolver.configure(CanonicalPath,
+ Resolver.configure(FilePath,
{{Deps.rpath, SearchPathType::RPath},
{ScanHelper.getSearchPaths(), SearchPathType::UsrOrSys},
{Deps.runPath, SearchPathType::RunPath}});
@@ -1138,8 +1103,42 @@ void LibraryScanner::scanBaseDir(LibrarySearchPath *SP) {
auto Status = *Entry.status();
if (sys::fs::is_regular_file(Status) || sys::fs::is_symlink_file(Status)) {
LLVM_DEBUG(dbgs() << " Found file: " << Entry.path() << "\n";);
+
+ std::string FinalPath;
+ bool IsSymlink = sys::fs::is_symlink_file(Status);
+
+ // Resolve symlink
+ if (IsSymlink) {
+ LLVM_DEBUG(dbgs() << " Symlink → resolving...\n");
+
+ auto CanonicalOpt = ScanHelper.resolve(Entry.path(), EC);
+ if (EC || !CanonicalOpt) {
+ LLVM_DEBUG(dbgs() << " -> Skipped: resolve failed (EC="
+ << EC.message() << ")\n");
+ continue;
+ }
+
+ FinalPath = std::move(*CanonicalOpt);
+
+ LLVM_DEBUG(dbgs() << " Canonical: " << FinalPath << "\n");
+
+ } else {
+ // make absolute
+ SmallString<256> Abs(Entry.path());
+ sys::fs::make_absolute(Abs);
+ FinalPath = Abs.str().str();
+
+ LLVM_DEBUG(dbgs() << " Regular: absolute = " << FinalPath << "\n");
+ }
+
+ // Check if it's a directory — skip directories
+ if (sys::fs::is_directory(Status)) {
+ LLVM_DEBUG(dbgs() << " -> Skipped: path is a directory.\n";);
+ continue;
+ }
+
// async support ?
- handleLibrary(Entry.path(), SP->Kind);
+ handleLibrary(FinalPath, SP->Kind);
}
}
>From 0afae12b64a1447c5fc4cd19447a3a47e897662d Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Thu, 4 Dec 2025 14:31:40 +0530
Subject: [PATCH 5/5] Refactor Scanner: improve cache
---
.../Orc/TargetProcess/LibraryScanner.h | 18 +-
.../Orc/TargetProcess/LibraryScanner.cpp | 183 +++++++++---------
.../Orc/LibraryResolverTest.cpp | 8 +-
3 files changed, 111 insertions(+), 98 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
index 3e6299c101a77..ce523287a979f 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h
@@ -187,9 +187,15 @@ class DylibSubstitutor {
/// checks whether they point to valid shared libraries.
class DylibPathValidator {
public:
- DylibPathValidator(PathResolver &PR) : LibPathResolver(PR) {}
+ DylibPathValidator(PathResolver &PR, LibraryPathCache &LC)
+ : LibPathResolver(PR), LibPathCache(LC) {}
static bool isSharedLibrary(StringRef Path);
+ bool isSharedLibraryCached(StringRef Path) const {
+ if (LibPathCache.hasSeen(Path))
+ return true;
+ return isSharedLibrary(Path);
+ }
std::optional<std::string> normalize(StringRef Path) const {
std::error_code ec;
@@ -202,11 +208,13 @@ class DylibPathValidator {
/// Validate the given path as a shared library.
std::optional<std::string> validate(StringRef Path) const {
+ if (LibPathCache.hasSeen(Path))
+ return Path.str();
auto realOpt = normalize(Path);
if (!realOpt)
return std::nullopt;
- if (!isSharedLibrary(*realOpt))
+ if (!isSharedLibraryCached(*realOpt))
return std::nullopt;
return realOpt;
@@ -214,6 +222,7 @@ class DylibPathValidator {
private:
PathResolver &LibPathResolver;
+ LibraryPathCache &LibPathCache;
};
enum class SearchPathType {
@@ -232,6 +241,7 @@ class SearchPathResolver {
SearchPathResolver(const SearchPathConfig &Cfg,
StringRef PlaceholderPrefix = "")
: Kind(Cfg.type), PlaceholderPrefix(PlaceholderPrefix) {
+ Paths.reserve(Cfg.Paths.size());
for (auto &path : Cfg.Paths)
Paths.emplace_back(path.str());
}
@@ -456,10 +466,10 @@ class LibraryScanner {
LibraryManager &LibMgr;
ShouldScanFn ShouldScanCall;
- bool shouldScan(StringRef FilePath);
+ bool shouldScan(StringRef FilePath, bool IsResolvingDep = false);
Expected<LibraryDepsInfo> extractDeps(StringRef FilePath);
- void handleLibrary(StringRef FilePath, PathType K, int level = 1);
+ void handleLibrary(StringRef FilePath, PathType K, int level = 0);
void scanBaseDir(LibrarySearchPath *U);
};
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
index e634ecb3ef820..025da92d01f21 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp
@@ -49,113 +49,116 @@ void handleError(Error Err, StringRef context = "") {
}));
}
-bool ObjectFileLoader::isArchitectureCompatible(const object::ObjectFile &Obj) {
- Triple HostTriple(sys::getProcessTriple());
- Triple ObjTriple = Obj.makeTriple();
-
- LLVM_DEBUG({
- dbgs() << "Host triple: " << HostTriple.str()
- << ", Object triple: " << ObjTriple.str() << "\n";
- });
+static std::string getHostFileFormatName() {
+ static const std::string HostFormat = []() -> std::string {
+ auto ObjOrErr = object::ObjectFile::createObjectFile(
+ sys::fs::getMainExecutable(nullptr, nullptr));
- if (ObjTriple.getArch() != Triple::UnknownArch &&
- HostTriple.getArch() != ObjTriple.getArch())
- return false;
+ if (!ObjOrErr) {
+ return "Unknown";
+ }
- if (ObjTriple.getOS() != Triple::UnknownOS &&
- HostTriple.getOS() != ObjTriple.getOS())
- return false;
+ return (*ObjOrErr).getBinary()->getFileFormatName().str();
+ }();
- if (ObjTriple.getEnvironment() != Triple::UnknownEnvironment &&
- HostTriple.getEnvironment() != Triple::UnknownEnvironment &&
- HostTriple.getEnvironment() != ObjTriple.getEnvironment())
- return false;
+ return HostFormat;
+}
- return true;
+bool ObjectFileLoader::isArchitectureCompatible(const object::ObjectFile &Obj) {
+ return Obj.getFileFormatName() == getHostFileFormatName();
}
Expected<object::OwningBinary<object::ObjectFile>>
ObjectFileLoader::loadObjectFileWithOwnership(StringRef FilePath) {
LLVM_DEBUG(dbgs() << "ObjectFileLoader: Attempting to open file " << FilePath
<< "\n";);
- auto BinOrErr = object::createBinary(FilePath);
- if (!BinOrErr) {
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to open file " << FilePath
- << "\n";);
- return BinOrErr.takeError();
- }
+ if (auto ObjOrErr =
+ object::ObjectFile::createObjectFile(FilePath)) {
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: Successfully opened file " << FilePath
- << "\n";);
+ LLVM_DEBUG(dbgs() << "ObjectFileLoader: Detected object file\n";);
- auto OwningBin = BinOrErr->takeBinary();
- object::Binary *Bin = OwningBin.first.get();
+ auto OwningBin = std::move(*ObjOrErr);
- if (Bin->isArchive()) {
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: File is an archive, not supported: "
- << FilePath << "\n";);
- return createStringError(std::errc::invalid_argument,
- "Archive files are not supported: %s",
- FilePath.str().c_str());
- }
+ if (!isArchitectureCompatible(*OwningBin.getBinary())) {
+ LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture: "
+ << FilePath << "\n";);
+ return createStringError(inconvertibleErrorCode(),
+ "Incompatible object file: %s",
+ FilePath.str().c_str());
+ }
+
+ LLVM_DEBUG(dbgs() << "ObjectFileLoader: Object file is compatible\n";);
+ return std::move(OwningBin);
+ } else {
#if defined(__APPLE__)
- if (auto *UB = dyn_cast<object::MachOUniversalBinary>(Bin)) {
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: Detected Mach-O universal binary: "
+ consumeError(ObjOrErr.takeError());
+ auto BinOrErr = object::createBinary(FilePath);
+ if (!BinOrErr) {
+ LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to open file " << FilePath
+ << "\n";);
+ return BinOrErr.takeError();
+ }
+
+ LLVM_DEBUG(dbgs() << "ObjectFileLoader: Successfully opened file "
<< FilePath << "\n";);
- for (auto ObjForArch : UB->objects()) {
- auto ObjOrErr = ObjForArch.getAsObjectFile();
- if (!ObjOrErr) {
- LLVM_DEBUG(
- dbgs()
- << "ObjectFileLoader: Skipping invalid architecture slice\n";);
- consumeError(ObjOrErr.takeError());
- continue;
- }
+ auto OwningBin = BinOrErr->takeBinary();
+ object::Binary *Bin = OwningBin.first.get();
- std::unique_ptr<object::ObjectFile> Obj = std::move(ObjOrErr.get());
- if (isArchitectureCompatible(*Obj)) {
- LLVM_DEBUG(
- dbgs() << "ObjectFileLoader: Found compatible object slice\n";);
+ if (Bin->isArchive()) {
+ LLVM_DEBUG(
+ dbgs() << "ObjectFileLoader: File is an archive, not supported: "
+ << FilePath << "\n";);
+ return createStringError(std::errc::invalid_argument,
+ "Archive files are not supported: %s",
+ FilePath.str().c_str());
+ }
- return object::OwningBinary<object::ObjectFile>(
- std::move(Obj), std::move(OwningBin.second));
+ if (auto *UB = dyn_cast<object::MachOUniversalBinary>(Bin)) {
+ LLVM_DEBUG(
+ dbgs() << "ObjectFileLoader: Detected Mach-O universal binary: "
+ << FilePath << "\n";);
+ for (auto ObjForArch : UB->objects()) {
+ auto ObjOrErr = ObjForArch.getAsObjectFile();
+ if (!ObjOrErr) {
+ LLVM_DEBUG(dbgs() << "ObjectFileLoader: Skipping invalid "
+ "architecture slice\n";);
+
+ consumeError(ObjOrErr.takeError());
+ continue;
+ }
- } else {
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture "
- "slice skipped\n";);
+ std::unique_ptr<object::ObjectFile> Obj = std::move(ObjOrErr.get());
+ if (isArchitectureCompatible(*Obj)) {
+ LLVM_DEBUG(
+ dbgs() << "ObjectFileLoader: Found compatible object slice\n";);
+
+ return object::OwningBinary<object::ObjectFile>(
+ std::move(Obj), std::move(OwningBin.second));
+
+ } else {
+ LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture "
+ "slice skipped\n";);
+ }
}
+ LLVM_DEBUG(dbgs() << "ObjectFileLoader: No compatible slices found in "
+ "universal binary\n";);
+ return createStringError(inconvertibleErrorCode(),
+ "No compatible object found in fat binary: %s",
+ FilePath.str().c_str());
}
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: No compatible slices found in "
- "universal binary\n";);
- return createStringError(inconvertibleErrorCode(),
- "No compatible object found in fat binary: %s",
- FilePath.str().c_str());
- }
-#endif
-
- auto ObjOrErr =
- object::ObjectFile::createObjectFile(Bin->getMemoryBufferRef());
- if (!ObjOrErr) {
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to create object file\n";);
return ObjOrErr.takeError();
- }
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: Detected object file\n";);
-
- std::unique_ptr<object::ObjectFile> Obj = std::move(*ObjOrErr);
- if (!isArchitectureCompatible(*Obj)) {
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture: "
- << FilePath << "\n";);
- return createStringError(inconvertibleErrorCode(),
- "Incompatible object file: %s",
- FilePath.str().c_str());
+#else
+ LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to open file " << FilePath
+ << "\n";);
+ return ObjOrErr.takeError();
+#endif
}
- LLVM_DEBUG(dbgs() << "ObjectFileLoader: Object file is compatible\n";);
-
- return object::OwningBinary<object::ObjectFile>(std::move(Obj),
- std::move(OwningBin.second));
+ return createStringError(inconvertibleErrorCode(),
+ "Not a compatible object file : %s",
+ FilePath.str().c_str());
}
template <class ELFT>
@@ -271,6 +274,7 @@ bool DylibPathValidator::isSharedLibrary(StringRef Path) {
consumeError(ObjOrErr.takeError());
return false;
}
+
return isSharedLibraryObject(ObjOrErr.get());
}
@@ -957,7 +961,7 @@ Expected<LibraryDepsInfo> LibraryScanner::extractDeps(StringRef FilePath) {
FilePath.str().c_str());
}
-bool LibraryScanner::shouldScan(StringRef FilePath) {
+bool LibraryScanner::shouldScan(StringRef FilePath, bool IsResolvingDep) {
LLVM_DEBUG(dbgs() << "[shouldScan] Checking: " << FilePath << "\n";);
LibraryPathCache &Cache = ScanHelper.getCache();
@@ -968,13 +972,13 @@ bool LibraryScanner::shouldScan(StringRef FilePath) {
}
// [2] Already tracked in LibraryManager?
- if (LibMgr.hasLibrary(FilePath)) {
+ /*if (LibMgr.hasLibrary(FilePath)) {
LLVM_DEBUG(dbgs() << " -> Skipped: already tracked by LibraryManager.\n";);
return false;
- }
+ }*/
// [3] Skip if it's not a shared library.
- if (!DylibPathValidator::isSharedLibrary(FilePath)) {
+ if (!IsResolvingDep && !DylibPathValidator::isSharedLibrary(FilePath)) {
LLVM_DEBUG(dbgs() << " -> Skipped: not a shared library.\n";);
return false;
}
@@ -995,7 +999,7 @@ bool LibraryScanner::shouldScan(StringRef FilePath) {
void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {
LLVM_DEBUG(dbgs() << "LibraryScanner::handleLibrary: Scanning: " << FilePath
<< ", level=" << level << "\n";);
- if (!shouldScan(FilePath)) {
+ if (!shouldScan(FilePath, level > 0)) {
LLVM_DEBUG(dbgs() << " Skipped (shouldScan returned false): " << FilePath
<< "\n";);
return;
@@ -1060,7 +1064,8 @@ void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {
return;
}
- DylibPathValidator Validator(ScanHelper.getPathResolver());
+ DylibPathValidator Validator(ScanHelper.getPathResolver(),
+ ScanHelper.getCache());
DylibResolver Resolver(Validator);
Resolver.configure(FilePath,
{{Deps.rpath, SearchPathType::RPath},
@@ -1071,7 +1076,6 @@ void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {
auto DepFullOpt = Resolver.resolve(Dep);
if (!DepFullOpt) {
LLVM_DEBUG(dbgs() << " Failed to resolve dep: " << Dep << "\n";);
-
continue;
}
LLVM_DEBUG(dbgs() << " Resolved dep to: " << *DepFullOpt << "\n";);
@@ -1157,5 +1161,4 @@ void LibraryScanner::scanNext(PathType K, size_t BatchSize) {
scanBaseDir(const_cast<LibrarySearchPath *>(SP));
}
}
-
} // end namespace llvm::orc
diff --git a/llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp b/llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp
index 441344b281411..11be6be0f531f 100644
--- a/llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/LibraryResolverTest.cpp
@@ -619,7 +619,7 @@ TEST_F(LibraryResolverIT, ResolveFromUsrOrSystemPaths) {
auto LibPathCache = std::make_shared<LibraryPathCache>();
auto PResolver = std::make_shared<PathResolver>(LibPathCache);
- DylibPathValidator validator(*PResolver);
+ DylibPathValidator validator(*PResolver, *LibPathCache);
std::vector<std::string> Paths = {"/foo/bar/", "temp/foo", libdir("C"),
libdir("A"), libdir("B"), libdir("Z")};
@@ -671,7 +671,7 @@ TEST_F(LibraryResolverIT, ResolveViaLoaderPathAndRPathSubstitution) {
auto LibPathCache = std::make_shared<LibraryPathCache>();
auto PResolver = std::make_shared<PathResolver>(LibPathCache);
- DylibPathValidator validator(*PResolver);
+ DylibPathValidator validator(*PResolver, *LibPathCache);
std::vector<std::string> Paths = {"@loader_path/../A", "@loader_path/../B",
"@loader_path/../C", "@loader_path/../Z"};
@@ -715,9 +715,9 @@ TEST_F(LibraryResolverIT, ResolveViaLoaderPathAndRPathSubstitution) {
#if defined(__linux__)
TEST_F(LibraryResolverIT, ResolveViaOriginAndRPathSubstitution) {
auto LibPathCache = std::make_shared<LibraryPathCache>();
- auto PResolver = std::make_shared<PathResolver>(LibPathCache);
+ auto PResolver = std::make_shared<PathResolver>(LibPathCach);
- DylibPathValidator validator(*PResolver);
+ DylibPathValidator validator(*PResolver, *LibPathCache);
// On Linux, $ORIGIN works like @loader_path
std::vector<std::string> Paths = {"$ORIGIN/../A", "$ORIGIN/../B",
More information about the llvm-commits
mailing list