gnu-libjit 0.9.0

A safe rust wrapper around the libjit just in time compiler
Documentation
use std::ffi::CString;
use std::os::raw::c_uint;
use gnu_libjit_sys::{jit_function_compile, jit_insn_pow, jit_insn_acos, jit_insn_asin, jit_insn_atan, jit_insn_cos, jit_insn_cosh, jit_insn_log, jit_insn_log10, jit_insn_sin, jit_insn_sinh, jit_insn_sqrt, jit_insn_tan, jit_insn_tanh, jit_value_create_float64_constant, jit_insn_not, jit_insn_ge, jit_insn_le, jit_insn_gt, jit_insn_lt, jit_insn_ne, jit_insn_and, jit_insn_or, jit_insn_xor, jit_function_t, jit_value_create_long_constant, jit_value_create_float32_constant, jit_value_create_nint_constant, jit_value_create, jit_type_float32, jit_insn_eq, jit_type_nint, jit_type_int, jit_type_uint, jit_type_ushort, jit_type_short, jit_insn_add, jit_insn_div, jit_insn_sub, jit_insn_call_native, jit_insn_mul, jit_insn_return, jit_type_create_signature, jit_type_void, jit_value_get_param, jit_dump_function, jit_abi_t, jit_function_to_closure, jit_insn_branch_if, jit_label_t, jit_insn_label, jit_insn_branch_if_not, jit_type_long, jit_type_ulong, jit_type_sbyte, jit_type_float64, jit_type_ubyte, jit_type_void_ptr, jit_insn_alloca, jit_insn_load, jit_insn_store, jit_insn_branch, jit_insn_load_relative, jit_insn_call, jit_value_t, jit_type_t, jit_insn_rem, jit_insn_exp, jit_float32_rint, jit_insn_ceil, jit_insn_floor, jit_insn_rint, jit_insn_round, jit_insn_trunc, jit_insn_load_elem_address};
use libc::{c_char, c_void};
use crate::context::Exception;
use crate::{Abi, JitType};
use crate::label::Label;
use crate::util::dump;
use crate::value::Value;

macro_rules! op {
    ($fn_name:ident, $jit_op:ident) => {
        pub fn $fn_name(&mut self, left: &Value, right: &Value) -> Value {
            unsafe {
                let v = $jit_op(self.function, left.value, right.value);
                Value::new(v)
            }
        }
    }
}

macro_rules! ret_op {
    ($fn_name:ident, $jit_op:ident) => {
        pub fn $fn_name(&mut self, value:& Value) {
            unsafe {
                $jit_op(self.function, value.value);
            }
        }
    }
}

macro_rules! unary_op {
    ($fn_name:ident, $jit_op:ident) => {
        pub fn $fn_name(&mut self, value: &Value) -> Value {
            unsafe {
                let v =$jit_op(self.function, value.value);
                Value::new(v)
            }
        }
    }
}

#[derive(Clone)]
pub struct Function {
    params: Vec<JitType>,
    function: jit_function_t,
    signature: jit_type_t,
}

macro_rules! constant_fn {
    ($fn_name:ident, $const_ty:ty, $typ: expr, $creator:expr) => {
        pub fn $fn_name(&mut self, constant_value: $const_ty) -> Value {
            let val = unsafe {
                $creator(self.function, $typ, constant_value)
            };
            Value::new(val)

        }
    }
}

macro_rules! constant_fn_long {
    ($fn_name:ident, $const_ty:ty, $typ: expr, $creator:expr) => {
        pub fn $fn_name(&mut self, constant_value: $const_ty) -> Value {
            let val = unsafe {
                $creator(self.function, $typ, constant_value as ::std::os::raw::c_long)
            };
            Value::new(val)

        }
    }
}


impl Function {
    // Use Context::new().function  to create a new function. This method is private.
    pub(crate) fn new(function: jit_function_t, signature: jit_type_t, params: Vec<JitType>) -> Function {
        Function { function, params, signature }
    }

    pub fn compile(&self) {
        unsafe {
            if jit_function_compile(self.function) == 0 {
                panic!("Failed to compile function");
            }
        }
    }

    pub fn alloca(&self, size: ::std::os::raw::c_long) -> Value {
        unsafe {
            let bytes = jit_value_create_nint_constant(self.function, jit_type_ubyte, size);
            Value::new(jit_insn_alloca(self.function, bytes))
        }
    }

    pub fn dump(&self) -> Result<String, std::fmt::Error> {
        dump(|fd| unsafe {
            jit_dump_function(std::mem::transmute(fd), self.function, "no-name-func".as_ptr() as *const ::std::os::raw::c_char);
        })
    }

