Skip to content

[REAPPLY][Clang-Repl] Add support for out-of-process execution. #110418 #144064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 28, 2025

Conversation

SahilPatidar
Copy link
Contributor

This PR introduces out-of-process (OOP) execution support for Clang-Repl. With this enhancement, two new flags, oop-executor and oop-executor-connect, are added to the Clang-Repl interface. These flags enable the launch of an external executor (llvm-jitlink-executor), which handles code execution in a separate process.

Copy link

github-actions bot commented Jun 13, 2025

✅ With the latest revision this PR passed the Python code formatter.

@SahilPatidar SahilPatidar requested a review from vgvassilev June 14, 2025 06:39
@vgvassilev
Copy link
Contributor

Looks a reasonable way forward. Is there a reason to be a draft?

@SahilPatidar SahilPatidar marked this pull request as ready for review June 16, 2025 05:05
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jun 16, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 16, 2025

@llvm/pr-subscribers-clang

Author: None (SahilPatidar)

Changes

This PR introduces out-of-process (OOP) execution support for Clang-Repl. With this enhancement, two new flags, oop-executor and oop-executor-connect, are added to the Clang-Repl interface. These flags enable the launch of an external executor (llvm-jitlink-executor), which handles code execution in a separate process.


Patch is 28.12 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/144064.diff

9 Files Affected:

  • (modified) clang/include/clang/Interpreter/Interpreter.h (+6-1)
  • (added) clang/include/clang/Interpreter/RemoteJITUtils.h (+38)
  • (modified) clang/lib/Interpreter/CMakeLists.txt (+1)
  • (modified) clang/lib/Interpreter/Interpreter.cpp (+29-8)
  • (added) clang/lib/Interpreter/RemoteJITUtils.cpp (+267)
  • (added) clang/test/Interpreter/out-of-process.cpp (+88)
  • (modified) clang/test/lit.cfg.py (+25)
  • (modified) clang/tools/clang-repl/CMakeLists.txt (+2)
  • (modified) clang/tools/clang-repl/ClangRepl.cpp (+122-1)
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index f8663e3193a18..78dff1165dcf5 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -20,6 +20,7 @@
 
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
 #include "llvm/Support/Error.h"
 #include <memory>
@@ -136,10 +137,14 @@ class Interpreter {
 public:
   virtual ~Interpreter();
   static llvm::Expected<std::unique_ptr<Interpreter>>
-  create(std::unique_ptr<CompilerInstance> CI);
+  create(std::unique_ptr<CompilerInstance> CI,
+         std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr);
   static llvm::Expected<std::unique_ptr<Interpreter>>
   createWithCUDA(std::unique_ptr<CompilerInstance> CI,
                  std::unique_ptr<CompilerInstance> DCI);
+  static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+  createLLJITBuilder(std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
+                     llvm::StringRef OrcRuntimePath);
   const ASTContext &getASTContext() const;
   ASTContext &getASTContext();
   const CompilerInstance *getCompilerInstance() const;
diff --git a/clang/include/clang/Interpreter/RemoteJITUtils.h b/clang/include/clang/Interpreter/RemoteJITUtils.h
new file mode 100644
index 0000000000000..8705a3b1f669d
--- /dev/null
+++ b/clang/include/clang/Interpreter/RemoteJITUtils.h
@@ -0,0 +1,38 @@
+//===-- RemoteJITUtils.h - Utilities for remote-JITing ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Utilities for ExecutorProcessControl-based remote JITing with Orc and
+// JITLink.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
+#define LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/Layer.h"
+#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
+#include "llvm/Support/Error.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
+launchExecutor(llvm::StringRef ExecutablePath, bool UseSharedMemory,
+               llvm::StringRef SlabAllocateSizeString);
+
+/// Create a JITLinkExecutor that connects to the given network address
+/// through a TCP socket. A valid NetworkAddress provides hostname and port,
+/// e.g. localhost:20000.
+llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
+connectTCPSocket(llvm::StringRef NetworkAddress, bool UseSharedMemory,
+                 llvm::StringRef SlabAllocateSizeString);
+
+#endif // LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index bf70cdfbee01e..38cf139fa86a6 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -27,6 +27,7 @@ add_clang_library(clangInterpreter
   Interpreter.cpp
   InterpreterValuePrinter.cpp
   InterpreterUtils.cpp
+  RemoteJITUtils.cpp
   Value.cpp
   ${WASM_SRC}
   PARTIAL_SOURCES_INTENDED
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 84feff82c63a7..30051a24662c6 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -46,6 +46,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Serialization/ObjectFilePCHContainerReader.h"
 #include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Errc.h"
@@ -455,10 +456,11 @@ const char *const Runtimes = R"(
 )";
 
 llvm::Expected<std::unique_ptr<Interpreter>>
-Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
+Interpreter::create(std::unique_ptr<CompilerInstance> CI,
+                    std::unique_ptr<llvm::orc::LLJITBuilder> JB) {
   llvm::Error Err = llvm::Error::success();
-  auto Interp =
-      std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err));
+  auto Interp = std::unique_ptr<Interpreter>(
+      new Interpreter(std::move(CI), Err, JB ? std::move(JB) : nullptr));
   if (Err)
     return std::move(Err);
 
@@ -617,6 +619,25 @@ createJITTargetMachineBuilder(const std::string &TT) {
   return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT));
 }
 
+llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+Interpreter::createLLJITBuilder(
+    std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
+    llvm::StringRef OrcRuntimePath) {
+  const std::string &TT = EPC->getTargetTriple().getTriple();
+  auto JTMB = createJITTargetMachineBuilder(TT);
+  if (!JTMB)
+    return JTMB.takeError();
+  auto JB = IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB));
+  if (!JB)
+    return JB.takeError();
+
+  (*JB)->setExecutorProcessControl(std::move(EPC));
+  (*JB)->setPlatformSetUp(
+      llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str()));
+
+  return std::move(*JB);
+}
+
 llvm::Error Interpreter::CreateExecutor() {
   if (IncrExecutor)
     return llvm::make_error<llvm::StringError>("Operation failed. "
@@ -756,11 +777,11 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
   if (!EE)
     return EE.takeError();
 
-  auto &DL = EE->getDataLayout();
-
-  if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load(
-          name, DL.getGlobalPrefix()))
-    EE->getMainJITDylib().addGenerator(std::move(*DLSG));
+  if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load(
+          EE->getExecutionSession(), name))
+    // FIXME: Eventually we should put each library in its own JITDylib and
+    //        turn off process symbols by default.
+    EE->getProcessSymbolsJITDylib()->addGenerator(std::move(*DLSG));
   else
     return DLSG.takeError();
 #endif
