Author: Vassil Vassilev Date: 2026-01-11T16:52:58+02:00 New Revision: f25ddef03618e263c2a545b3a870b592fa9532a7
URL: https://github.com/llvm/llvm-project/commit/f25ddef03618e263c2a545b3a870b592fa9532a7 DIFF: https://github.com/llvm/llvm-project/commit/f25ddef03618e263c2a545b3a870b592fa9532a7.diff LOG: [clang-repl] Fix OrcRuntime lookup for Solaris and unit tests. (#175435) The out-of-process execution in the interpreter depends on the orc runtime. It is generally easy to discover as it is in the clang runtime path. However, the clang runtime path is relative to clang's resource directory which is relative to the clang binary. That does not work well if clang is linked into a different binary which can be in a random place in the build directory structure. This patch performs a conservative approach to detect the common directory structure and correctly infer the paths. That fixes the out-of-process execution unittests. The patch also contains a small adjustment for solaris. Another take on trying to fix the issue uncovered by #175322. Added: Modified: clang/lib/Interpreter/Interpreter.cpp clang/tools/clang-repl/ClangRepl.cpp clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp Removed: ################################################################################ diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 763d298b052f2..f69c57fe48001 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -275,16 +275,14 @@ llvm::Error IncrementalExecutorBuilder::UpdateOrcRuntimePath( if (!IsOutOfProcess) return llvm::Error::success(); - // Candidate runtime filenames to look for (tweak as appropriate). static constexpr std::array<const char *, 3> OrcRTLibNames = { "liborc_rt.a", "liborc_rt_osx.a", "liborc_rt-x86_64.a", }; - // Return the first file found inside 'Base' (Base may be a directory). auto findInDir = [&](llvm::StringRef Base) -> std::optional<std::string> { - if (Base.empty()) + if (Base.empty() || !llvm::sys::fs::exists(Base)) return std::nullopt; for (const char *LibName : OrcRTLibNames) { llvm::SmallString<256> Candidate(Base); @@ -296,74 +294,60 @@ llvm::Error IncrementalExecutorBuilder::UpdateOrcRuntimePath( }; const clang::driver::Driver &D = C.getDriver(); - + const clang::driver::ToolChain &TC = C.getDefaultToolChain(); llvm::SmallVector<std::string, 8> triedPaths; - // Prefer Driver::ResourceDir-derived locations: - // ResourceDir is typically: <prefix>/lib/clang/<version> - if (!D.ResourceDir.empty()) { - llvm::SmallString<256> Resource(D.ResourceDir); - - // Directly searching ResourceDir is cheap and sometimes sufficient. - if (auto F = findInDir(Resource)) { - OrcRuntimePath = *F; - return llvm::Error::success(); - } - triedPaths.emplace_back(std::string(Resource.str())); - - // Build <prefix>/lib/clang/<version>/lib. Resource already contains - // .../clang/<version>) - llvm::SmallString<256> ClangLibDir(Resource); - // ClangLibDir currently: <prefix>/lib/clang/<version> - // We want: <prefix>/lib/clang/<version>/lib - llvm::sys::path::append(ClangLibDir, "lib"); - if (auto F = findInDir(ClangLibDir)) { - OrcRuntimePath = *F; - return llvm::Error::success(); - } - triedPaths.emplace_back(std::string(ClangLibDir.str())); - - // Walk up to <prefix>/lib and search there and common variants. - llvm::SmallString<256> PrefixLib = Resource; - llvm::sys::path::remove_filename(PrefixLib); // remove <version> - llvm::sys::path::remove_filename(PrefixLib); // remove clang - if (!PrefixLib.empty()) { - if (auto F = findInDir(PrefixLib)) { - OrcRuntimePath = *F; - return llvm::Error::success(); + llvm::SmallString<256> Resource(D.ResourceDir); + if (llvm::sys::fs::exists(Resource)) { + // Ask the ToolChain for its runtime paths first (most authoritative). + for (auto RuntimePath : + {TC.getRuntimePath(), std::make_optional(TC.getCompilerRTPath())}) { + if (RuntimePath) { + if (auto Found = findInDir(*RuntimePath)) { + OrcRuntimePath = *Found; + return llvm::Error::success(); + } + triedPaths.emplace_back(*RuntimePath); } - triedPaths.emplace_back(std::string(PrefixLib.str())); - - // Also check <prefix>/<libdir_basename>/clang/<version>/lib if present in - // this environment. We extract version from the original ResourceDir - // filename (the '<version>' component). - llvm::SmallString<64> Version = - llvm::sys::path::filename(llvm::StringRef(Resource)); - llvm::SmallString<256> FormalClangLib = PrefixLib; - llvm::sys::path::append(FormalClangLib, "lib", "clang", Version, "lib"); - if (auto F = findInDir(FormalClangLib)) { + } + + // Check ResourceDir and ResourceDir/lib + for (auto P : {Resource.str().str(), (Resource + "/lib").str()}) { + if (auto F = findInDir(P)) { OrcRuntimePath = *F; return llvm::Error::success(); } - triedPaths.emplace_back(std::string(FormalClangLib.str())); + triedPaths.emplace_back(P); } - } - - // ToolChain runtime/compiler-rt locations (if available). - const clang::driver::ToolChain &TC = C.getDefaultToolChain(); - for (auto RuntimePath : - {TC.getRuntimePath(), std::make_optional(TC.getCompilerRTPath())}) { - if (RuntimePath && TC.getVFS().exists(*RuntimePath)) { - if (auto Found = findInDir(*RuntimePath)) { - OrcRuntimePath = *Found; - return llvm::Error::success(); - } else { - triedPaths.emplace_back(*RuntimePath); + } else { + // The binary was misplaced. Generic Backward Search (Climbing the tree) + // This allows unit tests in tools/clang/unittests to find the real lib/ + llvm::SmallString<256> Cursor = Resource; + // ResourceDir-derived locations + llvm::StringRef Version = llvm::sys::path::filename(Resource); + llvm::StringRef OSName = TC.getOSLibName(); + while (llvm::sys::path::has_parent_path(Cursor)) { + Cursor = llvm::sys::path::parent_path(Cursor).str(); + // At each level, try standard relative layouts + for (auto Rel : + {(llvm::Twine("lib/clang/") + Version + "/lib/" + OSName).str(), + (llvm::Twine("lib/clang/") + Version + "/lib").str(), + (llvm::Twine("lib/") + OSName).str(), std::string("lib/clang")}) { + llvm::SmallString<256> Candidate = Cursor; + llvm::sys::path::append(Candidate, Rel); + if (auto F = findInDir(Candidate)) { + OrcRuntimePath = *F; + return llvm::Error::success(); + } + triedPaths.emplace_back(std::string(Candidate.str())); } + // Stop if we hit the root or go too far (safety check) + if (triedPaths.size() > 32) + break; } } - // If we reached here, nothing was found. Build a helpful error string. + // Build a helpful error string if everything failed. std::string Joined; for (size_t i = 0; i < triedPaths.size(); ++i) { if (i) diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index e94749555ad1a..95786d688b76e 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -288,6 +288,8 @@ int main(int argc, const char **argv) { return 0; } + ExitOnErr(sanitizeOopArguments(argv[0])); + clang::IncrementalCompilerBuilder CB; CB.SetCompilerArgs(ClangArgv); @@ -320,8 +322,6 @@ int main(int argc, const char **argv) { DeviceCI = ExitOnErr(CB.CreateCudaDevice()); } - ExitOnErr(sanitizeOopArguments(argv[0])); - // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It // can replace the boilerplate code for creation of the compiler instance. std::unique_ptr<clang::CompilerInstance> CI; diff --git a/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp b/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp index d33005244d8da..225d6c8c66cab 100644 --- a/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp +++ b/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp @@ -102,6 +102,15 @@ static std::string getExecutorPath() { return ExecutorPath.str().str(); } +class OutOfProcessInterpreterTest : public InterpreterTestBase { +protected: + static bool HostSupportsOutOfProcessJIT() { + if (!InterpreterTestBase::HostSupportsJIT()) + return false; + return !getExecutorPath().empty(); + } +}; + struct OutOfProcessInterpreterInfo { std::string OrcRuntimePath; std::unique_ptr<Interpreter> Interpreter; @@ -162,8 +171,8 @@ static size_t DeclsSize(TranslationUnitDecl *PTUDecl) { return std::distance(PTUDecl->decls().begin(), PTUDecl->decls().end()); } -TEST_F(InterpreterTestBase, SanityWithRemoteExecution) { - if (!HostSupportsJIT()) +TEST_F(OutOfProcessInterpreterTest, SanityWithRemoteExecution) { + if (!HostSupportsOutOfProcessJIT()) GTEST_SKIP(); auto io_ctx = std::make_shared<IOContext>(); @@ -174,11 +183,6 @@ TEST_F(InterpreterTestBase, SanityWithRemoteExecution) { Interpreter *Interp = Info.Interpreter.get(); ASSERT_TRUE(Interp); - std::string ExecutorPath = getExecutorPath(); - if (!llvm::sys::fs::exists(Info.OrcRuntimePath) || - !llvm::sys::fs::exists(ExecutorPath)) - GTEST_SKIP(); - using PTU = PartialTranslationUnit; PTU &R1(cantFail(Interp->Parse("void g(); void g() {}"))); EXPECT_EQ(2U, DeclsSize(R1.TUPart)); @@ -192,8 +196,8 @@ TEST_F(InterpreterTestBase, SanityWithRemoteExecution) { EXPECT_NE(std::string::npos, captured_stdout.find("CustomizeFork executed")); } -TEST_F(InterpreterTestBase, FindRuntimeInterface) { - if (!HostSupportsJIT()) +TEST_F(OutOfProcessInterpreterTest, FindRuntimeInterface) { + if (!HostSupportsOutOfProcessJIT()) GTEST_SKIP(); // make a fresh io context for this test _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
