https://github.com/vgvassilev created 
https://github.com/llvm/llvm-project/pull/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.

>From 4c7f639985f3492600dbf40cb6b08951892db4c6 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <[email protected]>
Date: Sun, 11 Jan 2026 11:23:51 +0000
Subject: [PATCH] [clang-repl] Fix OrcRuntime lookup for Solaris and unit
 tests.

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.
---
 clang/lib/Interpreter/Interpreter.cpp         | 104 ++++++++----------
 clang/tools/clang-repl/ClangRepl.cpp          |   4 +-
 .../OutOfProcessInterpreterTests.cpp          |  22 ++--
 3 files changed, 59 insertions(+), 71 deletions(-)

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

Reply via email to