diff --git a/clang/lib/Interpreter/RemoteJITUtils.cpp b/clang/lib/Interpreter/RemoteJITUtils.cpp
new file mode 100644
index 0000000000000..24a5f729f2dcb
--- /dev/null
+++ b/clang/lib/Interpreter/RemoteJITUtils.cpp
@@ -0,0 +1,267 @@
+//===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// FIXME: Unify this code with similar functionality in llvm-jitlink.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Interpreter/RemoteJITUtils.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
+#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
+#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
+#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+#ifdef LLVM_ON_UNIX
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif // LLVM_ON_UNIX
+
+using namespace llvm;
+using namespace llvm::orc;
+
+Expected<uint64_t> getSlabAllocSize(StringRef SizeString) {
+  SizeString = SizeString.trim();
+
+  uint64_t Units = 1024;
+
+  if (SizeString.ends_with_insensitive("kb"))
+    SizeString = SizeString.drop_back(2).rtrim();
+  else if (SizeString.ends_with_insensitive("mb")) {
+    Units = 1024 * 1024;
+    SizeString = SizeString.drop_back(2).rtrim();
+  } else if (SizeString.ends_with_insensitive("gb")) {
+    Units = 1024 * 1024 * 1024;
+    SizeString = SizeString.drop_back(2).rtrim();
+  }
+
+  uint64_t SlabSize = 0;
+  if (SizeString.getAsInteger(10, SlabSize))
+    return make_error<StringError>("Invalid numeric format for slab size",
+                                   inconvertibleErrorCode());
+
+  return SlabSize * Units;
+}
+
+Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>>
+createSharedMemoryManager(SimpleRemoteEPC &SREPC,
+                          StringRef SlabAllocateSizeString) {
+  SharedMemoryMapper::SymbolAddrs SAs;
+  if (auto Err = SREPC.getBootstrapSymbols(
+          {{SAs.Instance, rt::ExecutorSharedMemoryMapperServiceInstanceName},
+           {SAs.Reserve,
+            rt::ExecutorSharedMemoryMapperServiceReserveWrapperName},
+           {SAs.Initialize,
+            rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName},
+           {SAs.Deinitialize,
+            rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName},
+           {SAs.Release,
+            rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName}}))
+    return std::move(Err);
+
+#ifdef _WIN32
+  size_t SlabSize = 1024 * 1024;
+#else
+  size_t SlabSize = 1024 * 1024 * 1024;
+#endif
+
+  if (!SlabAllocateSizeString.empty()) {
+    if (auto S = getSlabAllocSize(SlabAllocateSizeString))
+      SlabSize = *S;
+    else
+      return S.takeError();
+  }
+
+  return MapperJITLinkMemoryManager::CreateWithMapper<SharedMemoryMapper>(
+      SlabSize, SREPC, SAs);
+}
+
+Expected<std::unique_ptr<SimpleRemoteEPC>>
+launchExecutor(StringRef ExecutablePath, bool UseSharedMemory,
+               llvm::StringRef SlabAllocateSizeString) {
+#ifndef LLVM_ON_UNIX
+  // FIXME: Add support for Windows.
+  return make_error<StringError>("-" + ExecutablePath +
+                                     " not supported on non-unix platforms",
+                                 inconvertibleErrorCode());
+#elif !LLVM_ENABLE_THREADS
+  // Out of process mode using SimpleRemoteEPC depends on threads.
+  return make_error<StringError>(
+      "-" + ExecutablePath +
+          " requires threads, but LLVM was built with "
+          "LLVM_ENABLE_THREADS=Off",
+      inconvertibleErrorCode());
+#else
+
+  if (!sys::fs::can_execute(ExecutablePath))
+    return make_error<StringError>(
+        formatv("Specified executor invalid: {0}", ExecutablePath),
+        inconvertibleErrorCode());
+
+  constexpr int ReadEnd = 0;
+  constexpr int WriteEnd = 1;
+
+  // Pipe FDs.
+  int ToExecutor[2];
+  int FromExecutor[2];
+
+  pid_t ChildPID;
+
+  // Create pipes to/from the executor..
+  if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
+    return make_error<StringError>("Unable to create pipe for executor",
+                                   inconvertibleErrorCode());
+
+  ChildPID = fork();
+
+  if (ChildPID == 0) {
+    // In the child...
+
+    // Close the parent ends of the pipes
+    close(ToExecutor[WriteEnd]);
+    close(FromExecutor[ReadEnd]);
+
+    // Execute the child process.
+    std::unique_ptr<char[]> ExecutorPath, FDSpecifier;
+    {
+      ExecutorPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
+      strcpy(ExecutorPath.get(), ExecutablePath.data());
+
+      std::string FDSpecifierStr("filedescs=");
+      FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
+      FDSpecifierStr += ',';
+      FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
+      FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
+      strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
+    }
+
+    char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
+    int RC = execvp(ExecutorPath.get(), Args);
+    if (RC != 0) {
+      errs() << "unable to launch out-of-process executor \""
+             << ExecutorPath.get() << "\"\n";
+      exit(1);
+    }
+  }
+  // else we're the parent...
+
+  // Close the child ends of the pipes
+  close(ToExecutor[ReadEnd]);
+  close(FromExecutor[WriteEnd]);
+
+  auto S = SimpleRemoteEPC::Setup();
+  if (UseSharedMemory)
+    S.CreateMemoryManager = [SlabAllocateSizeString](SimpleRemoteEPC &EPC) {
+      return createSharedMemoryManager(EPC, SlabAllocateSizeString);
+    };
+
+  return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
+      std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
+      std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
+#endif
+}
+
+#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
+
+static Expected<int> connectTCPSocketImpl(std::string Host,
+                                          std::string PortStr) {
+  addrinfo *AI;
+  addrinfo Hints{};
+  Hints.ai_family = AF_INET;
+  Hints.ai_socktype = SOCK_STREAM;
+  Hints.ai_flags = AI_NUMERICSERV;
+
+  if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
+    return make_error<StringError>(
+        formatv("address resolution failed ({0})", gai_strerror(EC)),
+        inconvertibleErrorCode());
+  // Cycle through the returned addrinfo structures and connect to the first
+  // reachable endpoint.
+  int SockFD;
+  addrinfo *Server;
+  for (Server = AI; Server != nullptr; Server = Server->ai_next) {
+    // socket might fail, e.g. if the address family is not supported. Skip to
+    // the next addrinfo structure in such a case.
+    if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
+      continue;
+
+    // If connect returns null, we exit the loop with a working socket.
+    if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
+      break;
+
+    close(SockFD);
+  }
+  freeaddrinfo(AI);
+
+  // If we reached the end of the loop without connecting to a valid endpoint,
+  // dump the last error that was logged in socket() or connect().
+  if (Server == nullptr)
+    return make_error<StringError>("invalid hostname",
+                                   inconvertibleErrorCode());
+
+  return SockFD;
+}
+#endif
+
+Expected<std::unique_ptr<SimpleRemoteEPC>>
+connectTCPSocket(StringRef NetworkAddress, bool UseSharedMemory,
+                 llvm::StringRef SlabAllocateSizeString) {
+#ifndef LLVM_ON_UNIX
+  // FIXME: Add TCP support for Windows.
+  return make_error<StringError>("-" + NetworkAddress +
+                                     " not supported on non-unix platforms",
+                                 inconvertibleErrorCode());
+#elif !LLVM_ENABLE_THREADS
+  // Out of process mode using SimpleRemoteEPC depends on threads.
+  return make_error<StringError>(
+      "-" + NetworkAddress +
+          " requires threads, but LLVM was built with "
+          "LLVM_ENABLE_THREADS=Off",
+      inconvertibleErrorCode());
+#else
+
+  auto CreateErr = [NetworkAddress](Twine Details) {
+    return make_error<StringError>(
+        formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
+                Details),
+        inconvertibleErrorCode());
+  };
+
+  StringRef Host, PortStr;
+  std::tie(Host, PortStr) = NetworkAddress.split(':');
+  if (Host.empty())
+    return CreateErr("Host name for -" + NetworkAddress + " can not be empty");
+  if (PortStr.empty())
+    return CreateErr("Port number in -" + NetworkAddress + " can not be empty");
+  int Port = 0;
+  if (PortStr.getAsInteger(10, Port))
+    return CreateErr("Port number '" + PortStr + "' is not a valid integer");
+
+  Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
+  if (!SockFD)
+    return SockFD.takeError();
+
+  auto S = SimpleRemoteEPC::Setup();
+  if (UseSharedMemory)
+    S.CreateMemoryManager = [SlabAllocateSizeString](SimpleRemoteEPC &EPC) {
+      return createSharedMemoryManager(EPC, SlabAllocateSizeString);
+    };
+
+  return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
+      std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
+      std::move(S), *SockFD, *SockFD);
+#endif
+}
diff --git a/clang/test/Interpreter/out-of-process.cpp b/clang/test/Interpreter/out-of-process.cpp
new file mode 100644
index 0000000000000..6922ca6e82053
--- /dev/null
+++ b/clang/test/Interpreter/out-of-process.cpp
@@ -0,0 +1,88 @@
+// REQUIRES: host-supports-jit,  host-supports-out-of-process-jit, x86_64-linux
+
+// RUN: cat %s | clang-repl -oop-executor -orc-runtime | FileCheck %s
+
+extern "C" int printf(const char *, ...);
+
+int intVar = 0;
+double doubleVar = 3.14;
+%undo
+double doubleVar = 2.71;
+
+auto r1 = printf("intVar = %d\n", intVar);
+// CHECK: intVar = 0
+auto r2 = printf("doubleVar = %.2f\n", doubleVar);
+// CHECK: doubleVar = 2.71
+
+// Test redefinition with inline and static functions.
+int add(int a, int b, int c) { return a + b + c; }
+%undo  // Revert to the initial version of add
+inline int add(int a, int b) { return a + b; }
+
+auto r3 = printf("add(1, 2) = %d\n", add(1, 2));
+// CHECK-NEXT: add(1, 2) = 3
+
+// Test inline and lambda functions with variations.
+inline int square(int x) { return x * x; }
+auto lambdaSquare = [](int x) { return x * x; };
+auto lambdaMult = [](int a, int b) { return a * b; };
+
+auto r4 = printf("square(4) = %d\n", square(4));
+// CHECK-NEXT: square(4) = 16
+auto lambda_r1 = printf("lambdaSquare(5) = %d\n", lambdaSquare(5));
+// CHECK-NEXT: lambdaSquare(5) = 25
+auto lambda_r2 = printf("lambdaMult(2, 3) = %d\n", lambdaMult(2, 3));
+// CHECK-NEXT: lambdaMult(2, 3) = 6
+
+%undo  // Undo previous lambda assignments
+auto lambda_r3 = lambdaMult(3, 4);  // Should fail or revert to the original lambda
+
+// Test weak and strong symbol linkage.
+int __attribute__((weak)) weakFunc() { return 42; }
+int strongFunc() { return 100; }
+%undo  // Revert the weak function
+
+auto r5 = printf("weakFunc() = %d\n", weakFunc());
+// CHECK: weakFunc() = 42
+auto r6 = printf("strongFunc() = %d\n", strongFunc());
+// CHECK-NEXT: strongFunc() = 100
+
+// Weak variable linkage with different types.
+int varA = 20;
+static __typeof(varA) weakVarA __attribute__((__weakref__("varA")));
+char charVar = 'c';
+static __typeof(charVar) weakCharVar __attribute__((__weakref__("charVar")));
+auto r7 = printf("weakVarA = %d\n", weakVarA);
+// CHECK: weakVarA = 20
+auto r8 = printf("weakCharVar = %c\n", weakCharVar);
+// CHECK-NEXT: weakCharVar = c
+
+// Test complex lambdas with captures.
+int captureVar = 5;
+auto captureLambda = [](int x) { return x + captureVar; };
+int result1 = captureLambda(10);
+%undo  // Undo capture lambda
+
+auto r9 = printf("captureLambda(10) = %d\n", result1);
+// CHECK: captureLambda(10) = 15
+
+// Multiline statement test with arithmetic operations.
+int sum = \
+  5 + \
+  10;
+int prod = sum * 2;
+auto r10 = printf("sum = %d, prod = %d\n", sum, prod);
+// CHECK: sum = 15, prod = 30
+
+// Test multiline functions and macro behavior.
+#define MULTIPLY(a, b) ((a) * (b))
+
+int complexFunc(int x) \
+{ \
+  return MULTIPLY(x, 2) + x; \
+}
+
+auto r11 = printf("complexFunc(5) = %d\n", complexFunc(5));
+// CHECK: complexFunc(5) = 15
+
+%quit
\ No newline at end of file
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index 2b35bb5dcbdaf..0f82f2089ac58 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -116,6 +116,28 @@
 if config.clang_examples:
     config.available_features.add("examples")
 