    // T must be a extern "C" fn() pointer to avoid disaster.
    // Also don't mess up the arg/return types lest you invite chaos.
    pub fn to_closure<T>(&self) -> T {
        unsafe {
            let void_ptr = jit_function_to_closure(self.function);
            std::mem::transmute_copy::<*mut c_void, T>(&void_ptr)
        }
    }

    // Call a native rust function
    pub fn insn_call_native(&self, native_func: *mut ::std::os::raw::c_void, params: Vec<Value>, return_type: Option<JitType>) -> Value {
        let c_str = CString::new("native-func").unwrap();
        let c_str_ptr = c_str.as_ptr();
        let mut sig_args = vec![];
        let mut args = vec![];
        for param in params.iter() {
            sig_args.push(param.value_type().inner);
            args.push(param.value);
        }
        unsafe {
            let signature = jit_type_create_signature(
                Abi::Cdecl as jit_abi_t,
                if let Some(jtype) = return_type { jtype.inner } else { jit_type_void },
                sig_args.as_mut_ptr(),
                params.len() as c_uint,
                1,
            );
            Value::new(jit_insn_call_native(self.function,
                                            c_str_ptr,
                                            native_func,
                                            signature,
                                            args.as_mut_ptr(),
                                            params.len() as c_uint,
                                            0,
            ))
        }
    }

    // Get the value of the idx'th arg to the function
    pub fn arg(&self, idx: i32) -> Result<Value, Exception> {
        let _arg_type = match self.params.get(idx as usize) {
            Some(arg_type) => arg_type,
            None => {
                return Err(Exception::ArgIndexTooLarge(format!("Function has {} args but you requested index {}", self.params.len(), idx)));
            }
        };
        let value = unsafe {
            jit_value_get_param(self.function, idx as c_uint)
        };
        Ok(Value::new(value))
    }

    op!(insn_mult, jit_insn_mul);
    op!(insn_add, jit_insn_add);
    op!(insn_pow, jit_insn_pow);
    op!(insn_rem, jit_insn_rem);
    op!(insn_div, jit_insn_div);
    op!(insn_sub, jit_insn_sub);
    op!(insn_eq, jit_insn_eq);
    op!(insn_and, jit_insn_and);
    op!(insn_or, jit_insn_or);
    op!(insn_xor, jit_insn_xor);
    op!(insn_le, jit_insn_le);
    op!(insn_lt, jit_insn_lt);
    op!(insn_ge, jit_insn_ge);
    op!(insn_gt, jit_insn_gt);
    op!(insn_ne, jit_insn_ne);

    unary_op!(insn_acos, jit_insn_acos);
    unary_op!(insn_asin, jit_insn_asin);
    unary_op!(insn_atan, jit_insn_atan);
    unary_op!(insn_cos, jit_insn_cos);
    unary_op!(insn_cosh, jit_insn_cosh);
    unary_op!(insn_exp, jit_insn_exp);
    unary_op!(insn_log, jit_insn_log);
    unary_op!(insn_log10, jit_insn_log10);
    unary_op!(insn_sin, jit_insn_sin);
    unary_op!(insn_sinh, jit_insn_sinh);
    unary_op!(insn_sqrt, jit_insn_sqrt);
    unary_op!(insn_tan, jit_insn_tan);
    unary_op!(insn_tanh, jit_insn_tanh);

    ret_op!(insn_return, jit_insn_return);

    unary_op!(insn_not, jit_insn_not);

    pub fn insn_branch(&self, label: &mut Label) {
        unsafe { jit_insn_branch(self.function, &mut label.inner as *mut jit_label_t); }
    }

    pub fn insn_branch_if(&self, value: &Value, label: &mut Label) {
        unsafe { jit_insn_branch_if(self.function, value.value, &mut label.inner as *mut jit_label_t); }
    }

    pub fn insn_branch_if_not(&self, value: &Value, label: &mut Label) {
        unsafe { jit_insn_branch_if_not(self.function, value.value, &mut label.inner as *mut jit_label_t); }
    }

    pub fn insn_load(&mut self, ptr: &Value) -> Value {
        unsafe {
            let value = jit_insn_load(self.function, ptr.value);
            // let value_type = jit_value_get_type(value);
            Value::new(value)
        }
    }

    pub fn insn_store(&mut self, ptr: &Value, value: &Value) {
        unsafe {
            jit_insn_store(self.function, ptr.value, value.value);
        }
    }

