mod ffi {
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals, dead_code, unused_imports)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
use std::ffi::CString;
use std::os::raw::{c_char, c_void};
use std::ptr;
pub struct Runtime {
inner: ffi::vtx_runtime_t,
}
pub struct Bytecode {
inner: *mut ffi::vtx_bytecode_t,
}
pub type Value = u64;
impl Runtime {
pub fn new() -> Result<Self, String> {
let mut inner: ffi::vtx_runtime_t = unsafe { std::mem::zeroed() };
let rc = unsafe { ffi::vtx_runtime_create(&mut inner) };
if rc != 0 {
return Err("failed to create runtime".to_string());
}
Ok(Runtime { inner })
}
pub fn enable_jit(&mut self, nthreads: u32) {
unsafe { ffi::vtx_runtime_enable_jit(&mut self.inner, nthreads); }
}
pub fn compile_t1(&mut self, method: &mut ffi::vtx_method_desc_t) -> Result<(), String> {
let rc = unsafe { ffi::vtx_runtime_compile(&mut self.inner, method, 1) };
if rc != 0 { Err("T1 compilation failed".to_string()) } else { Ok(()) }
}
pub fn compile_t2(&mut self, method: &mut ffi::vtx_method_desc_t) -> Result<(), String> {
let rc = unsafe { ffi::vtx_runtime_compile(&mut self.inner, method, 2) };
if rc != 0 { Err("T2 compilation failed".to_string()) } else { Ok(()) }
}
pub fn run(&mut self, bc: &Bytecode) -> Value {
unsafe { ffi::vtx_runtime_run(&mut self.inner, bc.inner) }
}
pub fn run_with_args(&mut self, bc: &Bytecode, args: &[Value]) -> Value {
unsafe {
ffi::vtx_runtime_run_with_args(
&mut self.inner, bc.inner,
args.as_ptr(), args.len() as u32,
)
}
}
pub fn as_ptr(&mut self) -> *mut ffi::vtx_runtime_t { &mut self.inner }
pub fn load_bytecode(path: &str) -> Result<Bytecode, String> {
let c_path = CString::new(path).map_err(|e| e.to_string())?;
let bc = unsafe { ffi::vtx_bytecode_load(c_path.as_ptr()) };
if bc.is_null() {
Err("failed to load bytecode".to_string())
} else {
Ok(Bytecode { inner: bc })
}
}
}
impl Drop for Runtime {
fn drop(&mut self) {
unsafe { ffi::vtx_runtime_destroy(&mut self.inner); }
}
}
impl Drop for Bytecode {
fn drop(&mut self) {
if !self.inner.is_null() {
unsafe {
let bc = &mut *self.inner;
if !bc.code.is_null() { libc::free(bc.code as *mut c_void); }
if !bc.constant_pool.is_null() { libc::free(bc.constant_pool as *mut c_void); }
libc::free(self.inner as *mut c_void);
}
}
}
}
pub fn make_smi(val: i64) -> Value {
const HEADER: u64 = 0x7FF8_0000_0000_0000;
const DATA_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
HEADER | (((val as u64) & DATA_MASK) << 3)
}
pub fn smi_value(val: Value) -> i64 {
((val << 13) as i64) >> 16
}
pub fn is_smi(val: Value) -> bool {
const HEADER: u64 = 0x7FF8_0000_0000_0000;
const TAG_MASK: u64 = 0x0007;
(val & HEADER) == HEADER && (val & TAG_MASK) == 0
}
pub fn make_double(val: f64) -> Value {
let bits = val.to_bits();
if ((bits >> 52) & 0x7FF) == 0x7FF && (bits & 0x000F_FFFF_FFFF_FFFF) != 0 {
return 0x7FF8_0000_0000_0002;
}
bits
}
pub fn double_value(val: Value) -> f64 {
const HEADER: u64 = 0x7FF8_0000_0000_0000;
if (val & HEADER) != HEADER {
return f64::from_bits(val);
}
f64::from_bits(0x7FF8_0000_0000_0000)
}
pub fn is_double(val: Value) -> bool {
const HEADER: u64 = 0x7FF8_0000_0000_0000;
const TAG_MASK: u64 = 0x0007;
const TAG_DOUBLE: u64 = 2;
if (val & HEADER) != HEADER {
return true; }
(val & TAG_MASK) == TAG_DOUBLE
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_smi() {
let v = make_smi(42);
assert!(is_smi(v));
assert_eq!(smi_value(v), 42);
}
#[test]
fn test_double() {
let v = make_double(3.14);
assert!(is_double(v));
assert!((double_value(v) - 3.14).abs() < 1e-10);
}
#[test]
fn test_runtime_create() {
let rt = Runtime::new();
assert!(rt.is_ok());
}
}