#![allow(unsafe_code)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
use std::os::raw::c_void;
use crate::dag::builder::DagBuilder;
use crate::ffi::types::RssnStatus;
#[cfg(feature = "cranelift-jit")]
use std::sync::{Mutex, OnceLock};
#[cfg(feature = "cranelift-jit")]
pub struct RssnJitContext {
compiler: crate::jit::compiler::JitCompiler,
}
#[cfg(feature = "cranelift-jit")]
static GLOBAL_JIT_CTX: OnceLock<Mutex<RssnJitContext>> = OnceLock::new();
#[cfg(feature = "cranelift-jit")]
pub(crate) fn global_jit_ctx() -> &'static Mutex<RssnJitContext> {
GLOBAL_JIT_CTX.get_or_init(|| {
Mutex::new(RssnJitContext {
compiler: crate::jit::compiler::JitCompiler::new(),
})
})
}
#[cfg(feature = "cranelift-jit")]
impl RssnJitContext {
#[inline]
pub(crate) const fn compiler_mut(&mut self) -> &mut crate::jit::compiler::JitCompiler {
&mut self.compiler
}
}
#[cfg(feature = "cranelift-jit")]
#[unsafe(no_mangle)]
pub extern "C" fn rssn_jit_context_new() -> *mut RssnJitContext {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
Box::into_raw(Box::new(RssnJitContext {
compiler: crate::jit::compiler::JitCompiler::new(),
}))
}));
result.unwrap_or(std::ptr::null_mut())
}
#[cfg(feature = "cranelift-jit")]
#[unsafe(no_mangle)]
pub extern "C" fn rssn_jit_context_free(ctx: *mut RssnJitContext) {
if ctx.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _ = unsafe { Box::from_raw(ctx) };
}));
}
#[cfg(feature = "cranelift-jit")]
#[unsafe(no_mangle)]
pub extern "C" fn rssn_dag_compile_with_ctx(
ctx: *mut RssnJitContext,
builder: *mut DagBuilder,
root: u32,
out_fn: *mut *mut c_void,
) -> RssnStatus {
if ctx.is_null() || builder.is_null() || out_fn.is_null() {
return RssnStatus::NullPointer;
}
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let ctx_ref = unsafe { &mut *ctx };
let builder_ref = unsafe { &mut *builder };
let root_id = crate::dag::node::DagNodeId::new(root);
let ast = crate::ast::convert::dag_to_ast(builder_ref.arena(), root_id);
ctx_ref
.compiler_mut()
.compile(&ast)
.map_or(RssnStatus::CompilationError, |compiled_fn| {
unsafe { *out_fn = compiled_fn as *mut c_void };
RssnStatus::Success
})
}));
result.unwrap_or(RssnStatus::Panic)
}
#[cfg(not(feature = "cranelift-jit"))]
pub struct RssnJitContext;
#[cfg(not(feature = "cranelift-jit"))]
#[unsafe(no_mangle)]
pub extern "C" fn rssn_jit_context_new() -> *mut RssnJitContext {
std::ptr::null_mut()
}
#[cfg(not(feature = "cranelift-jit"))]
#[unsafe(no_mangle)]
pub extern "C" fn rssn_jit_context_free(_ctx: *mut RssnJitContext) {}
#[cfg(not(feature = "cranelift-jit"))]
#[unsafe(no_mangle)]
pub extern "C" fn rssn_dag_compile_with_ctx(
_ctx: *mut RssnJitContext,
_builder: *mut DagBuilder,
_root: u32,
_out_fn: *mut *mut c_void,
) -> RssnStatus {
RssnStatus::CompilationError
}
#[cfg(all(test, feature = "cranelift-jit"))]
mod tests {
use super::*;
use crate::dag::builder::DagBuilder;
use crate::parser::expr::parse_expression;
#[test]
fn jit_context_compile_and_execute() {
let ctx = rssn_jit_context_new();
assert!(!ctx.is_null());
let mut b = DagBuilder::new();
let _id = parse_expression("x + 1.0", &mut b).expect("parse");
let builder_ptr: *mut DagBuilder = &mut b;
let mut fn_ptr: *mut c_void = std::ptr::null_mut();
let status =
rssn_dag_compile_with_ctx(ctx, builder_ptr, b.arena().len() as u32 - 1, &mut fn_ptr);
assert_eq!(status, RssnStatus::Success);
assert!(!fn_ptr.is_null());
rssn_jit_context_free(ctx);
}
#[test]
fn null_context_returns_null_pointer_status() {
let mut b = DagBuilder::new();
let _ = b.variable("x");
let mut fn_ptr: *mut c_void = std::ptr::null_mut();
let status = rssn_dag_compile_with_ctx(std::ptr::null_mut(), &mut b, 0, &mut fn_ptr);
assert_eq!(status, RssnStatus::NullPointer);
}
}