From 52db011dccd8aaa45f1cd69c67f061ccbd80ad46 Mon Sep 17 00:00:00 2001 From: PENGUINLIONG Date: Wed, 24 Aug 2022 13:39:21 +0800 Subject: [PATCH] [aot] C-API error handling mechanism (#5847) * C-API error handling * Check procedural errors * Null check * Fixed compilation * Vulkan interop * Fixed compilation * Fixed compilation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed compilation * Update c_api/src/taichi_core_impl.cpp Co-authored-by: Ailing * Fixed typo Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ailing --- build.ps1 | 2 +- c_api/include/taichi/taichi_core.h | 23 + c_api/src/taichi_core_impl.cpp | 404 +++++++++++------- c_api/src/taichi_core_impl.h | 48 ++- c_api/src/taichi_vulkan_impl.cpp | 143 +++---- c_api/taichi.json | 49 +++ misc/generate_c_api.py | 2 + taichi/aot/module_loader.h | 11 +- taichi/common/serialization.h | 19 +- taichi/runtime/gfx/aot_module_loader_impl.cpp | 26 +- .../runtime/llvm/llvm_aot_module_loader.cpp | 12 +- taichi/runtime/llvm/llvm_aot_module_loader.h | 3 +- 12 files changed, 480 insertions(+), 262 deletions(-) diff --git a/build.ps1 b/build.ps1 index 0390677db98d2..ff056bc5eedca 100644 --- a/build.ps1 +++ b/build.ps1 @@ -144,7 +144,7 @@ if ($env:VK_SDK_PATH) { # Chain up the cmake arguments. Write-Host "Will build Taichi ($BuildType) with the following CMake args:" -$TaichiCMakeArgs = $env:TAICHI_CMAKE_ARGS +$TaichiCMakeArgs = $env:TAICHI_CMAKE_ARGS ?? "" foreach ($Pair in $CMakeArgs.GetEnumerator()) { $Key = $Pair | Select-Object -ExpandProperty Key $Value = ($Pair | Select-Object -ExpandProperty Value) -replace "\\", "/" diff --git a/c_api/include/taichi/taichi_core.h b/c_api/include/taichi/taichi_core.h index 10712c62958d7..34e5626be603c 100644 --- a/c_api/include/taichi/taichi_core.h +++ b/c_api/include/taichi/taichi_core.h @@ -41,6 +41,21 @@ typedef struct TiKernel_t *TiKernel; // handle.compute_graph typedef struct TiComputeGraph_t *TiComputeGraph; +// enumeration.error +typedef enum TiError { + TI_ERROR_INCOMPLETE = 1, + TI_ERROR_SUCCESS = 0, + TI_ERROR_NOT_SUPPORTED = -1, + TI_ERROR_CORRUPTED_DATA = -2, + TI_ERROR_NAME_NOT_FOUND = -3, + TI_ERROR_INVALID_ARGUMENT = -4, + TI_ERROR_ARGUMENT_NULL = -5, + TI_ERROR_ARGUMENT_OUT_OF_RANGE = -6, + TI_ERROR_ARGUMENT_NOT_FOUND = -7, + TI_ERROR_INVALID_INTEROP = -8, + TI_ERROR_MAX_ENUM = 0xffffffff, +} TiError; + // enumeration.arch typedef enum TiArch { TI_ARCH_X64 = 0, @@ -262,6 +277,14 @@ typedef struct TiNamedArgument { TiArgument argument; } TiNamedArgument; +// function.get_last_error +TI_DLL_EXPORT TiError TI_API_CALL ti_get_last_error(uint64_t message_size, + char *message); + +// function.set_last_error +TI_DLL_EXPORT void TI_API_CALL ti_set_last_error(TiError error, + const char *message); + // function.create_runtime TI_DLL_EXPORT TiRuntime TI_API_CALL ti_create_runtime(TiArch arch); diff --git a/c_api/src/taichi_core_impl.cpp b/c_api/src/taichi_core_impl.cpp index b534ea91babd7..e2a45f483f301 100644 --- a/c_api/src/taichi_core_impl.cpp +++ b/c_api/src/taichi_core_impl.cpp @@ -3,6 +3,43 @@ #include "taichi_llvm_impl.h" #include "taichi/program/ndarray.h" +struct ErrorCache { + TiError error{TI_ERROR_SUCCESS}; + std::string message{}; +}; + +namespace { +// Error is recorded on a per-thread basis. +thread_local ErrorCache thread_error_cache; + +const char *describe_error(TiError error) { + switch (error) { + case TI_ERROR_INCOMPLETE: + return "incomplete"; + case TI_ERROR_SUCCESS: + return "success"; + case TI_ERROR_NOT_SUPPORTED: + return "not supported"; + case TI_ERROR_CORRUPTED_DATA: + return "path not found"; + case TI_ERROR_NAME_NOT_FOUND: + return "name not found"; + case TI_ERROR_INVALID_ARGUMENT: + return "invalid argument"; + case TI_ERROR_ARGUMENT_NULL: + return "argument null"; + case TI_ERROR_ARGUMENT_OUT_OF_RANGE: + return "argument out of range"; + case TI_ERROR_ARGUMENT_NOT_FOUND: + return "argument not found"; + case TI_ERROR_INVALID_INTEROP: + return "invalid interop"; + default: + return "unknown error"; + } +} +} // namespace + Runtime::Runtime(taichi::Arch arch) : arch(arch) { } Runtime::~Runtime() { @@ -31,15 +68,18 @@ AotModule::AotModule(Runtime &runtime, : runtime_(&runtime), aot_module_(std::move(aot_module)) { } -taichi::lang::aot::CompiledGraph &AotModule::get_cgraph( +taichi::lang::aot::Kernel *AotModule::get_kernel(const std::string &name) { + return aot_module_->get_kernel(name); +} +taichi::lang::aot::CompiledGraph *AotModule::get_cgraph( const std::string &name) { auto it = loaded_cgraphs_.find(name); if (it == loaded_cgraphs_.end()) { - return *loaded_cgraphs_ - .emplace(std::make_pair(name, aot_module_->get_graph(name))) - .first->second; + return loaded_cgraphs_ + .emplace(std::make_pair(name, aot_module_->get_graph(name))) + .first->second.get(); } else { - return *it->second; + return it->second.get(); } } taichi::lang::aot::Module &AotModule::get() { @@ -62,6 +102,35 @@ Runtime &Event::runtime() { // ----------------------------------------------------------------------------- +TiError ti_get_last_error(uint64_t message_size, char *message) { + // Emit message only if the output buffer is property provided. + if (message_size > 0 && message != nullptr) { + size_t n = thread_error_cache.message.size(); + if (n >= message_size) { + n = message_size - 1; // -1 for the byte of `\0`. + } + std::memcpy(message, thread_error_cache.message.data(), n); + message[n] = '\0'; + } + return thread_error_cache.error; +} +// C-API errors MUST be set via this interface. No matter from internal or +// external procedures. +void ti_set_last_error(TiError error, const char *message) { + if (error < TI_ERROR_SUCCESS) { + TI_WARN("C-API error: ({}) {}", describe_error(error), message); + if (message != nullptr) { + thread_error_cache.message = message; + } else { + thread_error_cache.message.clear(); + } + thread_error_cache.error = error; + } else { + thread_error_cache.error = TI_ERROR_SUCCESS; + thread_error_cache.message.clear(); + } +} + TiRuntime ti_create_runtime(TiArch arch) { switch (arch) { #ifdef TI_WITH_VULKAN @@ -84,46 +153,41 @@ TiRuntime ti_create_runtime(TiArch arch) { } #endif // TI_WITH_LLVM default: { - TI_WARN("ignored attempt to create runtime on unknown arch"); + TI_CAPI_NOT_SUPPORTED(arch); return TI_NULL_HANDLE; } } return TI_NULL_HANDLE; } void ti_destroy_runtime(TiRuntime runtime) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to destroy runtime of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); delete (Runtime *)runtime; } TiMemory ti_allocate_memory(TiRuntime runtime, - const TiMemoryAllocateInfo *createInfo) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to allocate memory on runtime of null handle"); - return TI_NULL_HANDLE; - } + const TiMemoryAllocateInfo *create_info) { + TI_CAPI_ARGUMENT_NULL_RV(runtime); + TI_CAPI_ARGUMENT_NULL_RV(create_info); taichi::lang::AllocUsage usage{}; - if (createInfo->usage & TI_MEMORY_USAGE_STORAGE_BIT) { + if (create_info->usage & TI_MEMORY_USAGE_STORAGE_BIT) { usage = usage | taichi::lang::AllocUsage::Storage; } - if (createInfo->usage & TI_MEMORY_USAGE_UNIFORM_BIT) { + if (create_info->usage & TI_MEMORY_USAGE_UNIFORM_BIT) { usage = usage | taichi::lang::AllocUsage::Uniform; } - if (createInfo->usage & TI_MEMORY_USAGE_VERTEX_BIT) { + if (create_info->usage & TI_MEMORY_USAGE_VERTEX_BIT) { usage = usage | taichi::lang::AllocUsage::Vertex; } - if (createInfo->usage & TI_MEMORY_USAGE_INDEX_BIT) { + if (create_info->usage & TI_MEMORY_USAGE_INDEX_BIT) { usage = usage | taichi::lang::AllocUsage::Index; } taichi::lang::Device::AllocParams params{}; - params.size = createInfo->size; - params.host_write = createInfo->host_write; - params.host_read = createInfo->host_read; - params.export_sharing = createInfo->export_sharing; + params.size = create_info->size; + params.host_write = create_info->host_write; + params.host_read = create_info->host_read; + params.export_sharing = create_info->export_sharing; params.usage = usage; TiMemory devmem = ((Runtime *)runtime)->allocate_memory(params); @@ -131,50 +195,35 @@ TiMemory ti_allocate_memory(TiRuntime runtime, } void ti_free_memory(TiRuntime runtime, TiMemory devmem) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to free memory on runtime of null handle"); - return; - } - if (devmem == nullptr) { - TI_WARN("ignored attempt to free memory of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(devmem); Runtime *runtime2 = (Runtime *)runtime; runtime2->free_memory(devmem); } void *ti_map_memory(TiRuntime runtime, TiMemory devmem) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to map memory on runtime of null handle"); - return nullptr; - } - if (devmem == nullptr) { - TI_WARN("ignored attempt to map memory of null handle"); - return nullptr; - } + TI_CAPI_ARGUMENT_NULL_RV(runtime); + TI_CAPI_ARGUMENT_NULL_RV(devmem); + Runtime *runtime2 = (Runtime *)runtime; return runtime2->get().map(devmem2devalloc(*runtime2, devmem)); } void ti_unmap_memory(TiRuntime runtime, TiMemory devmem) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to unmap memory on runtime of null handle"); - return; - } - if (devmem == nullptr) { - TI_WARN("ignored attempt to unmap memory of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(devmem); + Runtime *runtime2 = (Runtime *)runtime; runtime2->get().unmap(devmem2devalloc(*runtime2, devmem)); } TiTexture ti_allocate_texture(TiRuntime runtime, const TiTextureAllocateInfo *allocate_info) { - TI_ERROR_IF(allocate_info->mip_level_count > 1, - "Multi mip-level is not supported yet"); - TI_ERROR_IF(allocate_info->extent.array_layer_count > 1, - "Array texture is not supported yet"); + TI_CAPI_ARGUMENT_NULL_RV(runtime); + TI_CAPI_ARGUMENT_NULL_RV(allocate_info); + + TI_CAPI_NOT_SUPPORTED_IF_RV(allocate_info->mip_level_count > 1); + TI_CAPI_NOT_SUPPORTED_IF_RV(allocate_info->extent.array_layer_count > 1); taichi::lang::ImageAllocUsage usage{}; if (allocate_info->usage & TI_TEXTURE_USAGE_STORAGE_BIT) { @@ -187,6 +236,30 @@ TiTexture ti_allocate_texture(TiRuntime runtime, usage = usage | taichi::lang::ImageAllocUsage::Attachment; } + switch ((taichi::lang::ImageDimension)allocate_info->dimension) { +#define PER_IMAGE_DIMENSION(x) case taichi::lang::ImageDimension::x: +#include "taichi/inc/image_dimension.inc.h" +#undef PER_IMAGE_DIMENSION + break; + default: { + ti_set_last_error(TI_ERROR_ARGUMENT_OUT_OF_RANGE, + "allocate_info->dimension"); + return TI_NULL_HANDLE; + } + } + + switch ((taichi::lang::BufferFormat)allocate_info->format) { +#define PER_BUFFER_FORMAT(x) case taichi::lang::BufferFormat::x: +#include "taichi/inc/buffer_format.inc.h" +#undef PER_BUFFER_FORMAT + break; + default: { + ti_set_last_error(TI_ERROR_ARGUMENT_OUT_OF_RANGE, + "allocate_info->format"); + return TI_NULL_HANDLE; + } + } + taichi::lang::ImageParams params{}; params.x = allocate_info->extent.width; params.y = allocate_info->extent.height; @@ -199,11 +272,16 @@ TiTexture ti_allocate_texture(TiRuntime runtime, TiTexture devtex = ((Runtime *)runtime)->allocate_texture(params); return devtex; } -void ti_free_texture(TiRuntime runtime, TiTexture devtex) { - ((Runtime *)runtime)->free_texture(devtex); +void ti_free_texture(TiRuntime runtime, TiTexture texture) { + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(texture); + + ((Runtime *)runtime)->free_texture(texture); } TiEvent ti_create_event(TiRuntime runtime) { + TI_CAPI_ARGUMENT_NULL_RV(runtime); + Runtime *runtime2 = (Runtime *)runtime; std::unique_ptr event = runtime2->get().create_event(); @@ -211,10 +289,7 @@ TiEvent ti_create_event(TiRuntime runtime) { return (TiEvent)event2; } void ti_destroy_event(TiEvent event) { - if (event == nullptr) { - TI_WARN("ignored attempt to destroy event of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(event); delete (Event *)event; } @@ -222,22 +297,13 @@ void ti_destroy_event(TiEvent event) { void ti_copy_memory_device_to_device(TiRuntime runtime, const TiMemorySlice *dst_memory, const TiMemorySlice *src_memory) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to copy memory on runtime of null handle"); - return; - } - if (dst_memory == nullptr || dst_memory->memory == nullptr) { - TI_WARN("ignored attempt to copy to dst memory of null handle"); - return; - } - if (src_memory == nullptr || src_memory->memory == nullptr) { - TI_WARN("ignored attempt to copy from src memory of null handle"); - return; - } - if (src_memory->size != dst_memory->size) { - TI_WARN("ignored attempt to copy memory of mismatched size"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(dst_memory); + TI_CAPI_ARGUMENT_NULL(dst_memory->memory); + TI_CAPI_ARGUMENT_NULL(src_memory); + TI_CAPI_ARGUMENT_NULL(src_memory->memory); + TI_CAPI_INVALID_ARGUMENT(dst_memory->memory != src_memory->memory); + Runtime *runtime2 = (Runtime *)runtime; auto dst = devmem2devalloc(*runtime2, dst_memory->memory) .get_ptr(dst_memory->offset); @@ -249,22 +315,20 @@ void ti_copy_memory_device_to_device(TiRuntime runtime, void ti_copy_texture_device_to_device(TiRuntime runtime, const TiTextureSlice *dst_texture, const TiTextureSlice *src_texture) { - if (dst_texture == nullptr || dst_texture->texture == nullptr) { - TI_WARN("ignored attempt to copy to dst texture of null handle"); - return; - } - if (src_texture == nullptr || src_texture->texture == nullptr) { - TI_WARN("ignored attempt to copy from src texture of null handle"); - return; - } - if (src_texture->extent.width != dst_texture->extent.width || - src_texture->extent.height != dst_texture->extent.height || - src_texture->extent.depth != dst_texture->extent.depth || - src_texture->extent.array_layer_count != - dst_texture->extent.array_layer_count) { - TI_WARN("ignored attempt to copy texture of mismatched extent"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(dst_texture); + TI_CAPI_ARGUMENT_NULL(dst_texture->texture); + TI_CAPI_ARGUMENT_NULL(src_texture); + TI_CAPI_ARGUMENT_NULL(src_texture->texture); + TI_CAPI_INVALID_ARGUMENT(src_texture->extent.width != + dst_texture->extent.width); + TI_CAPI_INVALID_ARGUMENT(src_texture->extent.height != + dst_texture->extent.height); + TI_CAPI_INVALID_ARGUMENT(src_texture->extent.depth != + dst_texture->extent.depth); + TI_CAPI_INVALID_ARGUMENT(src_texture->extent.array_layer_count != + dst_texture->extent.array_layer_count); + Runtime *runtime2 = (Runtime *)runtime; auto dst = devtex2devalloc(*runtime2, dst_texture->texture); auto src = devtex2devalloc(*runtime2, src_texture->texture); @@ -278,73 +342,84 @@ void ti_copy_texture_device_to_device(TiRuntime runtime, void ti_transition_texture(TiRuntime runtime, TiTexture texture, TiTextureLayout layout) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to transition memory on runtime of null handle"); - return; - } - if (texture == nullptr) { - TI_WARN("ignored attempt to transition texture of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(texture); Runtime *runtime2 = (Runtime *)runtime; auto image = devtex2devalloc(*runtime2, texture); auto layout2 = (taichi::lang::ImageLayout)layout; + switch ((taichi::lang::ImageLayout)layout) { +#define PER_IMAGE_LAYOUT(x) case taichi::lang::ImageLayout::x: +#include "taichi/inc/image_layout.inc.h" +#undef PER_IMAGE_LAYOUT + break; + default: { + ti_set_last_error(TI_ERROR_ARGUMENT_OUT_OF_RANGE, "layout"); + return; + } + } + runtime2->transition_image(image, layout2); } TiAotModule ti_load_aot_module(TiRuntime runtime, const char *module_path) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to load aot module on runtime of null handle"); - return TI_NULL_HANDLE; - } - if (module_path == nullptr) { - TI_WARN("ignored attempt to load aot module with null path"); + TI_CAPI_ARGUMENT_NULL_RV(runtime); + TI_CAPI_ARGUMENT_NULL_RV(module_path); + + TiAotModule aot_module = ((Runtime *)runtime)->load_aot_module(module_path); + + if (aot_module == TI_NULL_HANDLE) { + ti_set_last_error(TI_ERROR_CORRUPTED_DATA, module_path); return TI_NULL_HANDLE; } - - return ((Runtime *)runtime)->load_aot_module(module_path); + return aot_module; } -void ti_destroy_aot_module(TiAotModule mod) { - if (mod == nullptr) { - TI_WARN("ignored attempt to destroy aot module of null handle"); - return; - } +void ti_destroy_aot_module(TiAotModule aot_module) { + TI_CAPI_ARGUMENT_NULL(aot_module); - delete (AotModule *)mod; + delete (AotModule *)aot_module; } -TiKernel ti_get_aot_module_kernel(TiAotModule mod, const char *name) { - if (mod == nullptr) { - TI_WARN("ignored attempt to get kernel from aot module of null handle"); +TiKernel ti_get_aot_module_kernel(TiAotModule aot_module, const char *name) { + TI_CAPI_ARGUMENT_NULL_RV(aot_module); + TI_CAPI_ARGUMENT_NULL_RV(name); + + taichi::lang::aot::Kernel *kernel = + ((AotModule *)aot_module)->get_kernel(name); + + if (kernel == nullptr) { + ti_set_last_error(TI_ERROR_NAME_NOT_FOUND, name); return TI_NULL_HANDLE; } - return (TiKernel)((AotModule *)mod)->get().get_kernel(name); + + return (TiKernel)kernel; } -TiComputeGraph ti_get_aot_module_compute_graph(TiAotModule mod, +TiComputeGraph ti_get_aot_module_compute_graph(TiAotModule aot_module, const char *name) { - if (mod == nullptr) { - TI_WARN( - "ignored attempt to get compute graph from aot module of null handle"); + TI_CAPI_ARGUMENT_NULL_RV(aot_module); + TI_CAPI_ARGUMENT_NULL_RV(name); + + taichi::lang::aot::CompiledGraph *cgraph = + ((AotModule *)aot_module)->get_cgraph(name); + + if (cgraph == nullptr) { + ti_set_last_error(TI_ERROR_NAME_NOT_FOUND, name); return TI_NULL_HANDLE; } - AotModule *aot_module = ((AotModule *)mod); - return (TiComputeGraph)&aot_module->get_cgraph(name); + + return (TiComputeGraph)cgraph; } void ti_launch_kernel(TiRuntime runtime, TiKernel kernel, uint32_t arg_count, const TiArgument *args) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to launch kernel on runtime of null handle"); - return; - } - if (kernel == nullptr) { - TI_WARN("ignored attempt to launch kernel of null handle"); - return; + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(kernel); + if (arg_count > 0) { + TI_CAPI_ARGUMENT_NULL(args); } Runtime &runtime2 = *((Runtime *)runtime); @@ -363,17 +438,13 @@ void ti_launch_kernel(TiRuntime runtime, break; } case TI_ARGUMENT_TYPE_NDARRAY: { + TI_CAPI_ARGUMENT_NULL(args[i].value.ndarray.memory); + // Don't allocate it on stack. `DeviceAllocation` is referred to by // `GfxRuntime::launch_kernel`. std::unique_ptr devalloc = std::make_unique( devmem2devalloc(runtime2, arg.value.ndarray.memory)); - if (devalloc->alloc_id + 1 == 0) { - TI_WARN( - "ignored attempt to launch kernel with ndarray memory of null " - "handle"); - return; - } const TiNdArray &ndarray = arg.value.ndarray; std::vector shape(ndarray.shape.dims, @@ -384,8 +455,11 @@ void ti_launch_kernel(TiRuntime runtime, devallocs.emplace_back(std::move(devalloc)); break; } - default: - TI_ASSERT(false); + default: { + ti_set_last_error(TI_ERROR_ARGUMENT_OUT_OF_RANGE, + ("args[" + std::to_string(i) + "].type").c_str()); + return; + } } } ((taichi::lang::aot::Kernel *)kernel)->launch(&runtime_context); @@ -395,14 +469,10 @@ void ti_launch_compute_graph(TiRuntime runtime, TiComputeGraph compute_graph, uint32_t arg_count, const TiNamedArgument *args) { - if (runtime == nullptr) { - TI_WARN( - "ignored attempt to launch compute graph on runtime of null handle"); - return; - } - if (compute_graph == nullptr) { - TI_WARN("ignored attempt to launch compute graph of null handle"); - return; + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(compute_graph); + if (arg_count > 0) { + TI_CAPI_ARGUMENT_NULL(args); } Runtime &runtime2 = *((Runtime *)runtime); @@ -411,6 +481,8 @@ void ti_launch_compute_graph(TiRuntime runtime, ndarrays.reserve(arg_count); for (uint32_t i = 0; i < arg_count; ++i) { + TI_CAPI_ARGUMENT_NULL(args[i].name); + const auto &arg = args[i]; switch (arg.argument.type) { case TI_ARGUMENT_TYPE_I32: { @@ -426,14 +498,10 @@ void ti_launch_compute_graph(TiRuntime runtime, break; } case TI_ARGUMENT_TYPE_NDARRAY: { + TI_CAPI_ARGUMENT_NULL(args[i].argument.value.ndarray.memory); + taichi::lang::DeviceAllocation devalloc = devmem2devalloc(runtime2, arg.argument.value.ndarray.memory); - if (devalloc.alloc_id + 1 == 0) { - TI_WARN( - "ignored attempt to launch kernel with ndarray memory of null " - "handle"); - return; - } const TiNdArray &ndarray = arg.argument.value.ndarray; std::vector shape(ndarray.shape.dims, @@ -481,8 +549,13 @@ void ti_launch_compute_graph(TiRuntime runtime, case TI_DATA_TYPE_GEN: prim_ty = &taichi::lang::PrimitiveType::gen; break; - default: - TI_ERROR("unexpected data type"); + default: { + ti_set_last_error(TI_ERROR_ARGUMENT_OUT_OF_RANGE, + ("args[" + std::to_string(i) + + "].argument.value.ndarray.elem_type") + .c_str()); + return; + } } ndarrays.emplace_back( @@ -491,38 +564,45 @@ void ti_launch_compute_graph(TiRuntime runtime, arg.name, taichi::lang::aot::IValue::create(ndarrays.back()))); break; } - default: - TI_ASSERT(false); + default: { + ti_set_last_error( + TI_ERROR_ARGUMENT_OUT_OF_RANGE, + ("args[" + std::to_string(i) + "].argument.type").c_str()); + return; + } } } ((taichi::lang::aot::CompiledGraph *)compute_graph)->run(arg_map); } void ti_signal_event(TiRuntime runtime, TiEvent event) { + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(event); + ((Runtime *)runtime)->signal_event(&((Event *)event)->get()); } void ti_reset_event(TiRuntime runtime, TiEvent event) { + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(event); + ((Runtime *)runtime)->reset_event(&((Event *)event)->get()); } void ti_wait_event(TiRuntime runtime, TiEvent event) { + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(event); + ((Runtime *)runtime)->wait_event(&((Event *)event)->get()); } void ti_submit(TiRuntime runtime) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to submit to runtime of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); ((Runtime *)runtime)->submit(); } void ti_wait(TiRuntime runtime) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to wait on runtime of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); ((Runtime *)runtime)->wait(); } diff --git a/c_api/src/taichi_core_impl.h b/c_api/src/taichi_core_impl.h index 697718901c4ee..a6445f432cad6 100644 --- a/c_api/src/taichi_core_impl.h +++ b/c_api/src/taichi_core_impl.h @@ -9,6 +9,51 @@ #include "taichi/program/context.h" #undef TI_RUNTIME_HOST +// Error reporting. +#define TI_CAPI_NOT_SUPPORTED(x) ti_set_last_error(TI_ERROR_NOT_SUPPORTED, #x); +#define TI_CAPI_NOT_SUPPORTED_IF(x) \ + if (x) { \ + ti_set_last_error(TI_ERROR_NOT_SUPPORTED, #x); \ + } +#define TI_CAPI_NOT_SUPPORTED_IF_RV(x) \ + if (x) { \ + ti_set_last_error(TI_ERROR_NOT_SUPPORTED, #x); \ + return TI_NULL_HANDLE; \ + } + +#define TI_CAPI_ARGUMENT_NULL(x) \ + if (x == TI_NULL_HANDLE) { \ + ti_set_last_error(TI_ERROR_ARGUMENT_NULL, #x); \ + return; \ + } +#define TI_CAPI_ARGUMENT_NULL_RV(x) \ + if (x == TI_NULL_HANDLE) { \ + ti_set_last_error(TI_ERROR_ARGUMENT_NULL, #x); \ + return TI_NULL_HANDLE; \ + } + +#define TI_CAPI_INVALID_ARGUMENT(x) \ + if (x == TI_NULL_HANDLE) { \ + ti_set_last_error(TI_ERROR_INVALID_ARGUMENT, #x); \ + return; \ + } +#define TI_CAPI_INVALID_ARGUMENT_RV(x) \ + if (x == TI_NULL_HANDLE) { \ + ti_set_last_error(TI_ERROR_INVALID_ARGUMENT, #x); \ + return TI_NULL_HANDLE; \ + } + +#define TI_CAPI_INVALID_INTEROP_ARCH(x, arch) \ + if (x != taichi::Arch::arch) { \ + ti_set_last_error(TI_ERROR_INVALID_INTEROP, "arch!=" #arch); \ + return; \ + } +#define TI_CAPI_INVALID_INTEROP_ARCH_RV(x, arch) \ + if (x != taichi::Arch::arch) { \ + ti_set_last_error(TI_ERROR_INVALID_INTEROP, "arch!=" #arch); \ + return TI_NULL_HANDLE; \ + } + class Runtime; class Context; class AotModule; @@ -78,7 +123,8 @@ class AotModule { AotModule(Runtime &runtime, std::unique_ptr aot_module); - taichi::lang::aot::CompiledGraph &get_cgraph(const std::string &name); + taichi::lang::aot::Kernel *get_kernel(const std::string &name); + taichi::lang::aot::CompiledGraph *get_cgraph(const std::string &name); taichi::lang::aot::Module &get(); Runtime &runtime(); }; diff --git a/c_api/src/taichi_vulkan_impl.cpp b/c_api/src/taichi_vulkan_impl.cpp index a5473b2bc74b6..7124a57c48e87 100644 --- a/c_api/src/taichi_vulkan_impl.cpp +++ b/c_api/src/taichi_vulkan_impl.cpp @@ -123,6 +123,9 @@ TiAotModule VulkanRuntime::load_aot_module(const char *module_path) { params.runtime = &get_gfx_runtime(); std::unique_ptr aot_module = taichi::lang::aot::Module::load(arch, params); + if (aot_module->is_corrupted()) { + return TI_NULL_HANDLE; + } size_t root_size = aot_module->get_root_size(); params.runtime->add_root_buffer(root_size); return (TiAotModule)(new AotModule(*this, std::move(aot_module))); @@ -164,22 +167,31 @@ void VulkanRuntime::wait() { TiRuntime ti_create_vulkan_runtime_ext(uint32_t api_version, const char **instance_extensions, - uint32_t instance_extensions_count, + uint32_t instance_extension_count, const char **device_extensions, - uint32_t device_extensions_count) { + uint32_t device_extension_count) { if (api_version < VK_API_VERSION_1_0) { - TI_WARN("ignored attempt to create vulkan runtime of version <1.0"); + ti_set_last_error(TI_ERROR_ARGUMENT_OUT_OF_RANGE, "api_version<1.0"); return TI_NULL_HANDLE; } + if (instance_extension_count > 0) { + TI_CAPI_ARGUMENT_NULL_RV(instance_extensions); + } + if (device_extension_count > 0) { + TI_CAPI_ARGUMENT_NULL_RV(device_extensions); + } + taichi::lang::vulkan::VulkanDeviceCreator::Params params; params.api_version = api_version; params.is_for_ui = false; - params.additional_instance_extensions.reserve(instance_extensions_count); - for (uint32_t i = 0; i < instance_extensions_count; ++i) { + params.additional_instance_extensions.reserve(instance_extension_count); + for (uint32_t i = 0; i < instance_extension_count; ++i) { + TI_CAPI_ARGUMENT_NULL_RV(instance_extensions[i]); params.additional_instance_extensions.push_back(instance_extensions[i]); } - params.additional_device_extensions.reserve(device_extensions_count); - for (uint32_t i = 0; i < device_extensions_count; ++i) { + params.additional_device_extensions.reserve(device_extension_count); + for (uint32_t i = 0; i < device_extension_count; ++i) { + TI_CAPI_ARGUMENT_NULL_RV(device_extensions[i]); params.additional_device_extensions.push_back(device_extensions[i]); } params.surface_creator = nullptr; @@ -187,22 +199,11 @@ TiRuntime ti_create_vulkan_runtime_ext(uint32_t api_version, } TiRuntime ti_import_vulkan_runtime( const TiVulkanRuntimeInteropInfo *interop_info) { - if (interop_info->api_version < VK_API_VERSION_1_0) { - TI_WARN("ignored attempt to import vulkan runtime of version <1.0"); - return TI_NULL_HANDLE; - } - if (interop_info->physical_device == nullptr) { - TI_WARN( - "ignored attempt to import vulkan runtime with vulkan physical device " - "of null handle"); - return TI_NULL_HANDLE; - } - if (interop_info->device == nullptr) { - TI_WARN( - "ignored attempt to import vulkan runtime with vulkan device of null " - "handle"); - return TI_NULL_HANDLE; - } + TI_CAPI_ARGUMENT_NULL_RV(interop_info); + TI_CAPI_ARGUMENT_NULL_RV(interop_info->instance); + TI_CAPI_ARGUMENT_NULL_RV(interop_info->physical_device); + TI_CAPI_ARGUMENT_NULL_RV(interop_info->device); + taichi::lang::vulkan::VulkanDevice::Params params{}; params.instance = interop_info->instance; params.physical_device = interop_info->physical_device; @@ -217,12 +218,10 @@ TiRuntime ti_import_vulkan_runtime( } void ti_export_vulkan_runtime(TiRuntime runtime, TiVulkanRuntimeInteropInfo *interop_info) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to export vulkan runtime of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(interop_info); + Runtime *runtime2 = (Runtime *)runtime; - TI_ASSERT(runtime2->arch == taichi::Arch::vulkan); taichi::lang::vulkan::VulkanDevice &vk_device = static_cast(runtime2)->get_vk(); interop_info->api_version = @@ -241,16 +240,12 @@ void ti_export_vulkan_runtime(TiRuntime runtime, TiMemory ti_import_vulkan_memory( TiRuntime runtime, const TiVulkanMemoryInteropInfo *interop_info) { - if (runtime == nullptr) { - TI_WARN( - "ignored attempt to import vulkan memory to runtime of null handle"); - return TI_NULL_HANDLE; - } + TI_CAPI_ARGUMENT_NULL_RV(runtime); + TI_CAPI_ARGUMENT_NULL_RV(interop_info); + TI_CAPI_ARGUMENT_NULL_RV(interop_info->buffer); + TI_CAPI_INVALID_INTEROP_ARCH_RV(((Runtime *)runtime)->arch, vulkan); + Runtime *runtime2 = (Runtime *)runtime; - if (runtime2->arch != taichi::Arch::vulkan) { - TI_WARN("ignored attempt to import vulkan memory to non-vulkan runtime"); - return TI_NULL_HANDLE; - } taichi::lang::vulkan::VulkanDevice &vk_runtime = static_cast(runtime2)->get_vk(); @@ -261,19 +256,15 @@ TiMemory ti_import_vulkan_memory( return devalloc2devmem(*runtime2, devalloc); } void ti_export_vulkan_memory(TiRuntime runtime, - TiMemory devmem, + TiMemory memory, TiVulkanMemoryInteropInfo *interop_info) { - if (runtime == nullptr) { - TI_WARN( - "ignored attempt to export vulkan memory from runtime of null handle"); - return; - } - if (devmem == nullptr) { - TI_WARN("ignored attempt to export vulkan memory of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(memory); + TI_CAPI_ARGUMENT_NULL(interop_info); + TI_CAPI_INVALID_INTEROP_ARCH(((Runtime *)runtime)->arch, vulkan); + VulkanRuntime *runtime2 = ((Runtime *)runtime)->as_vk(); - taichi::lang::DeviceAllocation devalloc = devmem2devalloc(*runtime2, devmem); + taichi::lang::DeviceAllocation devalloc = devmem2devalloc(*runtime2, memory); vkapi::IVkBuffer buffer = runtime2->get_vk().get_vkbuffer(devalloc); interop_info->buffer = buffer.get()->buffer; interop_info->size = buffer.get()->size; @@ -284,16 +275,12 @@ TiTexture ti_import_vulkan_texture( const TiVulkanTextureInteropInfo *interop_info, VkImageViewType view_type, VkImageLayout layout) { - if (runtime == nullptr) { - TI_WARN( - "ignored attempt to import vulkan texture from runtime of null handle"); - return TI_NULL_HANDLE; - } + TI_CAPI_ARGUMENT_NULL_RV(runtime); + TI_CAPI_ARGUMENT_NULL_RV(interop_info); + TI_CAPI_ARGUMENT_NULL_RV(interop_info->image); + TI_CAPI_INVALID_INTEROP_ARCH_RV(((Runtime *)runtime)->arch, vulkan); + Runtime *runtime2 = ((Runtime *)runtime)->as_vk(); - if (runtime2->arch != taichi::Arch::vulkan) { - TI_WARN("ignored attempt to import vulkan texture to non-vulkan runtime"); - return TI_NULL_HANDLE; - } taichi::lang::vulkan::VulkanDevice &vk_runtime = static_cast(runtime2)->get_vk(); @@ -337,16 +324,13 @@ TiTexture ti_import_vulkan_texture( void ti_export_vulkan_texture(TiRuntime runtime, TiTexture texture, TiVulkanTextureInteropInfo *interop_info) { - if (runtime == nullptr) { - TI_WARN( - "ignored attempt to export vulkan texture from runtime of null handle"); - return; - } - if (texture == nullptr) { - TI_WARN("ignored attempt to export vulkan texture of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(texture); + TI_CAPI_ARGUMENT_NULL(interop_info); + TI_CAPI_INVALID_INTEROP_ARCH(((Runtime *)runtime)->arch, vulkan); + VulkanRuntime *runtime2 = ((Runtime *)runtime)->as_vk(); + taichi::lang::DeviceAllocation devalloc = devtex2devalloc(*runtime2, texture); vkapi::IVkImage image = std::get<0>(runtime2->get_vk().get_vk_image(devalloc)); @@ -364,15 +348,12 @@ void ti_export_vulkan_texture(TiRuntime runtime, TiEvent ti_import_vulkan_event(TiRuntime runtime, const TiVulkanEventInteropInfo *interop_info) { - if (runtime == nullptr) { - TI_WARN("ignored attempt to import vulkan event to runtime of null handle"); - return TI_NULL_HANDLE; - } + TI_CAPI_ARGUMENT_NULL_RV(runtime); + TI_CAPI_ARGUMENT_NULL_RV(interop_info); + TI_CAPI_ARGUMENT_NULL_RV(interop_info->event); + TI_CAPI_INVALID_INTEROP_ARCH_RV(((Runtime *)runtime)->arch, vulkan); + Runtime *runtime2 = (Runtime *)runtime; - if (runtime2->arch != taichi::Arch::vulkan) { - TI_WARN("ignored attempt to import vulkan memory to non-vulkan runtime"); - return TI_NULL_HANDLE; - } vkapi::IVkEvent event = std::make_unique(); event->device = runtime2->as_vk()->get_vk().vk_device(); @@ -387,15 +368,11 @@ TiEvent ti_import_vulkan_event(TiRuntime runtime, void ti_export_vulkan_event(TiRuntime runtime, TiEvent event, TiVulkanEventInteropInfo *interop_info) { - if (runtime == nullptr) { - TI_WARN( - "ignored attempt to export vulkan memory from runtime of null handle"); - return; - } - if (event == nullptr) { - TI_WARN("ignored attempt to export vulkan memory of null handle"); - return; - } + TI_CAPI_ARGUMENT_NULL(runtime); + TI_CAPI_ARGUMENT_NULL(event); + TI_CAPI_ARGUMENT_NULL(interop_info); + TI_CAPI_INVALID_INTEROP_ARCH(((Runtime *)runtime)->arch, vulkan); + auto event2 = (taichi::lang::vulkan::VulkanDeviceEvent *)(&((Event *)event)->get()); interop_info->event = event2->vkapi_ref->event; diff --git a/c_api/taichi.json b/c_api/taichi.json index 331a88a925ada..50130408f0fe5 100644 --- a/c_api/taichi.json +++ b/c_api/taichi.json @@ -75,6 +75,22 @@ "type": "handle", "is_dispatchable": false }, + { + "name": "error", + "type": "enumeration", + "cases": { + "incomplete": 1, + "success": 0, + "not_supported": -1, + "corrupted_data": -2, + "name_not_found": -3, + "invalid_argument": -4, + "argument_null": -5, + "argument_out_of_range": -6, + "argument_not_found": -7, + "invalid_interop": -8 + } + }, { "name": "arch", "type": "enumeration", @@ -356,6 +372,39 @@ } ] }, + { + "name": "get_last_error", + "type": "function", + "parameters": [ + { + "name": "@return", + "type": "enumeration.error" + }, + { + "name": "message_size", + "type": "uint64_t" + }, + { + "name": "message", + "type": "char", + "count": "message_size", + "by_mut": true + } + ] + }, + { + "name": "set_last_error", + "type": "function", + "parameters": [ + { + "type": "enumeration.error" + }, + { + "name": "message", + "type": "const char*" + } + ] + }, { "name": "create_runtime", "type": "function", diff --git a/misc/generate_c_api.py b/misc/generate_c_api.py index 93b486656d225..49e3e26f92c8d 100644 --- a/misc/generate_c_api.py +++ b/misc/generate_c_api.py @@ -167,6 +167,8 @@ def generate_module_header(module): BuiltInType("VkImageTiling", "VkImageTiling"), BuiltInType("VkImageLayout", "VkImageLayout"), BuiltInType("VkImageUsageFlags", "VkImageUsageFlags"), + BuiltInType("VkImageViewType", "VkImageViewType"), + BuiltInType("char", "char"), } for module in Module.load_all(builtin_tys): diff --git a/taichi/aot/module_loader.h b/taichi/aot/module_loader.h index 192c217f25e5c..3ffe0d1a41842 100644 --- a/taichi/aot/module_loader.h +++ b/taichi/aot/module_loader.h @@ -90,18 +90,27 @@ class TI_DLL_EXPORT Module { KernelTemplate *get_kernel_template(const std::string &name); Field *get_snode_tree(const std::string &name); - virtual std::unique_ptr get_graph(std::string name) { + virtual std::unique_ptr get_graph( + const std::string &name) { TI_NOT_IMPLEMENTED; } + inline bool is_corrupted() const { + return is_corrupted_; + } + protected: virtual std::unique_ptr make_new_kernel(const std::string &name) = 0; virtual std::unique_ptr make_new_kernel_template( const std::string &name) = 0; virtual std::unique_ptr make_new_field(const std::string &name) = 0; + inline void mark_corrupted() { + is_corrupted_ = true; + } std::unordered_map graphs_; private: + bool is_corrupted_{false}; std::unordered_map> loaded_kernels_; std::unordered_map> loaded_kernel_templates_; diff --git a/taichi/common/serialization.h b/taichi/common/serialization.h index eefb4d7f5c73a..84d77a6c165dd 100644 --- a/taichi/common/serialization.h +++ b/taichi/common/serialization.h @@ -222,7 +222,7 @@ inline std::vector read_data_from_file(const std::string &fn) { std::vector data; std::FILE *f = fopen(fn.c_str(), "rb"); if (f == nullptr) { - TI_ERROR("Cannot open file: {}", fn); + TI_DEBUG("Cannot open file: {}", fn); return std::vector(); } if (ends_with(fn, ".zip")) { @@ -288,11 +288,15 @@ class BinarySerializer : public Serializer { using Base::assets; template - typename std::enable_if::type initialize( + typename std::enable_if::type initialize( const std::string &fn) { data = read_data_from_file(fn); + if (data.size() == 0) { + return false; + } c_data = reinterpret_cast(&data[0]); head = sizeof(std::size_t); + return true; } void write_to_file(const std::string &fn) { @@ -305,7 +309,7 @@ class BinarySerializer : public Serializer { } template - typename std::enable_if::type initialize( + typename std::enable_if::type initialize( std::size_t preserved_ = std::size_t(0), void *c_data = nullptr) { std::size_t n = 0; @@ -322,6 +326,7 @@ class BinarySerializer : public Serializer { this->c_data = nullptr; } this->operator()("", n); + return true; } template @@ -874,12 +879,16 @@ operator<<(std::ostream &os, const T &t) { return os; } +// Returns true if deserialization succeeded. template -void read_from_binary_file(T &t, const std::string &file_name) { +bool read_from_binary_file(T &t, const std::string &file_name) { BinaryInputSerializer reader; - reader.initialize(file_name); + if (!reader.initialize(file_name)) { + return false; + } reader(t); reader.finalize(); + return true; } template diff --git a/taichi/runtime/gfx/aot_module_loader_impl.cpp b/taichi/runtime/gfx/aot_module_loader_impl.cpp index 497636b5b7c2c..916221f13d4b2 100644 --- a/taichi/runtime/gfx/aot_module_loader_impl.cpp +++ b/taichi/runtime/gfx/aot_module_loader_impl.cpp @@ -27,7 +27,10 @@ class AotModuleImpl : public aot::Module { : runtime_(params.runtime), device_api_backend_(device_api_backend) { const std::string bin_path = fmt::format("{}/metadata.tcb", params.module_path); - read_from_binary_file(ti_aot_data_, bin_path); + if (!read_from_binary_file(ti_aot_data_, bin_path)) { + mark_corrupted(); + return; + } for (int i = 0; i < ti_aot_data_.kernels.size(); ++i) { auto k = ti_aot_data_.kernels[i]; @@ -36,6 +39,10 @@ class AotModuleImpl : public aot::Module { for (int j = 0; j < k.tasks_attribs.size(); ++j) { std::vector res = read_spv_file(params.module_path, k.tasks_attribs[j]); + if (res.size() == 0) { + mark_corrupted(); + return; + } spirv_sources_codes.push_back(res); } ti_aot_data_.spirv_codes.push_back(spirv_sources_codes); @@ -43,13 +50,22 @@ class AotModuleImpl : public aot::Module { const std::string graph_path = fmt::format("{}/graphs.tcb", params.module_path); - read_from_binary_file(graphs_, graph_path); + if (!read_from_binary_file(graphs_, graph_path)) { + mark_corrupted(); + return; + } } - std::unique_ptr get_graph(std::string name) override { - TI_ERROR_IF(graphs_.count(name) == 0, "Cannot find graph {}", name); + std::unique_ptr get_graph( + const std::string &name) override { + auto it = graphs_.find(name); + if (it == graphs_.end()) { + TI_DEBUG("Cannot find graph {}", name); + return nullptr; + } + std::vector dispatches; - for (auto &dispatch : graphs_[name].dispatches) { + for (auto &dispatch : it->second.dispatches) { dispatches.push_back({dispatch.kernel_name, dispatch.symbolic_args, get_kernel(dispatch.kernel_name)}); } diff --git a/taichi/runtime/llvm/llvm_aot_module_loader.cpp b/taichi/runtime/llvm/llvm_aot_module_loader.cpp index 5c66d91358557..028483c96e780 100644 --- a/taichi/runtime/llvm/llvm_aot_module_loader.cpp +++ b/taichi/runtime/llvm/llvm_aot_module_loader.cpp @@ -40,10 +40,16 @@ std::unique_ptr LlvmAotModule::make_new_field( return std::make_unique(std::move(loaded)); } -std::unique_ptr LlvmAotModule::get_graph(std::string name) { - TI_ERROR_IF(graphs_.count(name) == 0, "Cannot find graph {}", name); +std::unique_ptr LlvmAotModule::get_graph( + const std::string &name) { + auto it = graphs_.find(name); + if (it == graphs_.end()) { + TI_DEBUG("Cannot find graph {}", name); + return nullptr; + } + std::vector dispatches; - for (auto &dispatch : graphs_[name].dispatches) { + for (auto &dispatch : it->second.dispatches) { dispatches.push_back({dispatch.kernel_name, dispatch.symbolic_args, get_kernel(dispatch.kernel_name)}); } diff --git a/taichi/runtime/llvm/llvm_aot_module_loader.h b/taichi/runtime/llvm/llvm_aot_module_loader.h index 2a8b3582eda1c..cbb1f0f1d40e6 100644 --- a/taichi/runtime/llvm/llvm_aot_module_loader.h +++ b/taichi/runtime/llvm/llvm_aot_module_loader.h @@ -54,7 +54,8 @@ class LlvmAotModule : public aot::Module { return initialized_snode_tree_ids.count(snode_tree_id); } - std::unique_ptr get_graph(std::string name) override; + std::unique_ptr get_graph( + const std::string &name) override; protected: virtual FunctionType convert_module_to_function(