This is an automated email from the ASF dual-hosted git repository.

cyx-6 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new 329ec838 [ORCJIT] Embed liborc_rt into the extension and add orc_rt 
selector (#660)
329ec838 is described below

commit 329ec838400c18d364062f5ec75dbe4263461d37
Author: Yaxing Cai <[email protected]>
AuthorDate: Sat Jul 11 15:33:27 2026 +0800

    [ORCJIT] Embed liborc_rt into the extension and add orc_rt selector (#660)
    
    ## Motivation
    
    The ORC runtime shipped as a separate `liborc_rt*.a` data file next to
    the extension `.so` and was resolved at startup via a filesystem glob
    (through the `tvm_ffi_orcjit.DefaultOrcRuntimePath` Python hook). That
    couples two artifacts, lets the runtime be lost/relocated by
    repackaging, and does a filesystem glob to find its own runtime on first
    session.
    
    ## What this does
    
    Bakes `liborc_rt.a` into the extension `.so`'s `.rodata` via a generated
    `.incbin` stub (`orc_rt_embed.S.in`) and hands the bytes to
    `ExecutorNativePlatform` as a **zero-copy** `MemoryBuffer`. The wheel
    now ships **one artifact**, startup does **no path lookup**, and the
    runtime cannot be separated from the extension. The embedded
    `start`/`end` symbols are `.hidden`, so they add no interposition
    surface and don't affect the existing `--exclude-libs` symbol hiding.
    
    This is **Solution A** of the embedded-orcrt design.
    
    ### `default_session()`
    
    Always uses the embedded runtime and is **deliberately not
    user-configurable**: a shared process-wide singleton with a hidden
    runtime knob would let whichever caller runs first silently pick the
    platform for everyone. The old `DefaultOrcRuntimePath` discovery hook is
    dropped.
    
    ### User-created `ExecutionSession` — new `orc_rt` selector
    
    Renamed `orc_rt_path` → `orc_rt`, now a 4-state selector:
    
    | `orc_rt=` | meaning |
    |---|---|
    | `"auto"` (default) | the embedded runtime |
    | `str` / `Path` | a custom `liborc_rt` archive on disk |
    | `bytes` | a custom `liborc_rt` archive in memory |
    | `None` | no ORC platform |
    
    C++ takes `Optional<Variant<String, Bytes>>`; empty `String` ==
    `"auto"`, `nullopt` == `None`. Linux/ELF only, matching where the ORC
    platform is wired up today; macOS/Windows ignore the selector as before.
    A custom runtime must match the LLVM/compiler-rt the extension was built
    against.
    
    ## Testing
    
    - New `orc_rt` tests: custom path (str + `Path`), in-memory bytes,
    `None` (no platform), and bad-type rejection. Path/bytes cases are
    `@skipif` non-Linux (the selector only takes effect on ELF).
    - Verified locally (aarch64 Linux, LLVM 22): the `.so` carries the
    hidden `orc_rt_archive` symbols in `.rodata`, absent from the dynamic
    table; no `liborc_rt*.a` bundled; C++ exception unwinding across a JIT
    frame works under `auto`/path/bytes (proving `ELFNixPlatform` is
    genuinely installed).
    - Full addon suite: 164 passed, 11 skipped. clang-format / ruff /
    cmake-format / ASF-header all clean.
---
 addons/tvm_ffi_orcjit/CMakeLists.txt               | 23 +++--
 .../python/tvm_ffi_orcjit/session.py               | 88 +++++++++-----------
 addons/tvm_ffi_orcjit/src/ffi/orc_rt_embed.S.in    | 34 ++++++++
 addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc      |  4 +-
 addons/tvm_ffi_orcjit/src/ffi/orcjit_session.cc    | 97 +++++++++++++++-------
 addons/tvm_ffi_orcjit/src/ffi/orcjit_session.h     | 32 +++++--
 .../tests/test_session_load_module.py              | 61 +++++++++++++-
 7 files changed, 239 insertions(+), 100 deletions(-)

diff --git a/addons/tvm_ffi_orcjit/CMakeLists.txt 
b/addons/tvm_ffi_orcjit/CMakeLists.txt
index fb33741f..bdb2952f 100644
--- a/addons/tvm_ffi_orcjit/CMakeLists.txt
+++ b/addons/tvm_ffi_orcjit/CMakeLists.txt
@@ -170,10 +170,12 @@ if (CMAKE_SYSTEM_NAME MATCHES 
"Linux|Android|FreeBSD|NetBSD|OpenBSD" AND CMAKE_C
   target_link_options(tvm_ffi_orcjit PRIVATE 
"-Wl,--exclude-libs=${_exclude_libs_arg}")
 endif ()
 
-# ---- Find and bundle liborc_rt ----
-# Platform notes:
+# ---- Find and embed liborc_rt (Linux/ELF only) ----
+# The archive bytes are baked into the .so's .rodata (see 
src/ffi/orc_rt_embed.S.in) and handed to
+# ExecutorNativePlatform as a zero-copy MemoryBuffer at runtime, so the wheel 
ships one artifact
+# with no on-disk path lookup. Platform notes:
 #
-# * Linux: liborc_rt is used for ExecutorNativePlatform → ELFNixPlatform.
+# * Linux: liborc_rt drives ExecutorNativePlatform → ELFNixPlatform.
 # * macOS: MachOPlatform is skipped to sidestep the compact-unwind delta bug 
(see
 #   src/ffi/orcjit_session.cc); liborc_rt is unused.
 # * Windows: COFFPlatform is not hooked up; liborc_rt is unused.
@@ -193,9 +195,18 @@ if (NOT APPLE AND NOT WIN32)
 endif ()
 
 if (ORC_RT_PATH)
-  message(STATUS "Found liborc_rt: ${ORC_RT_PATH}")
-  file(COPY "${ORC_RT_PATH}" DESTINATION "${CMAKE_BINARY_DIR}")
-  install(FILES "${ORC_RT_PATH}" DESTINATION lib)
+  message(STATUS "Embedding liborc_rt: ${ORC_RT_PATH}")
+  enable_language(ASM)
+  configure_file(
+    "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/orc_rt_embed.S.in"
+    "${CMAKE_CURRENT_BINARY_DIR}/orc_rt_embed.S" @ONLY
+  )
+  target_sources(tvm_ffi_orcjit PRIVATE 
"${CMAKE_CURRENT_BINARY_DIR}/orc_rt_embed.S")
+  target_compile_definitions(tvm_ffi_orcjit PRIVATE 
TVM_FFI_ORCJIT_EMBED_ORC_RT=1)
+  # Re-run .incbin if the archive itself changes (path is stable across 
builds).
+  set_property(
+    SOURCE "${CMAKE_CURRENT_BINARY_DIR}/orc_rt_embed.S" PROPERTY 
OBJECT_DEPENDS "${ORC_RT_PATH}"
+  )
 endif ()
 
 # ---- Install ----
diff --git a/addons/tvm_ffi_orcjit/python/tvm_ffi_orcjit/session.py 
b/addons/tvm_ffi_orcjit/python/tvm_ffi_orcjit/session.py
index 89e6b58e..66c7789f 100644
--- a/addons/tvm_ffi_orcjit/python/tvm_ffi_orcjit/session.py
+++ b/addons/tvm_ffi_orcjit/python/tvm_ffi_orcjit/session.py
@@ -18,7 +18,6 @@
 
 from __future__ import annotations
 
-import sys
 import threading
 from pathlib import Path
 from typing import TYPE_CHECKING
@@ -26,48 +25,12 @@ from typing import TYPE_CHECKING
 import tvm_ffi
 from tvm_ffi import Module, Object, register_object
 
-from . import _ffi_api, _lib_dir
+from . import _ffi_api
 
 if TYPE_CHECKING:
     from collections.abc import Sequence
 
 
-def _find_orc_rt_library() -> str | None:
-    """Find the bundled liborc_rt library in the same directory as the 
.so/.dll."""
-    # Windows: skip ORC runtime entirely. LLVM's COFFPlatform (loaded via
-    # ExecutorNativePlatform with liborc_rt) depends on MSVC C++ runtime 
symbols
-    # that are not available in the JIT environment. On Windows, ORC JIT uses a
-    # C-only strategy: JIT objects are compiled as pure C (TVMFFISafeCallType 
ABI),
-    # avoiding all C++ runtime dependencies (magic statics, RTTI, sized delete,
-    # SEH, COMDAT). Our custom InitFiniPlugin handles .CRT$XC*/.CRT$XT* 
init/fini
-    # sections, and DLLImportDefinitionGenerator resolves __imp_ DLL import 
stubs.
-    #
-    # macOS: skip ORC runtime too. ExecutorNativePlatform would install
-    # MachOPlatform, which triggers a compact-unwind 32-bit-delta bug in
-    # JITLink when a user graph mmaps below the per-JITDylib Mach-O header
-    # (see repo-root fix-machoplatform-libunwind-dso-base.patch). Our
-    # InitFiniPlugin handles __mod_init_func / __mod_term_func instead.
-    # Tradeoff: no C++ exception unwinding across JIT frames on macOS.
-    if sys.platform in ("win32", "darwin"):
-        return None
-    patterns = ["liborc_rt*.a"]
-    for pattern in patterns:
-        for lib_path in _lib_dir.glob(pattern):
-            return str(lib_path)
-    return None
-
-
-@tvm_ffi.register_global_func("tvm_ffi_orcjit.DefaultOrcRuntimePath", 
override=True)
-def _default_orc_runtime_path() -> str:
-    """Resolve the ORC runtime path for the shared session (C++ hook).
-
-    Called once by the C++ ``GlobalDefault`` singleton on first use. Returns 
the
-    runtime bundled next to this extension, or an empty string to run with no
-    ORC platform (macOS/Windows, or when no bundled runtime is found).
-    """
-    return _find_orc_rt_library() or ""
-
-
 @register_object("tvm_ffi_orcjit.ExecutionSession")
 class ExecutionSession(Object):
     """ORC JIT Execution Session.
@@ -86,14 +49,26 @@ class ExecutionSession(Object):
 
     """
 
-    def __init__(self, orc_rt_path: str | None = None, slab_size: int = 0) -> 
None:
+    def __init__(
+        self,
+        orc_rt: str | Path | bytes | bytearray | None = "auto",
+        slab_size: int = 0,
+    ) -> None:
         """Initialize ExecutionSession.
 
         Parameters
         ----------
-        orc_rt_path : str, optional
-            Optional path to the liborc_rt library. If not provided, it will be
-            automatically discovered using clang.
+        orc_rt : str or Path or bytes or None
+            Which ORC runtime to install. Linux/ELF only — ignored on macOS and
+            Windows, which never configure an ORC platform.
+
+            - ``"auto"`` (default): the runtime embedded in this extension.
+            - a path (``str`` or ``Path``): a custom liborc_rt archive on disk.
+            - ``bytes``: a custom liborc_rt archive held in memory.
+            - ``None``: no ORC platform at all.
+
+            A custom runtime (path or bytes) must match the LLVM/compiler-rt 
this
+            extension was built against; ``"auto"`` is almost always what you 
want.
         slab_size : int
             Per-slab capacity in bytes for the JIT memory manager. Linux only —
             ignored on macOS and Windows, where the slab allocator is compiled
@@ -109,11 +84,23 @@ class ExecutionSession(Object):
             session is destroyed or ``clear_free_slabs()`` is called.
 
         """
-        if orc_rt_path is None:
-            orc_rt_path = _find_orc_rt_library()
-            if orc_rt_path is None:
-                orc_rt_path = ""
-        self.__init_handle_by_constructor__(_ffi_api.ExecutionSession, 
orc_rt_path, slab_size)  # type: ignore
+        # Normalize to what the C++ ctor (Optional<Variant<String, Bytes>>) 
reads:
+        # None -> no platform; "" -> auto/embedded; str -> path; bytes -> 
in-memory.
+        rt: str | bytes | None
+        if orc_rt is None:
+            rt = None
+        elif orc_rt == "auto":
+            rt = ""
+        elif isinstance(orc_rt, (bytes, bytearray)):
+            rt = bytes(orc_rt)
+        elif isinstance(orc_rt, (str, Path)):
+            rt = str(orc_rt)
+        else:
+            raise TypeError(
+                "orc_rt must be 'auto', a path (str or Path), liborc_rt bytes, 
or None, "
+                f"but got {type(orc_rt).__name__}"
+            )
+        self.__init_handle_by_constructor__(_ffi_api.ExecutionSession, rt, 
slab_size)  # type: ignore
 
     def load_module(
         self,
@@ -223,10 +210,9 @@ def default_session() -> ExecutionSession:
     symbols, the slab arena, and cross-library linking. Created on first call
     and cached for the lifetime of the process.
 
-    The ORC runtime path is resolved once, on first use, by the C++ singleton
-    calling the registered ``tvm_ffi_orcjit.DefaultOrcRuntimePath`` hook (which
-    returns the runtime bundled next to this extension, or none). For an 
isolated
-    session or a tuned arena, construct an :class:`ExecutionSession` directly.
+    The session uses the ORC runtime embedded in the extension (no on-disk path
+    lookup). For an isolated session or a tuned arena, construct an
+    :class:`ExecutionSession` directly.
 
     Returns
     -------
diff --git a/addons/tvm_ffi_orcjit/src/ffi/orc_rt_embed.S.in 
b/addons/tvm_ffi_orcjit/src/ffi/orc_rt_embed.S.in
new file mode 100644
index 00000000..6937b1fb
--- /dev/null
+++ b/addons/tvm_ffi_orcjit/src/ffi/orc_rt_embed.S.in
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Embed liborc_rt.a verbatim into .rodata (Solution A). CMake configure_file
+ * substitutes @ORC_RT_PATH@; the assembler copies the bytes with .incbin, so
+ * no 5 MB literal is compiled. The start/end symbols are .hidden to match the
+ * target's hidden visibility (no dynamic-table entry, no interposition).
+ */
+    .section .rodata
+    .balign 8
+    .hidden orc_rt_archive_start
+    .global orc_rt_archive_start
+orc_rt_archive_start:
+    .incbin "@ORC_RT_PATH@"
+    .hidden orc_rt_archive_end
+    .global orc_rt_archive_end
+orc_rt_archive_end:
diff --git a/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc 
b/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc
index 6e98e863..1de989df 100644
--- a/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc
+++ b/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc
@@ -363,8 +363,8 @@ static void RegisterOrcJITFunctions() {
 
   refl::GlobalDef()
       .def("tvm_ffi_orcjit.ExecutionSession",
-           [](const std::string& orc_rt_path, int64_t slab_size_bytes) {
-             return ORCJITExecutionSession(orc_rt_path, slab_size_bytes);
+           [](const Optional<Variant<String, Bytes>>& orc_rt, int64_t 
slab_size_bytes) {
+             return ORCJITExecutionSession(orc_rt, slab_size_bytes);
            })
       .def("tvm_ffi_orcjit.GlobalDefaultSession",
            []() { return ORCJITExecutionSessionObj::GlobalDefault(); })
diff --git a/addons/tvm_ffi_orcjit/src/ffi/orcjit_session.cc 
b/addons/tvm_ffi_orcjit/src/ffi/orcjit_session.cc
index 24e4cc0c..4f534dc0 100644
--- a/addons/tvm_ffi_orcjit/src/ffi/orcjit_session.cc
+++ b/addons/tvm_ffi_orcjit/src/ffi/orcjit_session.cc
@@ -27,15 +27,16 @@
 #include <llvm/ExecutionEngine/Orc/LLJIT.h>
 #include <llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h>
 #include <llvm/Support/Error.h>
+#include <llvm/Support/MemoryBuffer.h>
 #include <llvm/Support/Process.h>
 #include <llvm/Support/TargetSelect.h>
 #include <tvm/ffi/cast.h>
 #include <tvm/ffi/error.h>
-#include <tvm/ffi/function.h>
 #include <tvm/ffi/object.h>
 #include <tvm/ffi/reflection/registry.h>
 
 #include <cstddef>
+#include <cstdint>
 #include <mutex>
 
 #include "orcjit_dylib.h"
@@ -71,7 +72,57 @@ struct LLVMInitializer {
 
 static LLVMInitializer llvm_initializer;
 
-ORCJITExecutionSessionObj::ORCJITExecutionSessionObj(const std::string& 
orc_rt_path,
+#ifdef TVM_FFI_ORCJIT_EMBED_ORC_RT
+// liborc_rt.a embedded in .rodata by orc_rt_embed.S.in. C linkage binds to the
+// assembler symbols; the linkage spec must be at namespace scope, not in-fn.
+extern "C" const char orc_rt_archive_start[];
+extern "C" const char orc_rt_archive_end[];
+#endif
+
+namespace {
+#ifdef TVM_FFI_ORCJIT_EMBED_ORC_RT
+// Zero-copy view of the embedded archive; its bytes live for the image 
lifetime.
+// Size via uintptr_t: the two symbols are distinct objects, so subtracting the
+// pointers directly would be UB.
+std::unique_ptr<llvm::MemoryBuffer> GetEmbeddedOrcRuntimeBuffer() {
+  return llvm::MemoryBuffer::getMemBuffer(
+      llvm::StringRef(orc_rt_archive_start,
+                      reinterpret_cast<std::uintptr_t>(orc_rt_archive_end) -
+                          
reinterpret_cast<std::uintptr_t>(orc_rt_archive_start)),
+      "liborc_rt.a", /*RequiresNullTerminator=*/false);
+}
+#endif
+
+// Install ExecutorNativePlatform per the `orc_rt` selector (see the ctor doc).
+// A no-op except on Linux/ELF: macOS skips the platform (compact-unwind bug)
+// and Windows never wires up COFFPlatform, so both ignore the selector.
+void SetUpOrcPlatform(llvm::orc::LLJITBuilder& builder,
+                      const Optional<Variant<String, Bytes>>& orc_rt) {
+#if defined(__APPLE__) || defined(_WIN32)
+  (void)builder;
+  (void)orc_rt;
+#else
+  if (!orc_rt.has_value()) return;  // None -> no platform
+  const Variant<String, Bytes>& sel = orc_rt.value();
+  if (auto opt_path = sel.as<String>()) {
+    const String& path = *opt_path;
+    if (path.empty()) {  // "auto" -> embedded (or nothing if compiled out)
+#ifdef TVM_FFI_ORCJIT_EMBED_ORC_RT
+      
builder.setPlatformSetUp(llvm::orc::ExecutorNativePlatform(GetEmbeddedOrcRuntimeBuffer()));
+#endif
+    } else {
+      builder.setPlatformSetUp(llvm::orc::ExecutorNativePlatform(path.operator 
std::string()));
+    }
+  } else {  // Bytes: ExecutorNativePlatform takes ownership of the copy.
+    const Bytes& bytes = sel.get<Bytes>();
+    
builder.setPlatformSetUp(llvm::orc::ExecutorNativePlatform(llvm::MemoryBuffer::getMemBufferCopy(
+        llvm::StringRef(bytes.data(), bytes.size()), "liborc_rt.a")));
+  }
+#endif
+}
+}  // namespace
+
+ORCJITExecutionSessionObj::ORCJITExecutionSessionObj(const 
Optional<Variant<String, Bytes>>& orc_rt,
                                                      int64_t slab_size_bytes)
     : jit_(nullptr) {
   // Create slab-backed memory manager — pre-reserves a contiguous VA region
@@ -156,21 +207,15 @@ 
ORCJITExecutionSessionObj::ORCJITExecutionSessionObj(const std::string& orc_rt_p
   };
 
   auto builder = llvm::orc::LLJITBuilder();
-#ifndef __APPLE__
-  // macOS: always skip ExecutorNativePlatform / MachOPlatform to sidestep
-  // the compact-unwind 32-bit-delta bug in JITLink's CompactUnwindSupport
-  // (personality delta against a per-JITDylib header base wraps `uint64_t`
-  // and fails `isUInt<32>` when a later user graph mmaps below the header;
-  // see the repo-root fix-machoplatform-libunwind-dso-base.patch for the
-  // full analysis).  InitFiniPlugin below handles __mod_init_func /
-  // __mod_term_func instead.  Tradeoff: no C++ exception unwinding across
-  // JIT frames on macOS.
-  if (!orc_rt_path.empty()) {
-    builder.setPlatformSetUp(llvm::orc::ExecutorNativePlatform(orc_rt_path));
-  }
-#else
-  (void)orc_rt_path;
-#endif
+  // Configure the ORC platform from `orc_rt` (a no-op off Linux/ELF; see
+  // SetUpOrcPlatform).  macOS in particular must skip ExecutorNativePlatform /
+  // MachOPlatform to sidestep the compact-unwind 32-bit-delta bug in JITLink's
+  // CompactUnwindSupport (personality delta against a per-JITDylib header base
+  // wraps `uint64_t` and fails `isUInt<32>` when a later user graph mmaps 
below
+  // the header; see the repo-root fix-machoplatform-libunwind-dso-base.patch).
+  // InitFiniPlugin below handles __mod_init_func / __mod_term_func instead;
+  // tradeoff: no C++ exception unwinding across JIT frames on macOS.
+  SetUpOrcPlatform(builder, orc_rt);
   setup_builder(builder);
   jit_ = TVM_FFI_ORCJIT_LLVM_CALL(builder.create());
 #ifdef _WIN32
@@ -198,26 +243,20 @@ 
ORCJITExecutionSessionObj::ORCJITExecutionSessionObj(const std::string& orc_rt_p
 #endif
 }
 
-ORCJITExecutionSession::ORCJITExecutionSession(const std::string& orc_rt_path,
+ORCJITExecutionSession::ORCJITExecutionSession(const Optional<Variant<String, 
Bytes>>& orc_rt,
                                                int64_t slab_size_bytes) {
   ObjectPtr<ORCJITExecutionSessionObj> obj =
-      make_object<ORCJITExecutionSessionObj>(orc_rt_path, slab_size_bytes);
+      make_object<ORCJITExecutionSessionObj>(orc_rt, slab_size_bytes);
   data_ = std::move(obj);
 }
 
 ORCJITExecutionSession ORCJITExecutionSessionObj::GlobalDefault() {
   // Leaked, never-destroyed: the owned LLJIT and slab arena must not be torn
   // down during interpreter finalization, where teardown could call back into
-  // the host language. Resolve the ORC runtime path once, from the registered
-  // global tvm_ffi_orcjit.DefaultOrcRuntimePath (a Python hook that returns 
the
-  // bundled runtime); absent or empty means no platform.
-  static ORCJITExecutionSession* inst = [] {
-    std::string orc_rt_path;
-    if (auto fpath = 
Function::GetGlobal("tvm_ffi_orcjit.DefaultOrcRuntimePath")) {
-      orc_rt_path = (*fpath)().cast<String>().operator std::string();
-    }
-    return new ORCJITExecutionSession(orc_rt_path, 0);
-  }();
+  // the host language. Always "auto" (empty String) and not user-configurable
+  // (see the header) — the embedded runtime's .rodata outlives it trivially.
+  static ORCJITExecutionSession* inst =
+      new ORCJITExecutionSession(Variant<String, Bytes>(String("")), 0);
   return *inst;
 }
 
diff --git a/addons/tvm_ffi_orcjit/src/ffi/orcjit_session.h 
b/addons/tvm_ffi_orcjit/src/ffi/orcjit_session.h
index 7e9b8998..6988490f 100644
--- a/addons/tvm_ffi_orcjit/src/ffi/orcjit_session.h
+++ b/addons/tvm_ffi_orcjit/src/ffi/orcjit_session.h
@@ -31,6 +31,7 @@
 #include <tvm/ffi/container/variant.h>
 #include <tvm/ffi/extra/module.h>
 #include <tvm/ffi/object.h>
+#include <tvm/ffi/optional.h>
 #include <tvm/ffi/string.h>
 
 #include <atomic>
@@ -58,10 +59,21 @@ class ORCJITExecutionSession;
 class ORCJITExecutionSessionObj : public Object {
  public:
   /*!
-   * \brief Default constructor (for make_object)
+   * \brief Construct a session, selecting the ORC runtime.
+   *
+   * \param orc_rt Runtime selector (Linux/ELF only; ignored on macOS/Windows,
+   *        which never configure an ORC platform):
+   *        - empty \c String (default, "auto"): the runtime embedded in this
+   *          extension (or no platform if the embed was compiled out);
+   *        - non-empty \c String: a custom liborc_rt archive on disk;
+   *        - \c Bytes: a custom liborc_rt archive held in memory;
+   *        - \c nullopt: no ORC platform at all.
+   *        A custom runtime must match the LLVM this extension was built 
against.
+   * \param slab_size_bytes Per-slab capacity for the JIT memory arena.
    */
-  explicit ORCJITExecutionSessionObj(const std::string& orc_rt_path = "",
-                                     int64_t slab_size_bytes = 0);
+  explicit ORCJITExecutionSessionObj(
+      const Optional<Variant<String, Bytes>>& orc_rt = Variant<String, 
Bytes>(String("")),
+      int64_t slab_size_bytes = 0);
 
   /*!
    * \brief Get the process-wide shared execution session.
@@ -71,9 +83,10 @@ class ORCJITExecutionSessionObj : public Object {
    * and cross-library linking. Never torn down (interpreter finalization could
    * otherwise call back into the host language during teardown).
    *
-   * The ORC runtime path is resolved on first call from the registered global
-   * \c tvm_ffi_orcjit.DefaultOrcRuntimePath (a Python-side hook that returns 
the
-   * bundled runtime, or empty to disable); absent or empty means no platform.
+   * Always uses the ORC runtime embedded in this extension; deliberately not
+   * user-configurable (a shared process-wide singleton with a hidden runtime
+   * knob would let the first caller pick the platform for everyone). Construct
+   * \ref ORCJITExecutionSession directly for a custom runtime.
    *
    * \return The shared execution session.
    */
@@ -221,9 +234,12 @@ class ORCJITExecutionSession : public ObjectRef {
  public:
   /*!
    * \brief Create a new ExecutionSession
-   * \return The created execution session instance
+   * \param orc_rt ORC runtime selector; see \ref ORCJITExecutionSessionObj.
+   * \param slab_size_bytes Per-slab capacity for the JIT memory arena.
    */
-  explicit ORCJITExecutionSession(const std::string& orc_rt_path = "", int64_t 
slab_size_bytes = 0);
+  explicit ORCJITExecutionSession(
+      const Optional<Variant<String, Bytes>>& orc_rt = Variant<String, 
Bytes>(String("")),
+      int64_t slab_size_bytes = 0);
   // Required: define object reference methods
   TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(ORCJITExecutionSession, 
ObjectRef,
                                                 ORCJITExecutionSessionObj);
diff --git a/addons/tvm_ffi_orcjit/tests/test_session_load_module.py 
b/addons/tvm_ffi_orcjit/tests/test_session_load_module.py
index 461e3340..1b11b753 100644
--- a/addons/tvm_ffi_orcjit/tests/test_session_load_module.py
+++ b/addons/tvm_ffi_orcjit/tests/test_session_load_module.py
@@ -31,6 +31,7 @@ limitations unrelated to this API.
 from __future__ import annotations
 
 import gc
+import sys
 import threading
 from concurrent.futures import ThreadPoolExecutor
 from pathlib import Path
@@ -51,6 +52,24 @@ def obj(name: str) -> str:
     return str(path)
 
 
+def _find_orc_rt_archive() -> str:
+    """Locate a liborc_rt archive under LLVM_PREFIX, or skip.
+
+    Used only by the custom-``orc_rt`` override tests. There is no bundled copy
+    next to the extension anymore (the runtime is embedded), so these tests 
fall
+    back to the archive shipped with the build's LLVM.
+    """
+    import os  # noqa: PLC0415
+
+    prefix = os.environ.get("LLVM_PREFIX")
+    if not prefix:
+        pytest.skip("LLVM_PREFIX not set; cannot locate a liborc_rt archive")
+    matches = sorted(Path(prefix).glob("lib/clang/*/lib/*/liborc_rt*.a"))
+    if not matches:
+        pytest.skip("no liborc_rt archive found under LLVM_PREFIX")
+    return str(matches[0])
+
+
 # ---------------------------------------------------------------------------
 # default_session()
 # ---------------------------------------------------------------------------
@@ -92,10 +111,44 @@ def test_default_session_concurrent_first_call() -> None:
     assert all(s is sessions[0] for s in sessions)
 
 
-def test_default_orc_runtime_path_hook_registered() -> None:
-    """The C++ singleton's ORC-path hook is registered and returns a string."""
-    path = tvm_ffi.get_global_func("tvm_ffi_orcjit.DefaultOrcRuntimePath")()
-    assert isinstance(path, str)  # bundled runtime path, or "" when 
none/disabled
+# ---------------------------------------------------------------------------
+# orc_rt override — custom runtime selectors on a user-created session.
+# (The "auto" default is covered via ExecutionSession() elsewhere.)
+# ---------------------------------------------------------------------------
+
+# A custom ORC runtime is only installed on Linux/ELF; elsewhere it is ignored.
+elf_only = pytest.mark.skipif(
+    sys.platform != "linux", reason="ORC platform (custom liborc_rt) is 
Linux/ELF only"
+)
+
+
+@elf_only
+def test_orc_rt_explicit_path() -> None:
+    """A custom on-disk liborc_rt archive is accepted (str and Path)."""
+    archive = _find_orc_rt_archive()
+    for spec in (archive, Path(archive)):
+        mod = ExecutionSession(orc_rt=spec).load_module(obj("c/test_funcs"))
+        assert mod.test_add(4, 5) == 9
+
+
+@elf_only
+def test_orc_rt_in_memory_bytes() -> None:
+    """A liborc_rt archive supplied as in-memory bytes is accepted."""
+    data = Path(_find_orc_rt_archive()).read_bytes()
+    mod = ExecutionSession(orc_rt=data).load_module(obj("c/test_funcs"))
+    assert mod.test_add(6, 7) == 13
+
+
+def test_orc_rt_none_no_platform() -> None:
+    """orc_rt=None runs with no ORC platform; C-ABI objects still load."""
+    mod = ExecutionSession(orc_rt=None).load_module(obj("c/test_funcs"))
+    assert mod.test_add(8, 9) == 17
+
+
+def test_orc_rt_rejects_bad_type() -> None:
+    """A non-str, non-bytes, non-None selector raises a clear TypeError."""
+    with pytest.raises(TypeError, match=r"orc_rt must be"):
+        ExecutionSession(orc_rt=12345)
 
 
 # ---------------------------------------------------------------------------

Reply via email to