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 6c98ca25 [CORE] Make ModuleObj Stable (#700)
6c98ca25 is described below

commit 6c98ca25213b31516f7d9d03526805f9a09015ba
Author: Tianqi Chen <[email protected]>
AuthorDate: Tue Aug 4 20:31:05 2026 +0800

    [CORE] Make ModuleObj Stable (#700)
    
    ## Rationale
    
    The TVMFFIAny-backed Optional layout is the intended forward ABI, but
    Optional<Function> grew from one pointer to one TVMFFIAny cell.
    ModuleObj function lookup is a C++ ABI boundary whose symbols do not
    encode return types, so retaining Optional<Function> makes old and new
    callers disagree about return storage.
    
    Use nullable Function for both ModuleObj lookup overloads. Function
    remains pointer-sized and preserves the historical null representation,
    while Optional itself and the String metadata APIs retain their current
    ABI.
    
    ## Change
    
    - Return nullable Function from direct and import-aware module lookup.
    - Update library and ORCJIT module implementations plus C++ examples.
---
 addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc |  4 ++--
 addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.h  |  2 +-
 docs/guides/cpp_lang_guide.md                 |  2 +-
 docs/guides/export_func_cls.rst               |  2 +-
 examples/quickstart/load/load_cpp.cc          |  2 +-
 examples/quickstart/load/load_cuda.cc         |  2 +-
 examples/stable_c_abi/src/load.c              |  2 +-
 include/tvm/ffi/extra/module.h                | 11 ++++++-----
 src/ffi/extra/library_module.cc               |  4 ++--
 src/ffi/extra/module.cc                       | 13 +++++++------
 src/ffi/extra/module_internal.h               | 14 +++++++-------
 11 files changed, 30 insertions(+), 28 deletions(-)

diff --git a/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc 
b/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc
index 1de989df..f7f8e38c 100644
--- a/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc
+++ b/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.cc
@@ -333,7 +333,7 @@ llvm::orc::JITDylib& ORCJITDynamicLibraryObj::GetJITDylib() 
{
   return *dylib_;
 }
 
-Optional<Function> ORCJITDynamicLibraryObj::GetFunction(const String& name) {
+Function ORCJITDynamicLibraryObj::GetFunction(const String& name) {
   // Pure symbol lookup. Context symbols were injected once at load time (see
   // Finalize), so this holds no lock and does no refresh — the returned
   // Function, once resolved, is invoked lock-free on the hot path.
@@ -345,7 +345,7 @@ Optional<Function> 
ORCJITDynamicLibraryObj::GetFunction(const String& name) {
     auto* wrapper = new DylibFnContextWithModule{GetRef<Module>(this)};
     return Function::FromExternC(wrapper, c_func, 
DeleteDylibFnContextWithModule);
   }
-  return std::nullopt;
+  return nullptr;
 }
 
 //-------------------------------------
diff --git a/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.h 
b/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.h
index b458b127..9ca8cc25 100644
--- a/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.h
+++ b/addons/tvm_ffi_orcjit/src/ffi/orcjit_dylib.h
@@ -63,7 +63,7 @@ class ORCJITDynamicLibraryObj : public ModuleObj {
 
   const char* kind() const final { return "orcjit"; }
 
-  Optional<Function> GetFunction(const String& name) override;
+  Function GetFunction(const String& name) override;
 
  private:
   /*!
diff --git a/docs/guides/cpp_lang_guide.md b/docs/guides/cpp_lang_guide.md
index d964421c..b6e1c019 100644
--- a/docs/guides/cpp_lang_guide.md
+++ b/docs/guides/cpp_lang_guide.md
@@ -336,7 +336,7 @@ The metadata contains:
 ffi::Module mod = ffi::Module::LoadFromFile("path/to/export_lib.so");
 
 // Get the function
-ffi::Function func = mod->GetFunction("add_one").value();
+ffi::Function func = mod->GetFunction("add_one");
 
 // Query metadata (type schema information)
 ffi::Optional<ffi::String> metadata = mod->GetFunctionMetadata("add_one");
diff --git a/docs/guides/export_func_cls.rst b/docs/guides/export_func_cls.rst
index ac528450..2d74ee2f 100644
--- a/docs/guides/export_func_cls.rst
+++ b/docs/guides/export_func_cls.rst
@@ -101,7 +101,7 @@ library and retrieve functions by name:
    namespace ffi = tvm::ffi;
 
    ffi::Module mod = ffi::Module::LoadFromFile("path/to/library.so");
-   ffi::Function func = mod->GetFunction("add_two").value();
+   ffi::Function func = mod->GetFunction("add_two");
    int result = func(40).cast<int>();  // -> 42
 
 
diff --git a/examples/quickstart/load/load_cpp.cc 
b/examples/quickstart/load/load_cpp.cc
index cee261ed..5278c73c 100644
--- a/examples/quickstart/load/load_cpp.cc
+++ b/examples/quickstart/load/load_cpp.cc
@@ -32,7 +32,7 @@ void Run(tvm::ffi::TensorView x, tvm::ffi::TensorView y) {
   // Load shared library `build/add_one_cpu.so`
   ffi::Module mod = ffi::Module::LoadFromFile("build/add_one_cpu.so");
   // Look up `add_one_cpu` function
-  ffi::Function add_one_cpu = mod->GetFunction("add_one_cpu").value();
+  ffi::Function add_one_cpu = mod->GetFunction("add_one_cpu");
   // Call the function
   add_one_cpu(x, y);
 }
diff --git a/examples/quickstart/load/load_cuda.cc 
b/examples/quickstart/load/load_cuda.cc
index 07e43ffa..db83d06e 100644
--- a/examples/quickstart/load/load_cuda.cc
+++ b/examples/quickstart/load/load_cuda.cc
@@ -32,7 +32,7 @@ void Run(tvm::ffi::TensorView x, tvm::ffi::TensorView y) {
   // Load shared library `build/add_one_cuda.so`
   ffi::Module mod = ffi::Module::LoadFromFile("build/add_one_cuda.so");
   // Look up `add_one_cuda` function
-  ffi::Function add_one_cuda = mod->GetFunction("add_one_cuda").value();
+  ffi::Function add_one_cuda = mod->GetFunction("add_one_cuda");
   // Call the function with CUDA tensors
   add_one_cuda(x, y);
 }
diff --git a/examples/stable_c_abi/src/load.c b/examples/stable_c_abi/src/load.c
index 5f207e90..b384a4e4 100644
--- a/examples/stable_c_abi/src/load.c
+++ b/examples/stable_c_abi/src/load.c
@@ -45,7 +45,7 @@ int Run(DLTensor* x, DLTensor* y) {
 
   // Step 2. Get function `add_one_cpu` from module
   // Equivalent to:
-  //    func = mod->GetFunction("add_one_cpu", /*query_imports=*/false).value()
+  //    func = mod->GetFunction("add_one_cpu", /*query_imports=*/false)
   call_args[0] = (TVMFFIAny){.type_index = mod.type_index, .v_obj = mod.v_obj};
   call_args[1] = (TVMFFIAny){.type_index = kTVMFFIRawStr, .v_c_str = 
"add_one_cpu"};
   call_args[2] = (TVMFFIAny){.type_index = kTVMFFIBool, .v_int64 = 0};
diff --git a/include/tvm/ffi/extra/module.h b/include/tvm/ffi/extra/module.h
index 8a824a3e..d7b07c19 100644
--- a/include/tvm/ffi/extra/module.h
+++ b/include/tvm/ffi/extra/module.h
@@ -57,9 +57,10 @@ class TVM_FFI_EXTRA_CXX_API ModuleObj : public Object {
   /*!
    * \brief Get a ffi::Function from the module.
    * \param name The name of the function.
-   * \return The function.
+   * \return The function, or nullptr if it is not found.
+   * \note The nullable Function return keeps this virtual interface 
pointer-sized.
    */
-  virtual Optional<Function> GetFunction(const String& name) = 0;
+  virtual Function GetFunction(const String& name) = 0;
   /*!
    * \brief Returns true if this module has a definition for a function of \p 
name.
    *
@@ -70,7 +71,7 @@ class TVM_FFI_EXTRA_CXX_API ModuleObj : public Object {
    * \param name The name of the function.
    * \return True if the module implements the function, false otherwise.
    */
-  virtual bool ImplementsFunction(const String& name) { return 
GetFunction(name).has_value(); }
+  virtual bool ImplementsFunction(const String& name) { return 
GetFunction(name) != nullptr; }
   /*!
    * \brief Get the docstring of the function, if available.
    * \param name The name of the function.
@@ -142,9 +143,9 @@ class TVM_FFI_EXTRA_CXX_API ModuleObj : public Object {
    * \brief Overloaded function to optionally query from imports.
    * \param name The name of the function.
    * \param query_imports Whether to query imported modules.
-   * \return The function.
+   * \return The function, or nullptr if it is not found.
    */
-  Optional<Function> GetFunction(const String& name, bool query_imports);
+  Function GetFunction(const String& name, bool query_imports);
   /*!
    * \brief Overloaded function to optionally query from imports.
    * \param name The name of the function.
diff --git a/src/ffi/extra/library_module.cc b/src/ffi/extra/library_module.cc
index cc992aa2..c69518eb 100644
--- a/src/ffi/extra/library_module.cc
+++ b/src/ffi/extra/library_module.cc
@@ -42,7 +42,7 @@ class LibraryModuleObj final : public ModuleObj {
   /*! \brief Get the property of the runtime module .*/
   int GetPropertyMask() const final { return Module::kBinarySerializable | 
Module::kRunnable; };
 
-  Optional<ffi::Function> GetFunction(const String& name) final {
+  ffi::Function GetFunction(const String& name) final {
     TVMFFISafeCallType faddr;
     faddr = 
reinterpret_cast<TVMFFISafeCallType>(lib_->GetSymbolWithSymbolPrefix(name));
     // ensure the function keeps the Library Module alive
@@ -55,7 +55,7 @@ class LibraryModuleObj final : public ModuleObj {
                                          args.size(), 
reinterpret_cast<TVMFFIAny*>(rv)));
       });
     }
-    return std::nullopt;
+    return nullptr;
   }
 
   Optional<String> GetFunctionMetadata(const String& name) final {
diff --git a/src/ffi/extra/module.cc b/src/ffi/extra/module.cc
index 31bb95bb..9c254a12 100644
--- a/src/ffi/extra/module.cc
+++ b/src/ffi/extra/module.cc
@@ -58,18 +58,19 @@ class ModuleGlobals {
   std::mutex mutex_;
 };
 
-Optional<Function> ModuleObj::GetFunction(const String& name, bool 
query_imports) {
-  if (auto opt_func = this->GetFunction(name)) {
-    return opt_func;
+Function ModuleObj::GetFunction(const String& name, bool query_imports) {
+  if (Function func = this->GetFunction(name); func != nullptr) {
+    return func;
   }
   if (query_imports) {
     for (const Any& import : imports_) {
-      if (auto opt_func = import.cast<Module>()->GetFunction(name, 
query_imports)) {
-        return *opt_func;
+      if (Function func = import.cast<Module>()->GetFunction(name, 
query_imports);
+          func != nullptr) {
+        return func;
       }
     }
   }
-  return std::nullopt;
+  return nullptr;
 }
 
 Optional<String> ModuleObj::GetFunctionMetadata(const String& name, bool 
query_imports) {
diff --git a/src/ffi/extra/module_internal.h b/src/ffi/extra/module_internal.h
index 4519d9ad..1a5742ab 100644
--- a/src/ffi/extra/module_internal.h
+++ b/src/ffi/extra/module_internal.h
@@ -76,21 +76,21 @@ struct ModuleObj::InternalUnsafe {
       return const_cast<FunctionObj*>((*it).second.operator->());
     }
 
-    auto opt_func = [&]() -> std::optional<Function> {
+    Function func = [&]() -> Function {
       for (const Any& import : module->imports_) {
-        if (auto opt_func = import.cast<Module>()->GetFunction(s_name, true)) {
-          return *opt_func;
+        if (Function func = import.cast<Module>()->GetFunction(s_name, true); 
func != nullptr) {
+          return func;
         }
       }
       // try global at last
-      return tvm::ffi::Function::GetGlobal(s_name);
+      return tvm::ffi::Function::GetGlobal(s_name).value_or(nullptr);
     }();
-    if (!opt_func.has_value()) {
+    if (func == nullptr) {
       TVM_FFI_THROW(RuntimeError) << "Cannot find function " << name
                                   << " in the imported modules or global 
registry.";
     }
-    module->import_lookup_cache_.Set(s_name, *opt_func);
-    return const_cast<FunctionObj*>((*opt_func).operator->());
+    module->import_lookup_cache_.Set(s_name, func);
+    return const_cast<FunctionObj*>(func.operator->());
   }
 
   static void RegisterReflection() {

Reply via email to