+def have_host_out_of_process_jit_feature_support():
+    clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir)
+
+    if not clang_repl_exe:
+        return False
+
+    testcode = b"\n".join([b"int i = 0;", b"%quit"])
+
+    try:
+        clang_repl_cmd = subprocess.run(
+            [clang_repl_exe, "-orc-runtime", "-oop-executor"],
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            input=testcode,
+        )
+    except OSError:
+        return False
+
+    if clang_repl_cmd.returncode == 0:
+        return True
+
+    return False
 
 def have_host_jit_feature_support(feature_name):
     clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir)
@@ -169,6 +191,9 @@ def have_host_clang_repl_cuda():
     if have_host_clang_repl_cuda():
         config.available_features.add('host-supports-cuda')
 
+    if have_host_out_of_process_jit_feature_support():
+        config.available_features.add("host-supports-out-of-process-jit")
+
 if confi...
[truncated]

@SahilPatidar
Copy link
Contributor Author

Is there a reason to be a draft?

Just to make sure there were no build failures.

if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load(
name, DL.getGlobalPrefix()))
EE->getMainJITDylib().addGenerator(std::move(*DLSG));
if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we spell out the type here?

#endif

if (!SlabAllocateSizeString.empty()) {
if (auto S = getSlabAllocSize(SlabAllocateSizeString))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we spell out the type?

close(ToExecutor[ReadEnd]);
close(FromExecutor[WriteEnd]);

auto S = SimpleRemoteEPC::Setup();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise.

@SahilPatidar
Copy link
Contributor Author

Will do.

Copy link
Contributor

@vgvassilev vgvassilev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modulo that one comment that's left -- LGTM!

@vgvassilev vgvassilev merged commit 3f53155 into llvm:main Jun 28, 2025
7 checks passed
rlavaee pushed a commit to rlavaee/llvm-project that referenced this pull request Jul 1, 2025
…110418 (llvm#144064)

This PR introduces out-of-process (OOP) execution support for
Clang-Repl. With this enhancement, two new flags, oop-executor and
oop-executor-connect, are added to the Clang-Repl interface. These flags
enable the launch of an external executor (llvm-jitlink-executor), which
handles code execution in a separate process.
rlavaee pushed a commit to rlavaee/llvm-project that referenced this pull request Jul 1, 2025
…110418 (llvm#144064)

This PR introduces out-of-process (OOP) execution support for
Clang-Repl. With this enhancement, two new flags, oop-executor and
oop-executor-connect, are added to the Clang-Repl interface. These flags
enable the launch of an external executor (llvm-jitlink-executor), which
handles code execution in a separate process.
@anutosh491 anutosh491 added this to the LLVM 21.x Release milestone Jul 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants