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() {