This is an automated email from the ASF dual-hosted git repository.
junrushao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new 05001bed64 [LLVM][RUNTIME] Enable multi systemlib with device code
(#14843)
05001bed64 is described below
commit 05001bed646c26f788521d4d14a2bad9bf755e65
Author: Tianqi Chen <[email protected]>
AuthorDate: Sat May 13 14:33:01 2023 -0400
[LLVM][RUNTIME] Enable multi systemlib with device code (#14843)
This PR enables combination of multiple system lib
into the same static library with a system_lib_prefix attribute.
This can open doors for multiple models to be compiled separately
then packaged into the same app via static library.
It resolves a previous issue that prevents multiple system
lib to be linked together when they come with extra binary component
such as CUDA due to symbol conflict.
---
include/tvm/ir/module.h | 29 ++++++++
include/tvm/runtime/module.h | 2 -
include/tvm/target/codegen.h | 9 ++-
python/tvm/runtime/module.py | 27 +++++--
src/runtime/library_module.h | 2 +
src/runtime/system_library.cc | 58 +++++++++++-----
src/target/codegen.cc | 31 ++++++---
src/target/llvm/codegen_amdgpu.cc | 2 +-
src/target/llvm/codegen_blob.cc | 22 +++---
src/target/llvm/codegen_blob.h | 4 +-
src/target/llvm/codegen_cpu.cc | 22 +++---
src/target/llvm/codegen_cpu.h | 9 ++-
src/target/llvm/codegen_hexagon.cc | 14 ++--
src/target/llvm/codegen_llvm.cc | 5 +-
src/target/llvm/codegen_llvm.h | 7 +-
src/target/llvm/codegen_nvptx.cc | 2 +-
src/target/llvm/llvm_module.cc | 53 +++++++++++---
tests/python/unittest/test_runtime_module_load.py | 58 ++++++++++------
tests/python/unittest/test_runtime_rpc.py | 18 +++--
tests/python/unittest/test_target_codegen_blob.py | 85 ++++++++++++++++-------
20 files changed, 322 insertions(+), 137 deletions(-)
diff --git a/include/tvm/ir/module.h b/include/tvm/ir/module.h
index fdb44b1188..85cb3c7613 100644
--- a/include/tvm/ir/module.h
+++ b/include/tvm/ir/module.h
@@ -503,6 +503,35 @@ constexpr const char* kConstants = "constants";
*/
constexpr const char* kExternalMods = "external_mods";
+/*!
+ * \brief A prefix for generating C symbols system lib creation.
+ *
+ * This prefix guides passes that creates global_symbol for internal functions
+ * that may have c linkage (e.g. TIR functions and some BYOC functions). It
also affects
+ * the symbol of the fat bin blob during module export.
+ *
+ * This attribute is used to avoid symbol conflict when we
+ * generate and combine multiple system libs that get linked into one.
+ *
+ * Rationale: mechanisms like BYOC rely on the common global symbol
+ * and each external compiler also has its own mechanism of mangling.
+ * As a result, we cannot rely on other mechanisms on setting a global_symbol
and then renaming,
+ * because the external compiler already agreed on the name.
+ *
+ * system_lib_prefix provides a way to hint at the passes to allow names to
+ * avoid name conflict at the beginning.
+ *
+ * Note that users can still directly specify global symbols that may conflict.
+ * It is up to the downstream toolchain to manage those external-facing
functions.
+ *
+ * This does not affect non-C linkage functions it is less of an issue because
+ * they will be embedded into fatbin that in different symbols,
+ * The system lib loader can pick the right prefix for a given prefix.
+ *
+ * Having this attribute implies system lib generation linkage.
+ */
+constexpr const char* kSystemLibPrefix = "system_lib_prefix";
+
/*!
* \brief All the named runtime::NDArrays accumulated during compilation by
external codegen.
* Generally the associated runtime::Module will indicate it requires bindings
for these names,
diff --git a/include/tvm/runtime/module.h b/include/tvm/runtime/module.h
index 508b34b351..3da4945c86 100644
--- a/include/tvm/runtime/module.h
+++ b/include/tvm/runtime/module.h
@@ -278,8 +278,6 @@ constexpr const char* tvm_get_c_metadata = "get_c_metadata";
constexpr const char* tvm_module_ctx = "__tvm_module_ctx";
/*! \brief Global variable to store device module blob */
constexpr const char* tvm_dev_mblob = "__tvm_dev_mblob";
-/*! \brief Number of bytes of device module blob. */
-constexpr const char* tvm_dev_mblob_nbytes = "__tvm_dev_mblob_nbytes";
/*! \brief global function to set device */
constexpr const char* tvm_set_device = "__tvm_set_device";
/*! \brief Auxiliary counter to global barrier. */
diff --git a/include/tvm/target/codegen.h b/include/tvm/target/codegen.h
index b2cab0e4bc..46a19ad71b 100644
--- a/include/tvm/target/codegen.h
+++ b/include/tvm/target/codegen.h
@@ -55,9 +55,11 @@ runtime::Module Build(IRModule mod, Target target);
*
* \param m The host module with the imports.
* \param system_lib Whether expose as system library.
+ * \param c_symbol_prefix Optional symbol prefix of the blob symbol.
* \return cstr The C string representation of the file.
*/
-std::string PackImportsToC(const runtime::Module& m, bool system_lib);
+std::string PackImportsToC(const runtime::Module& m, bool system_lib,
+ const std::string& c_symbol_prefix = "");
/*!
* \brief Pack imported device library to a LLVM module.
@@ -68,10 +70,13 @@ std::string PackImportsToC(const runtime::Module& m, bool
system_lib);
* \param m The host module with the imports.
* \param system_lib Whether expose as system library.
* \param target_triple LLVM target triple
+ * \param c_symbol_prefix Optional symbol prefix of the blob symbol.
+ *
* \return runtime::Module The generated LLVM module.
*/
runtime::Module PackImportsToLLVM(const runtime::Module& m, bool system_lib,
- const std::string& target_triple);
+ const std::string& target_triple,
+ const std::string& c_symbol_prefix = "");
} // namespace codegen
} // namespace tvm
#endif // TVM_TARGET_CODEGEN_H_
diff --git a/python/tvm/runtime/module.py b/python/tvm/runtime/module.py
index c78a6d9c31..671e3a390e 100644
--- a/python/tvm/runtime/module.py
+++ b/python/tvm/runtime/module.py
@@ -508,6 +508,7 @@ class Module(object):
files = addons if addons else []
is_system_lib = False
has_c_module = False
+ system_lib_prefix = None
llvm_target_string = None
global_object_format = "o"
for index, module in enumerate(modules):
@@ -549,6 +550,8 @@ class Module(object):
if module.type_key == "llvm":
is_system_lib = module.get_function("__tvm_is_system_module")()
llvm_target_string =
module.get_function("_get_target_string")()
+ system_lib_prefix =
module.get_function("__tvm_get_system_lib_prefix")()
+
if not fcompile:
if file_name.endswith(".tar"):
fcompile = _tar.tar
@@ -564,15 +567,21 @@ class Module(object):
raise ValueError("%s need --system-lib option" % str(fcompile))
if self.imported_modules:
+ pack_lib_prefix = system_lib_prefix if system_lib_prefix else ""
+
if enabled("llvm") and llvm_target_string:
- path_obj = os.path.join(workspace_dir,
f"devc.{global_object_format}")
- m = _ffi_api.ModulePackImportsToLLVM(self, is_system_lib,
llvm_target_string)
+ path_obj = os.path.join(
+ workspace_dir,
f"{pack_lib_prefix}devc.{global_object_format}"
+ )
+ m = _ffi_api.ModulePackImportsToLLVM(
+ self, is_system_lib, llvm_target_string, pack_lib_prefix
+ )
m.save(path_obj)
files.append(path_obj)
else:
- path_cc = os.path.join(workspace_dir, "devc.c")
+ path_cc = os.path.join(workspace_dir,
f"{pack_lib_prefix}devc.c")
with open(path_cc, "w") as f:
- f.write(_ffi_api.ModulePackImportsToC(self, is_system_lib))
+ f.write(_ffi_api.ModulePackImportsToC(self, is_system_lib,
pack_lib_prefix))
files.append(path_cc)
# The imports could contain a c module but the object format could be
tar
@@ -589,7 +598,7 @@ class Module(object):
return fcompile(file_name, files, **kwargs)
-def system_lib():
+def system_lib(symbol_prefix=""):
"""Get system-wide library module singleton.
System lib is a global module that contains self register functions in
startup.
@@ -602,12 +611,18 @@ def system_lib():
The system lib is intended to be linked and loaded during the entire
life-cyle of the program.
If you want dynamic loading features, use dso modules instead.
+ Parameters
+ ----------
+ symbol_prefix: Optional[str]
+ Optional symbol prefix that can be used for search. When we lookup a
symbol
+ symbol_prefix + name will first be searched, then the name without
symbol_prefix.
+
Returns
-------
module : runtime.Module
The system-wide library module.
"""
- return _ffi_api.SystemLib()
+ return _ffi_api.SystemLib(symbol_prefix)
def load_module(path, fmt=""):
diff --git a/src/runtime/library_module.h b/src/runtime/library_module.h
index 44dc323186..167e819601 100644
--- a/src/runtime/library_module.h
+++ b/src/runtime/library_module.h
@@ -101,6 +101,8 @@ ObjectPtr<Library> CreateDSOLibraryObject(std::string
library_path);
* \param lib The library.
* \param wrapper Optional function used to wrap a TVMBackendPackedCFunc,
* by default WrapPackedFunc is used.
+ * \param symbol_prefix Optional symbol prefix that can be used to search
alternative symbols.
+ *
* \return The corresponding loaded module.
*
* \note This function can create multiple linked modules
diff --git a/src/runtime/system_library.cc b/src/runtime/system_library.cc
index fe29146d8b..be9257e53f 100644
--- a/src/runtime/system_library.cc
+++ b/src/runtime/system_library.cc
@@ -32,20 +32,8 @@
namespace tvm {
namespace runtime {
-class SystemLibrary : public Library {
+class SystemLibraryRegistry {
public:
- SystemLibrary() = default;
-
- void* GetSymbol(const char* name) final {
- std::lock_guard<std::mutex> lock(mutex_);
- auto it = tbl_.find(name);
- if (it != tbl_.end()) {
- return it->second;
- } else {
- return nullptr;
- }
- }
-
void RegisterSymbol(const std::string& name, void* ptr) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = tbl_.find(name);
@@ -56,8 +44,18 @@ class SystemLibrary : public Library {
tbl_[name] = ptr;
}
- static const ObjectPtr<SystemLibrary>& Global() {
- static auto inst = make_object<SystemLibrary>();
+ void* GetSymbol(const char* name) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto it = tbl_.find(name);
+ if (it != tbl_.end()) {
+ return it->second;
+ } else {
+ return nullptr;
+ }
+ }
+
+ static SystemLibraryRegistry* Global() {
+ static SystemLibraryRegistry* inst = new SystemLibraryRegistry();
return inst;
}
@@ -68,14 +66,36 @@ class SystemLibrary : public Library {
std::unordered_map<std::string, void*> tbl_;
};
-TVM_REGISTER_GLOBAL("runtime.SystemLib").set_body_typed([]() {
- static auto mod = CreateModuleFromLibrary(SystemLibrary::Global());
- return mod;
+class SystemLibrary : public Library {
+ public:
+ explicit SystemLibrary(const std::string& symbol_prefix) :
symbol_prefix_(symbol_prefix) {}
+
+ void* GetSymbol(const char* name) {
+ if (symbol_prefix_.length() != 0) {
+ std::string name_with_prefix = symbol_prefix_ + name;
+ void* symbol = reg_->GetSymbol(name_with_prefix.c_str());
+ if (symbol != nullptr) return symbol;
+ }
+ return reg_->GetSymbol(name);
+ }
+
+ private:
+ SystemLibraryRegistry* reg_ = SystemLibraryRegistry::Global();
+ std::string symbol_prefix_;
+};
+
+TVM_REGISTER_GLOBAL("runtime.SystemLib").set_body([](TVMArgs args,
TVMRetValue* rv) {
+ std::string symbol_prefix = "";
+ if (args.size() != 0) {
+ symbol_prefix = args[0].operator std::string();
+ }
+ auto mod =
CreateModuleFromLibrary(make_object<SystemLibrary>(symbol_prefix));
+ *rv = mod;
});
} // namespace runtime
} // namespace tvm
int TVMBackendRegisterSystemLibSymbol(const char* name, void* ptr) {
- tvm::runtime::SystemLibrary::Global()->RegisterSymbol(name, ptr);
+ tvm::runtime::SystemLibraryRegistry::Global()->RegisterSymbol(name, ptr);
return 0;
}
diff --git a/src/target/codegen.cc b/src/target/codegen.cc
index 24dbfebe55..bbb2c15a64 100644
--- a/src/target/codegen.cc
+++ b/src/target/codegen.cc
@@ -240,8 +240,15 @@ std::string SerializeModule(const runtime::Module& mod) {
}
} // namespace
-std::string PackImportsToC(const runtime::Module& mod, bool system_lib) {
+std::string PackImportsToC(const runtime::Module& mod, bool system_lib,
+ const std::string& c_symbol_prefix) {
std::string bin = SerializeModule(mod);
+ std::string mdev_blob_name = c_symbol_prefix +
runtime::symbol::tvm_dev_mblob;
+
+ if (c_symbol_prefix.length() != 0) {
+ CHECK(system_lib)
+ << "c_symbol_prefix advanced option should be used in conjuction with
system-lib";
+ }
// translate to C program
std::ostringstream os;
@@ -253,10 +260,10 @@ std::string PackImportsToC(const runtime::Module& mod,
bool system_lib) {
os << "#ifdef __cplusplus\n"
<< "extern \"C\" {\n"
<< "#endif\n";
- os << "TVM_EXPORT extern const unsigned char " <<
runtime::symbol::tvm_dev_mblob << "[];\n";
+ os << "TVM_EXPORT extern const unsigned char " << mdev_blob_name << "[];\n";
uint64_t nbytes = bin.length();
- os << "const unsigned char " << runtime::symbol::tvm_dev_mblob << "["
- << bin.length() + sizeof(nbytes) << "] = {\n ";
+ os << "const unsigned char " << mdev_blob_name << "[" << bin.length() +
sizeof(nbytes)
+ << "] = {\n ";
os << std::hex;
size_t nunit = 80 / 4;
for (size_t i = 0; i < sizeof(nbytes); ++i) {
@@ -279,9 +286,9 @@ std::string PackImportsToC(const runtime::Module& mod, bool
system_lib) {
os << "\n};\n";
if (system_lib) {
os << "extern int TVMBackendRegisterSystemLibSymbol(const char*,
void*);\n";
- os << "static int " << runtime::symbol::tvm_dev_mblob << "_reg_ = "
- << "TVMBackendRegisterSystemLibSymbol(\"" <<
runtime::symbol::tvm_dev_mblob << "\", (void*)"
- << runtime::symbol::tvm_dev_mblob << ");\n";
+ os << "static int " << mdev_blob_name << "_reg_ = "
+ << "TVMBackendRegisterSystemLibSymbol(\"" << mdev_blob_name << "\",
(void*)"
+ << mdev_blob_name << ");\n";
}
os << "#ifdef __cplusplus\n"
<< "}\n"
@@ -290,7 +297,13 @@ std::string PackImportsToC(const runtime::Module& mod,
bool system_lib) {
}
runtime::Module PackImportsToLLVM(const runtime::Module& mod, bool system_lib,
- const std::string& llvm_target_string) {
+ const std::string& llvm_target_string,
+ const std::string& c_symbol_prefix) {
+ if (c_symbol_prefix.length() != 0) {
+ CHECK(system_lib)
+ << "c_symbol_prefix advanced option should be used in conjuction with
system-lib";
+ }
+
std::string bin = SerializeModule(mod);
uint64_t nbytes = bin.length();
@@ -308,7 +321,7 @@ runtime::Module PackImportsToLLVM(const runtime::Module&
mod, bool system_lib,
// the codegen function.
const PackedFunc* codegen_f = runtime::Registry::Get(codegen_f_name);
ICHECK(codegen_f != nullptr) << "codegen.codegen_blob is not presented.";
- return (*codegen_f)(blob_byte_array, system_lib, llvm_target_string);
+ return (*codegen_f)(blob_byte_array, system_lib, llvm_target_string,
c_symbol_prefix);
}
TVM_REGISTER_GLOBAL("target.Build").set_body_typed(Build);
diff --git a/src/target/llvm/codegen_amdgpu.cc
b/src/target/llvm/codegen_amdgpu.cc
index a177aa7f68..3efe548e1c 100644
--- a/src/target/llvm/codegen_amdgpu.cc
+++ b/src/target/llvm/codegen_amdgpu.cc
@@ -260,7 +260,7 @@ runtime::Module BuildAMDGPU(IRModule mod, Target target) {
#endif
auto cg = std::make_unique<CodeGenAMDGPU>();
- cg->Init("TVMAMDGPUModule", llvm_target.get(), false, false, false);
+ cg->Init("TVMAMDGPUModule", llvm_target.get(), NullOpt, false, false);
cg->AddFunctionsOrdered(mod->functions.begin(), mod->functions.end(),
[](auto& kv) {
ICHECK(kv.second->template IsInstance<PrimFuncNode>())
diff --git a/src/target/llvm/codegen_blob.cc b/src/target/llvm/codegen_blob.cc
index 5bfc69126d..b4fe93b518 100644
--- a/src/target/llvm/codegen_blob.cc
+++ b/src/target/llvm/codegen_blob.cc
@@ -62,19 +62,22 @@ namespace tvm {
namespace codegen {
std::unique_ptr<llvm::Module> CodeGenBlob(const std::string& data, bool
system_lib,
- LLVMTarget* llvm_target) {
+ LLVMTarget* llvm_target,
+ const std::string& c_symbol_prefix) {
llvm::TargetMachine* tm = llvm_target->GetOrCreateTargetMachine();
const llvm::Triple& triple = tm->getTargetTriple();
llvm::LLVMContext* ctx = llvm_target->GetContext();
- std::string module_name = "devc";
+ std::string module_name = c_symbol_prefix + "devc";
auto module = std::make_unique<llvm::Module>(module_name, *ctx);
module->setTargetTriple(triple.str());
llvm_target->SetTargetMetadata(module.get());
module->setDataLayout(tm->createDataLayout());
auto* blob_value = llvm::ConstantDataArray::getString(*ctx, data, false);
+ std::string mdev_blob_name = c_symbol_prefix +
runtime::symbol::tvm_dev_mblob;
+
auto* tvm_dev_mblob = new llvm::GlobalVariable(
*module, blob_value->getType(), true,
llvm::GlobalValue::ExternalLinkage, blob_value,
- runtime::symbol::tvm_dev_mblob, nullptr,
llvm::GlobalVariable::NotThreadLocal, 0);
+ mdev_blob_name, nullptr, llvm::GlobalVariable::NotThreadLocal, 0);
// If large const data (>2GB) is saved to default .rodata section
// then linking it to shared library will fail - relocation truncated to
fit: R_X86_64_PC32.
@@ -106,9 +109,9 @@ std::unique_ptr<llvm::Module> CodeGenBlob(const
std::string& data, bool system_l
auto int8_ptr_ty = int8_ty->getPointerTo(0);
llvm::Constant* constant_zero = llvm::Constant::getNullValue(int32_ty);
- auto* tvm_dev_mblob_reg = new llvm::GlobalVariable(
- *module, int32_ty, false, llvm::GlobalValue::InternalLinkage,
constant_zero,
- std::string(runtime::symbol::tvm_dev_mblob) + "_reg_");
+ auto* tvm_dev_mblob_reg =
+ new llvm::GlobalVariable(*module, int32_ty, false,
llvm::GlobalValue::InternalLinkage,
+ constant_zero, mdev_blob_name + "_reg_");
auto tvm_dev_mblob_reg_alignment =
#if TVM_LLVM_VERSION >= 110
module->getDataLayout().getABITypeAlign(int32_ty);
@@ -121,13 +124,12 @@ std::unique_ptr<llvm::Module> CodeGenBlob(const
std::string& data, bool system_l
tvm_dev_mblob_reg->setAlignment(tvm_dev_mblob_reg_alignment);
#endif
- auto* tvm_dev_mblob_string_ty =
- llvm::ArrayType::get(int8_ty,
std::strlen(runtime::symbol::tvm_dev_mblob) + 1);
+ auto* tvm_dev_mblob_string_ty = llvm::ArrayType::get(int8_ty,
mdev_blob_name.length() + 1);
auto* tvm_dev_mblob_string_value =
- llvm::ConstantDataArray::getString(*ctx,
runtime::symbol::tvm_dev_mblob, true);
+ llvm::ConstantDataArray::getString(*ctx, mdev_blob_name, true);
auto* tvm_dev_mblob_string = new llvm::GlobalVariable(
*module, tvm_dev_mblob_string_ty, true,
llvm::GlobalValue::PrivateLinkage,
- tvm_dev_mblob_string_value,
std::string(runtime::symbol::tvm_dev_mblob) + ".str");
+ tvm_dev_mblob_string_value, mdev_blob_name + ".str");
#if TVM_LLVM_VERSION >= 100
tvm_dev_mblob_string->setAlignment(llvm::Align(1));
#else
diff --git a/src/target/llvm/codegen_blob.h b/src/target/llvm/codegen_blob.h
index a06c043c07..e3d61ff684 100644
--- a/src/target/llvm/codegen_blob.h
+++ b/src/target/llvm/codegen_blob.h
@@ -44,11 +44,13 @@ class LLVMTarget;
* \param data Blob data
* \param system_lib Whether expose as system library.
* \param target_triple LLVM target triple
+ * \param c_symbol prefix The C symbol prefix of the blob.
*
* \return LLVM module and LLVM context
*/
std::unique_ptr<llvm::Module> CodeGenBlob(const std::string& data, bool
system_lib,
- LLVMTarget* llvm_target);
+ LLVMTarget* llvm_target,
+ const std::string& c_symbol_prefix =
"");
} // namespace codegen
} // namespace tvm
diff --git a/src/target/llvm/codegen_cpu.cc b/src/target/llvm/codegen_cpu.cc
index 59575c370f..dbcdb4a3af 100644
--- a/src/target/llvm/codegen_cpu.cc
+++ b/src/target/llvm/codegen_cpu.cc
@@ -71,9 +71,11 @@ namespace codegen {
CodeGenCPU::CodeGenCPU() = default;
CodeGenCPU::~CodeGenCPU() = default;
-void CodeGenCPU::Init(const std::string& module_name, LLVMTarget* llvm_target,
bool system_lib,
- bool dynamic_lookup, bool target_c_runtime) {
- CodeGenLLVM::Init(module_name, llvm_target, system_lib, dynamic_lookup,
target_c_runtime);
+void CodeGenCPU::Init(const std::string& module_name, LLVMTarget* llvm_target,
+ Optional<String> system_lib_prefix, bool dynamic_lookup,
+ bool target_c_runtime) {
+ CodeGenLLVM::Init(module_name, llvm_target, system_lib_prefix,
dynamic_lookup, target_c_runtime);
+ system_lib_prefix_ = system_lib_prefix;
dbg_info_ = CreateDebugInfo(module_.get());
static_assert(sizeof(TVMValue) == sizeof(double), "invariant");
func_handle_map_.clear();
@@ -153,7 +155,7 @@ void CodeGenCPU::Init(const std::string& module_name,
LLVMTarget* llvm_target, b
ftype_tvm_static_init_callback_->getPointerTo(), t_void_p_, t_int_},
false);
// initialize TVM runtime API
- if (system_lib && !target_c_runtime) {
+ if (system_lib_prefix_.defined() && !target_c_runtime) {
// We will need this in environment for backward registration.
// Defined in include/tvm/runtime/c_backend_api.h:
// int TVMBackendRegisterSystemLibSymbol(const char* name, void* ptr);
@@ -163,7 +165,7 @@ void CodeGenCPU::Init(const std::string& module_name,
LLVMTarget* llvm_target, b
} else {
f_tvm_register_system_symbol_ = nullptr;
}
- if (dynamic_lookup || system_lib) {
+ if (dynamic_lookup || system_lib_prefix_.defined()) {
f_tvm_func_call_ = llvm::Function::Create(ftype_tvm_func_call_,
llvm::Function::ExternalLinkage,
"TVMFuncCall", module_.get());
f_tvm_get_func_from_env_ =
@@ -180,7 +182,6 @@ void CodeGenCPU::Init(const std::string& module_name,
LLVMTarget* llvm_target, b
"TVMBackendParallelBarrier", module_.get());
}
target_c_runtime_ = target_c_runtime;
- is_system_lib_ = system_lib;
InitGlobalContext(dynamic_lookup);
}
@@ -527,12 +528,12 @@ llvm::Value*
CodeGenCPU::GetContextPtr(llvm::GlobalVariable* gv) {
}
void CodeGenCPU::InitGlobalContext(bool dynamic_lookup) {
+ std::string ctx_symbol = system_lib_prefix_.value_or("") +
tvm::runtime::symbol::tvm_module_ctx;
// Module context
- gv_mod_ctx_ = InitContextPtr(t_void_p_,
tvm::runtime::symbol::tvm_module_ctx);
+ gv_mod_ctx_ = InitContextPtr(t_void_p_, ctx_symbol);
// Register back the locations.
if (f_tvm_register_system_symbol_ != nullptr && !target_c_runtime_) {
- export_system_symbols_.emplace_back(
- std::make_pair(tvm::runtime::symbol::tvm_module_ctx, gv_mod_ctx_));
+ export_system_symbols_.emplace_back(std::make_pair(ctx_symbol,
gv_mod_ctx_));
} else {
if (!dynamic_lookup) {
gv_tvm_func_call_ = InitContextPtr(ftype_tvm_func_call_->getPointerTo(),
"__TVMFuncCall");
@@ -1344,7 +1345,8 @@ void
CodeGenCPU::DefineMetadata(runtime::metadata::Metadata metadata) {
}
void CodeGenCPU::DefineFunctionRegistry(Array<String> func_names) {
- ICHECK(is_system_lib_) << "Loading of --system-lib modules is yet to be
defined for C runtime";
+ ICHECK(system_lib_prefix_.defined())
+ << "Loading of --system-lib modules is yet to be defined for C runtime";
Array<String> symbols;
std::vector<llvm::Constant*> funcs;
for (auto sym : func_names) {
diff --git a/src/target/llvm/codegen_cpu.h b/src/target/llvm/codegen_cpu.h
index afbd49e143..3cc1bbeb41 100644
--- a/src/target/llvm/codegen_cpu.h
+++ b/src/target/llvm/codegen_cpu.h
@@ -64,8 +64,9 @@ class CodeGenCPU : public CodeGenLLVM {
CodeGenCPU();
virtual ~CodeGenCPU();
- void Init(const std::string& module_name, LLVMTarget* llvm_target, bool
system_lib,
- bool dynamic_lookup, bool target_c_runtime) override;
+ void Init(const std::string& module_name, LLVMTarget* llvm_target,
+ Optional<String> system_lib_prefix, bool dynamic_lookup,
+ bool target_c_runtime) override;
void AddFunction(const PrimFunc& f) override;
void AddMainFunction(const std::string& entry_func_name) override;
std::unique_ptr<llvm::Module> Finish() override;
@@ -191,7 +192,9 @@ class CodeGenCPU : public CodeGenLLVM {
// internal debug information, to be populated by
std::unique_ptr<DebugInfo> dbg_info_;
bool target_c_runtime_;
- bool is_system_lib_;
+ // The system lib prefix if it is not nullopt, then we should do
+ // system lib registration with the given prefix. The prefix can be ""
+ Optional<String> system_lib_prefix_;
// Get the DWARF type corresponding to the LLVM type |ty|. The current API
in practice only
// generates |int32|, and |int8*|.
diff --git a/src/target/llvm/codegen_hexagon.cc
b/src/target/llvm/codegen_hexagon.cc
index 2adb95f659..a2f13e98b1 100644
--- a/src/target/llvm/codegen_hexagon.cc
+++ b/src/target/llvm/codegen_hexagon.cc
@@ -69,8 +69,9 @@ namespace codegen {
// Hexagon code generation
class CodeGenHexagon final : public CodeGenCPU {
public:
- void Init(const std::string& module_name, LLVMTarget* llvm_target, bool
system_lib,
- bool dynamic_lookup, bool target_c_runtime) override;
+ void Init(const std::string& module_name, LLVMTarget* llvm_target,
+ Optional<String> system_lib_prefix, bool dynamic_lookup,
+ bool target_c_runtime) override;
void InitTarget() final;
using CodeGenCPU::VisitStmt_;
@@ -114,9 +115,10 @@ class CodeGenHexagon final : public CodeGenCPU {
"tvm_vect_qhmath_hvx_ceil_ahf", "tvm_vect_qhmath_hvx_pow_ahf"};
};
-void CodeGenHexagon::Init(const std::string& module_name, LLVMTarget*
llvm_target, bool system_lib,
- bool dynamic_lookup, bool target_c_runtime) {
- CodeGenCPU::Init(module_name, llvm_target, system_lib, dynamic_lookup,
target_c_runtime);
+void CodeGenHexagon::Init(const std::string& module_name, LLVMTarget*
llvm_target,
+ Optional<String> system_lib_prefix, bool
dynamic_lookup,
+ bool target_c_runtime) {
+ CodeGenCPU::Init(module_name, llvm_target, system_lib_prefix,
dynamic_lookup, target_c_runtime);
}
void CodeGenHexagon::InitTarget() {
@@ -563,7 +565,7 @@ runtime::Module BuildHexagon(IRModule mod, Target target) {
funcs.emplace_back(f);
}
- cg->Init("TVMHexagonModule", llvm_target.get(), false, false, false);
+ cg->Init("TVMHexagonModule", llvm_target.get(), NullOpt, false, false);
cg->AddFunctionsOrdered(funcs.begin(), funcs.end());
if (entry_func.length() != 0) {
cg->AddMainFunction(entry_func);
diff --git a/src/target/llvm/codegen_llvm.cc b/src/target/llvm/codegen_llvm.cc
index 2a8c3226f3..01e25d5361 100644
--- a/src/target/llvm/codegen_llvm.cc
+++ b/src/target/llvm/codegen_llvm.cc
@@ -136,8 +136,9 @@ std::unique_ptr<CodeGenLLVM>
CodeGenLLVM::Create(LLVMTarget* llvm_target) {
}
}
-void CodeGenLLVM::Init(const std::string& module_name, LLVMTarget*
llvm_target, bool system_lib,
- bool dynamic_lookup, bool target_c_runtime) {
+void CodeGenLLVM::Init(const std::string& module_name, LLVMTarget* llvm_target,
+ Optional<String> system_lib_prefix, bool dynamic_lookup,
+ bool target_c_runtime) {
llvm_target_ = llvm_target;
llvm::LLVMContext* ctx = llvm_target_->GetContext();
builder_.reset(new IRBuilder(*ctx));
diff --git a/src/target/llvm/codegen_llvm.h b/src/target/llvm/codegen_llvm.h
index 0d5650c473..ca4c916f84 100644
--- a/src/target/llvm/codegen_llvm.h
+++ b/src/target/llvm/codegen_llvm.h
@@ -116,14 +116,15 @@ class CodeGenLLVM : public ExprFunctor<llvm::Value*(const
PrimExpr&)>,
* \param module_name The name of the module.
* \param tm Target machine model
* \param ctx The context.
- * \param system_lib Whether to insert system library registration.
+ * \param system_lib_prefix If the value is not NullOpt, insert system lib
registration.
+ * The value corresponds to the prefix of the
system lib symbols.
* \param dynamic_lookup Whether dynamically lookup runtime function
* or use the runtime function table passed by caller.
* \param target_c_runtime If true, generate a module to be executed by the
C runtime. In practice
* this option influences whether global ctors are
used.
*/
- virtual void Init(const std::string& module_name, LLVMTarget* llvm_target,
bool system_lib,
- bool dynamic_lookup, bool target_c_runtime);
+ virtual void Init(const std::string& module_name, LLVMTarget* llvm_target,
+ Optional<String> system_lib_prefix, bool dynamic_lookup,
bool target_c_runtime);
/*!
* \brief Turn on fast math flags for floating point operations.
diff --git a/src/target/llvm/codegen_nvptx.cc b/src/target/llvm/codegen_nvptx.cc
index 46816eb20c..18f6092291 100644
--- a/src/target/llvm/codegen_nvptx.cc
+++ b/src/target/llvm/codegen_nvptx.cc
@@ -309,7 +309,7 @@ runtime::Module BuildNVPTX(IRModule mod, Target target) {
int compute_ver = GetCUDAComputeVersion(target);
auto cg = std::make_unique<CodeGenNVPTX>();
- cg->Init("TVMPTXModule", llvm_target.get(), false, false, false);
+ cg->Init("TVMPTXModule", llvm_target.get(), NullOpt, false, false);
cg->AddFunctionsOrdered(mod->functions.begin(), mod->functions.end(),
[](auto& kv) {
ICHECK(kv.second->template IsInstance<PrimFuncNode>())
diff --git a/src/target/llvm/llvm_module.cc b/src/target/llvm/llvm_module.cc
index 2173cad4a7..b6a0da8475 100644
--- a/src/target/llvm/llvm_module.cc
+++ b/src/target/llvm/llvm_module.cc
@@ -142,6 +142,15 @@ PackedFunc LLVMModuleNode::GetFunction(const std::string&
name,
if (name == "__tvm_is_system_module") {
bool flag = (module_->getFunction("__tvm_module_startup") != nullptr);
return PackedFunc([flag](TVMArgs args, TVMRetValue* rv) { *rv = flag; });
+ } else if (name == "__tvm_get_system_lib_prefix") {
+ return PackedFunc([this](TVMArgs args, TVMRetValue* rv) {
+ auto* md = module_->getModuleFlag("tvm_system_lib_prefix");
+ if (md != nullptr) {
+ *rv = llvm::cast<llvm::MDString>(md)->getString().str();
+ } else {
+ *rv = nullptr;
+ }
+ });
} else if (name == "get_func_names") {
return PackedFunc(
[sptr_to_self, this](TVMArgs args, TVMRetValue* rv) { *rv =
this->function_names_; });
@@ -290,7 +299,12 @@ void LLVMModuleNode::Init(const IRModule& mod, const
Target& target) {
std::string entry_func;
relay::Runtime runtime =
mod->GetAttr<relay::Runtime>(tvm::attr::kRuntime).value_or(relay::Runtime::Create("cpp"));
- bool system_lib = runtime->GetAttr<Bool>("system-lib").value_or(Bool(false));
+
+ Optional<String> system_lib_prefix =
mod->GetAttr<String>(tvm::attr::kSystemLibPrefix);
+ if (!system_lib_prefix &&
runtime->GetAttr<Bool>("system-lib").value_or(Bool(false))) {
+ system_lib_prefix = "";
+ }
+
bool target_c_runtime = runtime->name == "crt";
for (auto kv : mod->functions) {
@@ -312,7 +326,8 @@ void LLVMModuleNode::Init(const IRModule& mod, const
Target& target) {
// ICHECK(funcs.size() > 0);
// TODO(tqchen): remove the entry function behavior as it does not
// makes sense when we start to use multiple modules.
- cg->Init("TVMMod", llvm_target.get(), system_lib, system_lib,
target_c_runtime);
+ cg->Init("TVMMod", llvm_target.get(), system_lib_prefix,
system_lib_prefix.defined(),
+ target_c_runtime);
cg->SetFastMathFlags(llvm_target->GetFastMathFlags());
cg->AddFunctionsOrdered(funcs.begin(), funcs.end());
@@ -326,6 +341,12 @@ void LLVMModuleNode::Init(const IRModule& mod, const
Target& target) {
module_->addModuleFlag(llvm::Module::Override, "Debug Info Version",
llvm::DEBUG_METADATA_VERSION);
+ if (system_lib_prefix) {
+ std::string str_val = system_lib_prefix.value();
+ module_->addModuleFlag(llvm::Module::Warning, "tvm_system_lib_prefix",
+ llvm::MDString::get(*(llvm_target->GetContext()),
str_val));
+ }
+
if (tm->getTargetTriple().isOSDarwin()) {
module_->addModuleFlag(llvm::Module::Override, "Dwarf Version", 2);
}
@@ -479,12 +500,13 @@ TVM_REGISTER_GLOBAL("codegen.llvm_target_enabled")
});
TVM_REGISTER_GLOBAL("codegen.codegen_blob")
- .set_body_typed([](std::string data, bool system_lib,
- std::string llvm_target_string) -> runtime::Module {
+ .set_body_typed([](std::string data, bool system_lib, std::string
llvm_target_string,
+ std::string c_symbol_prefix) -> runtime::Module {
auto n = make_object<LLVMModuleNode>();
auto llvm_instance = std::make_unique<LLVMInstance>();
With<LLVMTarget> llvm_target(*llvm_instance, llvm_target_string);
- std::unique_ptr<llvm::Module> blob = CodeGenBlob(data, system_lib,
llvm_target.get());
+ std::unique_ptr<llvm::Module> blob =
+ CodeGenBlob(data, system_lib, llvm_target.get(), c_symbol_prefix);
n->Init(std::move(blob), std::move(llvm_instance));
return runtime::Module(n);
});
@@ -493,10 +515,15 @@ runtime::Module
CreateLLVMCppMetadataModule(runtime::metadata::Metadata metadata
tvm::relay::Runtime runtime) {
auto llvm_instance = std::make_unique<LLVMInstance>();
With<LLVMTarget> llvm_target(*llvm_instance, target);
- bool system_lib = runtime->GetAttr<Bool>("system-lib").value_or(Bool(false));
+
+ Optional<String> system_lib_prefix = NullOpt;
+ if (runtime->GetAttr<Bool>("system-lib").value_or(Bool(false))) {
+ system_lib_prefix = "";
+ }
+
auto cg = std::make_unique<CodeGenCPU>();
- cg->Init("TVMMetadataMod", llvm_target.get(), system_lib, system_lib,
+ cg->Init("TVMMetadataMod", llvm_target.get(), system_lib_prefix,
system_lib_prefix.defined(),
/*target_c_runtime=*/false);
cg->DefineMetadata(metadata);
@@ -531,13 +558,19 @@ runtime::Module CreateLLVMCrtMetadataModule(const
Array<runtime::Module>& module
auto llvm_instance = std::make_unique<LLVMInstance>();
With<LLVMTarget> llvm_target(*llvm_instance, target);
- bool system_lib = runtime->GetAttr<Bool>("system-lib").value_or(Bool(false));
+
+ Optional<String> system_lib_prefix = NullOpt;
+ if (runtime->GetAttr<Bool>("system-lib").value_or(Bool(false))) {
+ system_lib_prefix = "";
+ }
+
bool target_c_runtime = runtime->name == "crt";
- ICHECK(system_lib && target_c_runtime)
+ ICHECK(system_lib_prefix.defined() && target_c_runtime)
<< "For LLVM C-runtime metadata module, must include --system-lib and
--runtime=c; "
<< "got target: " << target->str();
auto cg = std::make_unique<CodeGenCPU>();
- cg->Init("TVMMetadataMod", llvm_target.operator->(), system_lib, system_lib,
target_c_runtime);
+ cg->Init("TVMMetadataMod", llvm_target.operator->(), system_lib_prefix,
+ system_lib_prefix.defined(), target_c_runtime);
cg->DefineFunctionRegistry(func_names);
auto mod = cg->Finish();
diff --git a/tests/python/unittest/test_runtime_module_load.py
b/tests/python/unittest/test_runtime_module_load.py
index 9d06763087..31e0faf0d4 100644
--- a/tests/python/unittest/test_runtime_module_load.py
+++ b/tests/python/unittest/test_runtime_module_load.py
@@ -16,8 +16,7 @@
# under the License.
import tvm
from tvm import te
-from tvm.contrib import cc, utils
-import ctypes
+from tvm.contrib import cc, utils, popen_pool
import sys
import numpy as np
import subprocess
@@ -128,15 +127,24 @@ def test_device_module_dump():
# test cross compiler function
f.export_library(path_dso, cc.cross_compiler("g++"))
- f1 = tvm.runtime.load_module(path_dso)
- a = tvm.nd.array(np.random.uniform(size=1024).astype(A.dtype), dev)
- b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev)
- f1(a, b)
- np.testing.assert_equal(b.numpy(), a.numpy() + 1)
- if sys.platform != "win32":
- f2 = tvm.runtime.system_lib()
- f2[name](a, b)
+ def popen_check():
+ import tvm
+ import sys
+
+ f1 = tvm.runtime.load_module(path_dso)
+ a = tvm.nd.array(np.random.uniform(size=1024).astype(A.dtype), dev)
+ b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev)
+ f1(a, b)
np.testing.assert_equal(b.numpy(), a.numpy() + 1)
+ if sys.platform != "win32":
+ f2 = tvm.runtime.system_lib()
+ f2[name](a, b)
+ np.testing.assert_equal(b.numpy(), a.numpy() + 1)
+
+ # system lib should be loaded in different process
+ worker = popen_pool.PopenWorker()
+ worker.send(popen_check)
+ worker.recv()
def check_stackvm(device):
dev = tvm.device(device, 0)
@@ -208,16 +216,26 @@ def test_combine_module_llvm():
fadd1.save(path1)
fadd2.save(path2)
cc.create_shared(path_dso, [path1, path2])
- # Load dll, will trigger system library registration
- ctypes.CDLL(path_dso)
- # Load the system wide library
- mm = tvm.runtime.system_lib()
- a = tvm.nd.array(np.random.uniform(size=nn).astype(A.dtype), dev)
- b = tvm.nd.array(np.zeros(nn, dtype=A.dtype), dev)
- mm["myadd1"](a, b)
- np.testing.assert_equal(b.numpy(), a.numpy() + 1)
- mm["myadd2"](a, b)
- np.testing.assert_equal(b.numpy(), a.numpy() + 1)
+
+ def popen_check():
+ import tvm.runtime
+ import ctypes
+
+ # Load dll, will trigger system library registration
+ ctypes.CDLL(path_dso)
+ # Load the system wide library
+ mm = tvm.runtime.system_lib()
+ a = tvm.nd.array(np.random.uniform(size=nn).astype(A.dtype), dev)
+ b = tvm.nd.array(np.zeros(nn, dtype=A.dtype), dev)
+ mm["myadd1"](a, b)
+ np.testing.assert_equal(b.numpy(), a.numpy() + 1)
+ mm["myadd2"](a, b)
+ np.testing.assert_equal(b.numpy(), a.numpy() + 1)
+
+ # system lib should be loaded in different process
+ worker = popen_pool.PopenWorker()
+ worker.send(popen_check)
+ worker.recv()
if sys.platform != "win32":
check_system_lib()
diff --git a/tests/python/unittest/test_runtime_rpc.py
b/tests/python/unittest/test_runtime_rpc.py
index 8d0567cecc..97016684a6 100644
--- a/tests/python/unittest/test_runtime_rpc.py
+++ b/tests/python/unittest/test_runtime_rpc.py
@@ -179,7 +179,7 @@ def test_rpc_large_array():
@tvm.testing.skip_if_32bit(reason="skipping test for i386.")
@tvm.testing.requires_rpc
def test_rpc_echo():
- def check(remote):
+ def check(remote, local_session):
fecho = remote.get_function("testing.echo")
assert fecho(1, 2, 3) == 1
assert fecho(100, 2, 3) == 100
@@ -191,15 +191,19 @@ def test_rpc_echo():
raise_err()
remote.cpu().sync()
- with pytest.raises(AttributeError):
- f3 = remote.system_lib()["notexist"]
+ # tests around system lib are not threadsafe by design
+ # and do not work well with multithread pytest
+ # skip local session as they are being tested elsewhere
+ if not local_session:
+ with pytest.raises(AttributeError):
+ f3 = remote.system_lib()["notexist"]
temp = rpc.server._server_env([])
server = rpc.Server()
client = rpc.connect("127.0.0.1", server.port)
- check(rpc.LocalSession())
+ check(rpc.LocalSession(), True)
- check(client)
+ check(client, False)
def check_minrpc():
if tvm.get_global_func("rpc.CreatePipeClient", allow_missing=True) is
None:
@@ -208,7 +212,7 @@ def test_rpc_echo():
temp = utils.tempdir()
minrpc_exec = temp.relpath("minrpc")
tvm.rpc.with_minrpc(cc.create_executable)(minrpc_exec, [])
- check(rpc.PopenSession(minrpc_exec))
+ check(rpc.PopenSession(minrpc_exec), False)
# minrpc on the remote
server = rpc.Server()
client = rpc.connect(
@@ -216,7 +220,7 @@ def test_rpc_echo():
server.port,
session_constructor_args=["rpc.PopenSession", open(minrpc_exec,
"rb").read()],
)
- check(client)
+ check(client, False)
check_minrpc()
diff --git a/tests/python/unittest/test_target_codegen_blob.py
b/tests/python/unittest/test_target_codegen_blob.py
index 62dcf924b4..570de4b248 100644
--- a/tests/python/unittest/test_target_codegen_blob.py
+++ b/tests/python/unittest/test_target_codegen_blob.py
@@ -15,14 +15,14 @@
# specific language governing permissions and limitations
# under the License.
+import ctypes
import numpy as np
from tvm import relay
-from tvm.relay import testing
-from tvm.contrib import graph_executor
+import tvm.relay.testing
+from tvm.contrib import graph_executor, cc, utils, popen_pool
import tvm
-from tvm import te
-import ctypes
import tvm.testing
+from tvm.script import ir as I, tir as T
@tvm.testing.uses_gpu
@@ -49,8 +49,6 @@ def test_synthetic():
with tvm.transform.PassContext(opt_level=3):
synthetic_gpu_lib = relay.build_module.build(synthetic_mod, "cuda",
params=synthetic_params)
- from tvm.contrib import utils
-
temp = utils.tempdir()
path_lib = temp.relpath("deploy_lib.so")
synthetic_gpu_lib.export_library(path_lib)
@@ -67,34 +65,71 @@ def test_synthetic():
@tvm.testing.uses_gpu
-def test_cuda_lib():
+def test_cuda_multi_lib():
+ # test combining two system lib together
+ # each contains a fatbin component in cuda
dev = tvm.cuda(0)
for device in ["llvm", "cuda"]:
if not tvm.testing.device_enabled(device):
print("skip because %s is not enabled..." % device)
return
- nn = 12
- n = tvm.runtime.convert(nn)
- A = te.placeholder((n,), name="A")
- B = te.compute(A.shape, lambda *i: A(*i) + 1.0, name="B")
- s = te.create_schedule(B.op)
- bx, tx = s[B].split(B.op.axis[0], factor=4)
- s[B].bind(bx, te.thread_axis("blockIdx.x"))
- s[B].bind(tx, te.thread_axis("threadIdx.x"))
- from tvm.contrib import utils
+ @tvm.script.ir_module
+ class ModA:
+ I.module_attrs({"system_lib_prefix": "modA_"})
+
+ @T.prim_func
+ def my_inplace_update(x: T.Buffer((12), "float32")) -> None:
+ T.func_attr({"global_symbol": "modA_my_inplace_update"})
+ for bx in T.thread_binding(T.int64(1), thread="blockIdx.x"):
+ for tx in T.thread_binding(T.int64(12), thread="threadIdx.x"):
+ x[tx] = x[tx] + 1
+
+ @tvm.script.ir_module
+ class ModB:
+ I.module_attrs({"system_lib_prefix": "modB_"})
+
+ @T.prim_func
+ def my_inplace_update(x: T.Buffer((12), "float32")) -> None:
+ T.func_attr({"global_symbol": "modB_my_inplace_update"})
+ for bx in T.thread_binding(T.int64(1), thread="blockIdx.x"):
+ for tx in T.thread_binding(T.int64(12), thread="threadIdx.x"):
+ x[tx] = x[tx] + 2
temp = utils.tempdir()
- fn_add = tvm.build(s, [A, B], target="cuda --host=llvm", name="add")
- path_lib = temp.relpath("deploy_lib.so")
- fn_add.export_library(path_lib)
- m = tvm.runtime.load_module(path_lib)
- a = tvm.nd.array(np.random.uniform(size=nn).astype(A.dtype), dev)
- b = tvm.nd.array(np.zeros(nn, dtype=A.dtype), dev)
- m["add"](a, b)
- np.testing.assert_equal(b.numpy(), a.numpy() + 1)
+ target = tvm.target.Target("cuda", host="llvm")
+ libA = tvm.build(ModA, target=target)
+ libB = tvm.build(ModB, target=target)
+
+ pathA = temp.relpath("libA.a")
+ pathB = temp.relpath("libB.a")
+ path_dso = temp.relpath("mylib.so")
+ libA.export_library(pathA, cc.create_staticlib)
+ libB.export_library(pathB, cc.create_staticlib)
+ # package two static libs together
+ cc.create_shared(path_dso, ["-Wl,--whole-archive", pathA, pathB,
"-Wl,--no-whole-archive"])
+
+ def popen_check():
+ # Load dll, will trigger system library registration
+ ctypes.CDLL(path_dso)
+ # Load the system wide library
+ dev = tvm.cuda()
+ a_np = np.random.uniform(size=12).astype("float32")
+ a_nd = tvm.nd.array(a_np, dev)
+ b_nd = tvm.nd.array(a_np, dev)
+ syslibA = tvm.runtime.system_lib("modA_")
+ syslibB = tvm.runtime.system_lib("modB_")
+ syslibA["my_inplace_update"](a_nd)
+ syslibB["my_inplace_update"](b_nd)
+ np.testing.assert_equal(a_nd.numpy(), a_np + 1)
+ np.testing.assert_equal(b_nd.numpy(), a_np + 2)
+
+ # system lib should be loaded in different process
+ worker = popen_pool.PopenWorker()
+ worker.send(popen_check)
+ worker.recv()
if __name__ == "__main__":
test_synthetic()
- test_cuda_lib()
+ test_cuda_multilib()