llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Vassil Vassilev (vgvassilev)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/175435.diff


3 Files Affected:

- (modified) clang/lib/Interpreter/Interpreter.cpp (+44-60) 
- (modified) clang/tools/clang-repl/ClangRepl.cpp (+2-2) 
- (modified) clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp 
(+13-9) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/175435
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to