use crate::atom::Atom;
use crate::gc;
use crate::interpreter::{ExecutionResult, run_with_registry};
use crate::module::ResolvedImportTarget;
use crate::process::{CodePosition, ExitReason, JitStatus, Process};
use crate::term::Term;
use crate::term::boxed::write_float;
use super::ir_common::JIT_DEOPT_SENTINEL;
use super::ir_exceptions::JitReturn;
pub(crate) const JIT_YIELD_SENTINEL: i64 = -2;
pub(crate) extern "C" fn jit_alloc_tuple(process: *mut Process, arity: u64) -> *mut u64 {
let Some(process) = process_from_abi(process) else {
return std::ptr::null_mut();
};
let Ok(arity) = usize::try_from(arity) else {
return std::ptr::null_mut();
};
let Some(words) = arity.checked_add(1) else {
return std::ptr::null_mut();
};
alloc_words(process, words)
}
pub(crate) extern "C" fn jit_alloc_cons(process: *mut Process) -> *mut u64 {
let Some(process) = process_from_abi(process) else {
return std::ptr::null_mut();
};
alloc_words(process, 2)
}
pub(crate) extern "C" fn jit_box_float(process: *mut Process, value: f64) -> u64 {
let Some(process) = process_from_abi(process) else {
return 0;
};
let heap = alloc_words(process, 2);
if heap.is_null() {
return 0;
}
let heap = unsafe { std::slice::from_raw_parts_mut(heap, 2) };
write_float(heap, value).map_or(0, Term::raw)
}
pub(crate) extern "C" fn jit_charge_reduction(process: *mut Process) -> u64 {
let Some(process) = process_from_abi(process) else {
return 1;
};
process.decrement_reductions(1);
u64::from(process.reductions_exhausted())
}
pub(crate) extern "C" fn jit_call_interpreted(
process: *mut Process,
module: u64,
function: u64,
arity: u64,
args: *const u64,
) -> JitReturn {
let Some(process) = process_from_abi(process) else {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
};
let Some(context) = process.jit_runtime_context() else {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
};
if context.module.is_null() || context.registry.is_null() {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
}
let Ok(module_index) = u32::try_from(module) else {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
};
let Ok(import_index) = usize::try_from(function) else {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
};
let Ok(arity) = u8::try_from(arity) else {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
};
if args.is_null() && arity != 0 {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
}
let module_atom = Atom::new(module_index);
for register in 0..arity {
let raw = if arity == 0 {
0
} else {
unsafe { *args.add(usize::from(register)) }
};
process.set_x_reg(u16::from(register), Term::from_raw(raw));
}
let current_module = unsafe { &*context.module };
let registry = unsafe { &*context.registry };
if current_module.name != module_atom {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
}
let Some(resolved) = current_module.resolved_imports.get(import_index) else {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
};
if resolved.arity != arity {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
}
let (target_module_atom, target_function, target_arity) = match resolved.target {
ResolvedImportTarget::Code { .. } | ResolvedImportTarget::Deferred { .. } => {
(resolved.module, resolved.function, resolved.arity)
}
ResolvedImportTarget::Unresolved { .. }
| ResolvedImportTarget::Native(_)
| ResolvedImportTarget::Denied { .. } => {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
}
};
let Some(target_module) = registry.lookup(target_module_atom) else {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
};
let Ok(instruction_pointer) = target_module.export_ip(target_function, target_arity) else {
return JitReturn::deopt(JIT_DEOPT_SENTINEL as u64);
};
let saved_module = process.current_module().cloned();
let saved_position = process.code_position();
process.set_current_module(target_module);
process.set_code_position(Some(CodePosition {
module: target_module_atom,
instruction_pointer,
}));
process.decrement_reductions(1);
if process.reductions_exhausted() {
process.set_jit_status(Some(JitStatus::Yield));
return JitReturn::yield_(JIT_YIELD_SENTINEL as u64);
}
let result = run_with_registry(process, current_module, registry);
if let Some(module) = saved_module {
process.set_current_module(module);
}
process.set_code_position(saved_position);
match result {
Ok(ExecutionResult::Exited(ExitReason::Normal)) => {
JitReturn::normal(process.x_reg(0).raw())
}
Ok(ExecutionResult::Exited(_)) if process.current_exception().is_some() => {
let reason = process
.current_exception()
.map_or(Term::NIL.raw(), |exception| exception.reason.raw());
JitReturn::exception(reason)
}
Ok(ExecutionResult::Exited(_))
| Ok(ExecutionResult::Waiting)
| Ok(ExecutionResult::DirtyCall { .. }) => JitReturn::deopt(JIT_DEOPT_SENTINEL as u64),
Ok(ExecutionResult::Yielded) => {
process.set_jit_status(Some(JitStatus::Yield));
JitReturn::yield_(JIT_YIELD_SENTINEL as u64)
}
Err(_error) if process.current_exception().is_some() => {
let reason = process
.current_exception()
.map_or(Term::NIL.raw(), |exception| exception.reason.raw());
JitReturn::exception(reason)
}
Err(_error) => JitReturn::deopt(JIT_DEOPT_SENTINEL as u64),
}
}
pub(crate) fn process_from_abi(process: *mut Process) -> Option<&'static mut Process> {
if process.is_null() {
return None;
}
Some(unsafe { &mut *process })
}
pub(crate) fn alloc_words(process: &mut Process, words: usize) -> *mut u64 {
if words == 0 {
return std::ptr::null_mut();
}
if gc::ensure_space(process, words, 256).is_err() {
return std::ptr::null_mut();
}
match process.heap_mut().alloc(words) {
Ok(ptr) => ptr,
Err(_heap_full) => std::ptr::null_mut(),
}
}