    pub fn insn_call(&mut self, function: &Function, args: Vec<Value>) -> Value {
        let mut values: Vec<jit_value_t> = args.iter().map(|arg| arg.value.clone()).collect();
        let val = unsafe {
            jit_insn_call(self.function, "-no-name-".as_ptr() as *mut c_char, function.function, function.signature, values.as_mut_ptr(), args.len() as c_uint, 0)
        };
        Value::new(val)
    }

    pub fn insn_load_relative(&mut self, base_ptr: &Value, offset_bytes: ::std::os::raw::c_long, typ: &JitType) -> Value {
        let res = unsafe {
            jit_insn_load_relative(self.function, base_ptr.value, offset_bytes, typ.inner)
        };
        Value::new(res)
    }

    pub fn create_value_int(&mut self) -> Value {
        Value::new(unsafe {
            jit_value_create(self.function, jit_type_int)
        })
    }

    pub fn create_value_void_ptr(&mut self) -> Value {
        Value::new(unsafe {
            jit_value_create(self.function, jit_type_void_ptr)
        })
    }
    pub fn create_value_float32(&mut self) -> Value {
        Value::new(unsafe {
            jit_value_create(self.function, jit_type_float32)
        })
    }
    pub fn create_value_float64(&mut self) -> Value {
        Value::new(unsafe {
            jit_value_create(self.function, jit_type_float64)
        })
    }

    pub fn insn_label(&self, label: &mut Label) {
        let lbl_ptr = (&mut label.inner) as *mut jit_label_t;
        unsafe { jit_insn_label(self.function, lbl_ptr); }
    }

    // Round value up towards positive infinity.
    pub fn insn_ceil(&self, value: &Value) -> Value {
        Value::new(unsafe { jit_insn_ceil(self.function, value.value) })
    }

    // Round value down towards negative infinity.
    pub fn insn_floor(&self, value: &Value) -> Value {
        Value::new(unsafe { jit_insn_floor(self.function, value.value) })
    }

    //  Round value to the nearest integer. Half-way cases are rounded to the even number.
    pub fn insn_rint(&self, value: &Value) -> Value {
        Value::new(unsafe { jit_insn_rint(self.function, value.value) })
    }

    // Round value to the nearest integer. Half-way cases are rounded away from zero.
    pub fn insn_round(&self, value: &Value) -> Value {
        Value::new(unsafe { jit_insn_round(self.function, value.value) })
    }

    // Round value towards zero.
    pub fn insn_trunc(&self, value: &Value) -> Value {
        Value::new(unsafe { jit_insn_trunc(self.function, value.value) })
    }

    // Load the effective address of an element of type elem_type at position index within the
    // array starting at base_addr. Essentially, this computes the expression base_addr + index *
    // sizeof(elem_type), but may be more efficient than performing the steps with jit_insn_mul and jit_insn_add.
    pub fn insn_load_elem_address(&self, base_addr: &Value, index: &Value, elem_type: &JitType) -> Value {
        Value::new(unsafe { jit_insn_load_elem_address(self.function, base_addr.value, index.value, elem_type.inner) })
    }

    constant_fn!(create_float32_constant, ::std::os::raw::c_float, jit_type_float32, jit_value_create_float32_constant);
    constant_fn!(create_float64_constant, ::std::os::raw::c_double, jit_type_float64, jit_value_create_float64_constant);
    constant_fn!(create_long_constant, ::std::os::raw::c_long, jit_type_long, jit_value_create_long_constant);

    constant_fn_long!(create_sbyte_constant, ::std::os::raw::c_char, jit_type_sbyte, jit_value_create_long_constant);
    constant_fn_long!(create_ubyte_constant, ::std::os::raw::c_uchar, jit_type_ubyte, jit_value_create_long_constant);
    constant_fn_long!(create_short_constant, ::std::os::raw::c_long, jit_type_short, jit_value_create_long_constant);
    constant_fn_long!(create_ushort_constant, ::std::os::raw::c_ulong, jit_type_ushort, jit_value_create_long_constant);
    constant_fn_long!(create_int_constant, ::std::os::raw::c_int, jit_type_int, jit_value_create_long_constant);
    constant_fn_long!(create_ulong_constant, ::std::os::raw::c_ulong, jit_type_ulong, jit_value_create_long_constant);
    constant_fn_long!(create_uint_constant, ::std::os::raw::c_uint, jit_type_uint, jit_value_create_long_constant);
    constant_fn_long!(create_nint_constant, ::std::os::raw::c_int, jit_type_nint, jit_value_create_long_constant);
    constant_fn_long!(create_nuint_constant, ::std::os::raw::c_int, jit_type_nint, jit_value_create_long_constant);
    constant_fn_long!(create_void_ptr_constant, *mut ::std::os::raw::c_void, jit_type_void_ptr, jit_value_create_long_constant);
}