blob: 46d9320248a9bdc147e83b9257a8559278720c1a [file]
use std::ffi::{CStr, c_char};
use std::sync::OnceLock;
use super::ffi::{Module, TargetMachine, Value};
type LLVMRustBundleImagesFn = unsafe extern "C" fn(&Module, &TargetMachine, *const c_char) -> bool;
type LLVMRustOffloadEmbedBufferInModuleFn = unsafe extern "C" fn(&Module, *const c_char) -> bool;
type LLVMRustOffloadMapperFn = unsafe extern "C" fn(&Value, &Value, *const &Value);
use rustc_session::config::host_tuple;
use rustc_session::filesearch;
use crate::llvm::LLVMRustVersionMajor;
pub(crate) struct RustOffloadWrapper {
LLVMRustBundleImages: LLVMRustBundleImagesFn,
LLVMRustOffloadEmbedBufferInModule: LLVMRustOffloadEmbedBufferInModuleFn,
LLVMRustOffloadMapper: LLVMRustOffloadMapperFn,
// Keep the dynamic library loaded while the function pointers are used.
_lib: libloading::Library,
}
#[derive(Debug)]
pub(crate) enum RustOffloadLibraryError {
NotFound { err: String },
LoadFailed { err: String },
}
impl From<libloading::Error> for RustOffloadLibraryError {
fn from(err: libloading::Error) -> Self {
Self::LoadFailed { err: format!("{err:?}") }
}
}
static OFFLOAD_INSTANCE: OnceLock<RustOffloadWrapper> = OnceLock::new();
impl RustOffloadWrapper {
pub(crate) fn get_or_init(
sysroot: &rustc_session::config::Sysroot,
) -> Result<&'static RustOffloadWrapper, RustOffloadLibraryError> {
OFFLOAD_INSTANCE.get_or_try_init(|| {
let w = Self::call_dynamic(sysroot)?;
Ok(w)
})
}
pub(crate) fn get_instance() -> &'static RustOffloadWrapper {
OFFLOAD_INSTANCE
.get()
.expect("RustOffloadWrapper not initialized. Call get_or_init with sysroot first.")
}
pub(crate) unsafe fn llvm_rust_bundle_images(
&self,
m: &Module,
tm: &TargetMachine,
c: &CStr,
) -> bool {
unsafe { (self.LLVMRustBundleImages)(m, tm, c.as_ptr()) }
}
pub(crate) unsafe fn llvm_rust_offload_embed_buffer_in_module(
&self,
m: &Module,
i: &CStr,
) -> bool {
unsafe { (self.LLVMRustOffloadEmbedBufferInModule)(m, i.as_ptr()) }
}
pub(crate) unsafe fn llvm_rust_offload_wrapper(&self, v1: &Value, v2: &Value, vs: &[&Value]) {
unsafe { (self.LLVMRustOffloadMapper)(v1, v2, vs.as_ptr()) }
}
fn call_dynamic(
sysroot: &rustc_session::config::Sysroot,
) -> Result<Self, RustOffloadLibraryError> {
let rust_offload_path = Self::get_rust_offload_path(sysroot)?;
let lib = unsafe { libloading::Library::new(rust_offload_path)? };
let llvm_rust_bundle_images =
*unsafe { lib.get::<LLVMRustBundleImagesFn>(b"LLVMRustBundleImages\0")? };
let llvm_rust_offload_embed_buffer_in_module = *unsafe {
lib.get::<LLVMRustOffloadEmbedBufferInModuleFn>(
b"LLVMRustOffloadEmbedBufferInModule\0",
)?
};
let llvm_rust_offload_wrapper =
*unsafe { lib.get::<LLVMRustOffloadMapperFn>(b"LLVMRustOffloadMapper\0")? };
Ok(Self {
LLVMRustBundleImages: llvm_rust_bundle_images,
LLVMRustOffloadEmbedBufferInModule: llvm_rust_offload_embed_buffer_in_module,
LLVMRustOffloadMapper: llvm_rust_offload_wrapper,
_lib: lib,
})
}
fn get_rust_offload_path(
sysroot: &rustc_session::config::Sysroot,
) -> Result<String, RustOffloadLibraryError> {
let llvm_version_major = unsafe { LLVMRustVersionMajor() };
let path_buf = sysroot
.all_paths()
.find_map(|p| {
let candidate = filesearch::make_target_lib_path(p, host_tuple())
.join(format!("libRustOffload-{}", llvm_version_major))
.with_extension(std::env::consts::DLL_EXTENSION);
candidate.exists().then_some(candidate)
})
.ok_or_else(|| {
let candidates = sysroot
.all_paths()
.map(|p| p.join("lib").display().to_string())
.collect::<Vec<String>>()
.join("\n* ");
RustOffloadLibraryError::NotFound {
err: format!(
"failed to find a `libRustOffload-{llvm_version_major}` \
in the sysroot candidates:\n* {candidates}"
),
}
})?;
Ok(path_buf
.to_str()
.ok_or_else(|| RustOffloadLibraryError::LoadFailed {
err: format!("invalid UTF-8 in path: {}", path_buf.display()),
})?
.to_string())
}
}