From 0b47758a30afec430933834c91fa67eb7515a5fe Mon Sep 17 00:00:00 2001 From: Yuval Shekel Date: Thu, 13 Jun 2024 17:04:26 +0300 Subject: [PATCH] rust runtime crate --- icicle_v3/include/icicle/runtime.h | 2 +- icicle_v3/src/runtime.cpp | 24 +-- wrappers/rust_v3/Cargo.toml | 16 ++ wrappers/rust_v3/icicle-runtime/Cargo.toml | 14 ++ wrappers/rust_v3/icicle-runtime/src/device.rs | 25 ++++ wrappers/rust_v3/icicle-runtime/src/errors.rs | 17 +++ wrappers/rust_v3/icicle-runtime/src/lib.rs | 26 ++++ .../rust_v3/icicle-runtime/src/runtime.rs | 137 ++++++++++++++++++ 8 files changed, 251 insertions(+), 10 deletions(-) create mode 100644 wrappers/rust_v3/Cargo.toml create mode 100644 wrappers/rust_v3/icicle-runtime/Cargo.toml create mode 100644 wrappers/rust_v3/icicle-runtime/src/device.rs create mode 100644 wrappers/rust_v3/icicle-runtime/src/errors.rs create mode 100644 wrappers/rust_v3/icicle-runtime/src/lib.rs create mode 100644 wrappers/rust_v3/icicle-runtime/src/runtime.rs diff --git a/icicle_v3/include/icicle/runtime.h b/icicle_v3/include/icicle/runtime.h index ba44e35d4..1e648785a 100644 --- a/icicle_v3/include/icicle/runtime.h +++ b/icicle_v3/include/icicle/runtime.h @@ -14,7 +14,7 @@ using namespace icicle; * @param path Path of the backend library or directory where backend libraries are installed * @return eIcicleError Status of the loaded backend */ -extern "C" eIcicleError icicle_load_backend(const std::string& path, bool is_recursive); +extern "C" eIcicleError icicle_load_backend(const char* path, bool is_recursive); /** * @brief Set active device for thread diff --git a/icicle_v3/src/runtime.cpp b/icicle_v3/src/runtime.cpp index d1d990d93..34772c42f 100644 --- a/icicle_v3/src/runtime.cpp +++ b/icicle_v3/src/runtime.cpp @@ -11,6 +11,12 @@ using namespace icicle; +extern "C" eIcicleError icicle_nop() +{ + std::cout << "HELLO icicle V3" << std::endl; + return eIcicleError::SUCCESS; +} + extern "C" eIcicleError icicle_set_device(const Device& device) { return DeviceAPI::set_thread_local_device(device); } extern "C" eIcicleError icicle_malloc(void** ptr, size_t size) @@ -89,11 +95,11 @@ const std::string SHARED_LIB_EXTENSION = ".dylib"; #error "Unsupported operating system" #endif -extern "C" eIcicleError icicle_load_backend(const std::string& path, bool is_recursive) +extern "C" eIcicleError icicle_load_backend(const char* path, bool is_recursive) { - auto is_directory = [](const std::string& path) { + auto is_directory = [](const char* path) { struct stat pathStat; - if (stat(path.c_str(), &pathStat) != 0) { return false; } + if (stat(path, &pathStat) != 0) { return false; } return S_ISDIR(pathStat.st_mode); }; @@ -103,15 +109,15 @@ extern "C" eIcicleError icicle_load_backend(const std::string& path, bool is_rec filename.size() - SHARED_LIB_EXTENSION.size(), SHARED_LIB_EXTENSION.size(), SHARED_LIB_EXTENSION) == 0; }; - auto load_library = [](const std::string& filePath) { + auto load_library = [](const char* filePath) { ICICLE_LOG_DEBUG << "Attempting load: " << filePath; - void* handle = dlopen(filePath.c_str(), RTLD_LAZY | RTLD_GLOBAL); + void* handle = dlopen(filePath, RTLD_LAZY | RTLD_GLOBAL); if (!handle) { ICICLE_LOG_ERROR << "Failed to load " << filePath << ": " << dlerror(); } }; if (is_directory(path)) { // Path is a directory, recursively search for libraries - DIR* dir = opendir(path.c_str()); + DIR* dir = opendir(path); if (!dir) { ICICLE_LOG_ERROR << "Cannot open directory: " << path; return eIcicleError::INVALID_ARGUMENT; @@ -119,14 +125,14 @@ extern "C" eIcicleError icicle_load_backend(const std::string& path, bool is_rec struct dirent* entry; while ((entry = readdir(dir)) != nullptr) { - std::string entryPath = path + "/" + entry->d_name; + const std::string& entryPath = std::string(path) + "/" + entry->d_name; // Skip "." and ".." entries if (std::string(entry->d_name) == "." || std::string(entry->d_name) == "..") { continue; } // Recurse into subdirectories and load libraries in files - const bool is_nested_dir = is_directory(entryPath); - if (is_recursive || !is_nested_dir) { icicle_load_backend(entryPath, is_recursive); } + const bool is_nested_dir = is_directory(entryPath.c_str()); + if (is_recursive || !is_nested_dir) { icicle_load_backend(entryPath.c_str(), is_recursive); } } closedir(dir); diff --git a/wrappers/rust_v3/Cargo.toml b/wrappers/rust_v3/Cargo.toml new file mode 100644 index 000000000..eae64ce1e --- /dev/null +++ b/wrappers/rust_v3/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] +resolver = "2" +members = [ + "icicle-runtime", +] +exclude = [] + +[workspace.package] +version = "3.0.0" +edition = "2021" +authors = [ "Ingonyama" ] +homepage = "https://www.ingonyama.com" +repository = "https://github.com/ingonyama-zk/icicle" + +[workspace.dependencies] +icicle-cuda-runtime = { path = "icicle-runtime" } diff --git a/wrappers/rust_v3/icicle-runtime/Cargo.toml b/wrappers/rust_v3/icicle-runtime/Cargo.toml new file mode 100644 index 000000000..e2752a866 --- /dev/null +++ b/wrappers/rust_v3/icicle-runtime/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "icicle-runtime" +version.workspace = true +edition.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[build-dependencies] +cmake = "0.1.50" diff --git a/wrappers/rust_v3/icicle-runtime/src/device.rs b/wrappers/rust_v3/icicle-runtime/src/device.rs new file mode 100644 index 000000000..16a88ed47 --- /dev/null +++ b/wrappers/rust_v3/icicle-runtime/src/device.rs @@ -0,0 +1,25 @@ +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +#[repr(C)] +pub struct Device { + pub device_type: *const std::os::raw::c_char, + pub id: i32, +} + +#[repr(C)] +pub struct DeviceProperties { + pub using_host_memory: bool, + pub num_memory_regions: i32, + pub supports_pinned_memory: bool, +} + +impl Device { + pub fn new(device_type: &str, id: i32) -> Device { + let c_string = CString::new(device_type).expect("CString::new failed"); + Device { + device_type: c_string.into_raw(), + id, + } + } +} diff --git a/wrappers/rust_v3/icicle-runtime/src/errors.rs b/wrappers/rust_v3/icicle-runtime/src/errors.rs new file mode 100644 index 000000000..a62500598 --- /dev/null +++ b/wrappers/rust_v3/icicle-runtime/src/errors.rs @@ -0,0 +1,17 @@ +#[repr(C)] +#[derive(Debug, PartialEq)] +pub enum eIcicleError { + Success = 0, // Operation completed successfully + InvalidDevice, // The specified device is invalid + OutOfMemory, // Memory allocation failed due to insufficient memory + InvalidPointer, // The specified pointer is invalid + AllocationFailed, // Memory allocation failed + DeallocationFailed, // Memory deallocation failed + CopyFailed, // Data copy operation failed + SynchronizationFailed, // Device synchronization failed + StreamCreationFailed, // Stream creation failed + StreamDestructionFailed, // Stream destruction failed + ApiNotImplemented, // The API is not implemented for a device + InvalidArgument, // Invalid argument passed + UnknownError, // An unknown error occurred +} diff --git a/wrappers/rust_v3/icicle-runtime/src/lib.rs b/wrappers/rust_v3/icicle-runtime/src/lib.rs new file mode 100644 index 000000000..230750b81 --- /dev/null +++ b/wrappers/rust_v3/icicle-runtime/src/lib.rs @@ -0,0 +1,26 @@ +pub mod device; +pub mod errors; +pub mod runtime; + +// Re-export the types for easier access +pub use device::{Device, DeviceProperties}; +pub use errors::eIcicleError; +pub use runtime::*; + +use std::ffi::CString; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_set_device() { + // load backends to process + let backend_install_dir = env!("DEFAULT_BACKEND_INSTALL_DIR"); + assert_eq!(load_backend(&backend_install_dir, true), eIcicleError::Success); + + let devtype = String::from("CPU"); + let dev = Device::new(&devtype, 0); + assert_eq!(set_device(&dev), eIcicleError::Success); + } +} diff --git a/wrappers/rust_v3/icicle-runtime/src/runtime.rs b/wrappers/rust_v3/icicle-runtime/src/runtime.rs new file mode 100644 index 000000000..68af93f7a --- /dev/null +++ b/wrappers/rust_v3/icicle-runtime/src/runtime.rs @@ -0,0 +1,137 @@ +use std::ffi::CString; +use std::os::raw::{c_char, c_void}; + +use crate::device::Device; +use crate::errors::eIcicleError; + +pub type IcicleStreamHandle = *mut c_void; + +extern "C" { + fn icicle_nop() -> eIcicleError; + fn icicle_load_backend(path: *const c_char, is_recursive: bool) -> eIcicleError; + fn icicle_set_device(device: &Device) -> eIcicleError; + // fn icicle_malloc(ptr: *mut *mut c_void, size: usize) -> eIcicleError; + // fn icicle_malloc_async(ptr: *mut *mut c_void, size: usize, stream: IcicleStreamHandle) -> eIcicleError; + // fn icicle_free(ptr: *mut c_void) -> eIcicleError; + // fn icicle_free_async(ptr: *mut c_void, stream: IcicleStreamHandle) -> eIcicleError; + // fn icicle_get_available_memory(total: *mut usize, free: *mut usize) -> eIcicleError; + // fn icicle_copy_to_host(dst: *mut u8, src: *const u8, size: usize) -> eIcicleError; + // fn icicle_copy_to_host_async(dst: *mut u8, src: *const u8, size: usize, stream: IcicleStreamHandle) + // -> eIcicleError; + // fn icicle_copy_to_device(dst: *mut u8, src: *const u8, size: usize) -> eIcicleError; + // fn icicle_copy_to_device_async( + // dst: *mut u8, + // src: *const u8, + // size: usize, + // stream: IcicleStreamHandle, + // ) -> eIcicleError; + // fn icicle_create_stream(stream: *mut IcicleStreamHandle) -> eIcicleError; + // fn icicle_destroy_stream(stream: IcicleStreamHandle) -> eIcicleError; + // fn icicle_stream_synchronize(stream: IcicleStreamHandle) -> eIcicleError; + fn icicle_device_synchronize() -> eIcicleError; + // fn icicle_get_device_properties(properties: *mut DeviceProperties) -> eIcicleError; +} + +pub fn nop() -> eIcicleError { + unsafe { icicle_nop() } +} + +pub fn load_backend(path: &str, is_recursive: bool) -> eIcicleError { + let c_path = CString::new(path).unwrap(); + unsafe { icicle_load_backend(c_path.as_ptr(), is_recursive) } +} + +pub fn set_device(device: &Device) -> eIcicleError { + unsafe { icicle_set_device(device) } +} + +// pub fn malloc(size: usize) -> Result<*mut c_void, eIcicleError> { +// let mut ptr: *mut c_void = std::ptr::null_mut(); +// let result = unsafe { icicle_malloc(&mut ptr, size) }; +// if result == eIcicleError::Success { +// Ok(ptr) +// } else { +// Err(result) +// } +// } + +// pub fn malloc_async(size: usize, stream: IcicleStreamHandle) -> Result<*mut c_void, eIcicleError> { +// let mut ptr: *mut c_void = std::ptr::null_mut(); +// let result = unsafe { icicle_malloc_async(&mut ptr, size, stream) }; +// if result == eIcicleError::Success { +// Ok(ptr) +// } else { +// Err(result) +// } +// } + +// pub fn free(ptr: *mut c_void) -> eIcicleError { +// unsafe { icicle_free(ptr) } +// } + +// pub fn free_async(ptr: *mut c_void, stream: IcicleStreamHandle) -> eIcicleError { +// unsafe { icicle_free_async(ptr, stream) } +// } + +// pub fn get_available_memory() -> Result<(usize, usize), eIcicleError> { +// let mut total: usize = 0; +// let mut free: usize = 0; +// let result = unsafe { icicle_get_available_memory(&mut total, &mut free) }; +// if result == eIcicleError::Success { +// Ok((total, free)) +// } else { +// Err(result) +// } +// } + +// pub fn copy_to_host(dst: *mut u8, src: *const u8, size: usize) -> eIcicleError { +// unsafe { icicle_copy_to_host(dst, src, size) } +// } + +// pub fn copy_to_host_async(dst: *mut u8, src: *const u8, size: usize, stream: IcicleStreamHandle) -> eIcicleError { +// unsafe { icicle_copy_to_host_async(dst, src, size, stream) } +// } + +// pub fn copy_to_device(dst: *mut u8, src: *const u8, size: usize) -> eIcicleError { +// unsafe { icicle_copy_to_device(dst, src, size) } +// } + +// pub fn copy_to_device_async(dst: *mut u8, src: *const u8, size: usize, stream: IcicleStreamHandle) -> eIcicleError { +// unsafe { icicle_copy_to_device_async(dst, src, size, stream) } +// } + +// pub fn create_stream() -> Result { +// let mut stream: IcicleStreamHandle = std::ptr::null_mut(); +// let result = unsafe { icicle_create_stream(&mut stream) }; +// if result == eIcicleError::Success { +// Ok(stream) +// } else { +// Err(result) +// } +// } + +// pub fn destroy_stream(stream: IcicleStreamHandle) -> eIcicleError { +// unsafe { icicle_destroy_stream(stream) } +// } + +// pub fn stream_synchronize(stream: IcicleStreamHandle) -> eIcicleError { +// unsafe { icicle_stream_synchronize(stream) } +// } + +pub fn device_synchronize() -> eIcicleError { + unsafe { icicle_device_synchronize() } +} + +// pub fn get_device_properties() -> Result { +// let mut properties = DeviceProperties { +// using_host_memory: false, +// num_memory_regions: 0, +// supports_pinned_memory: false, +// }; +// let result = unsafe { icicle_get_device_properties(&mut properties) }; +// if result == eIcicleError::Success { +// Ok(properties) +// } else { +// Err(result) +// } +// }