blob: f240e793e3d0b826a4841190f2ed8868f3bfa696 [file] [log] [blame] [edit]
use std::ffi::{c_void, CStr, CString};
use std::marker::PhantomData;
use std::mem;
use std::os::raw::{c_int, c_ulong};
use std::ptr;
use std::str::Utf8Error;
use gccjit_sys::gcc_jit_int_option::*;
use gccjit_sys::gcc_jit_str_option::*;
use gccjit_sys::gcc_jit_bool_option::*;
use block::{self, BinaryOp, Block, UnaryOp, ComparisonOp};
use field::{self, Field};
use function::{self, Function, FunctionType};
use location::{self, Location};
use lvalue::{self, LValue};
use object::{self, Object, ToObject};
use parameter::{self, Parameter};
use rvalue::{self, RValue, ToRValue};
use structs::{self, Struct};
#[cfg(feature="master")]
use target_info::{self, TargetInfo};
use Type;
use types;
#[repr(C)]
#[derive(Debug)]
pub enum GlobalKind {
Exported,
Internal,
Imported,
}
/// Represents an optimization level that the JIT compiler
/// will use when compiling your code.
#[repr(C)]
#[derive(Debug)]
pub enum OptimizationLevel {
/// No optimizations are applied.
None,
/// Optimizies for both speed and code size, but doesn't apply
/// any optimizations that take extended periods of time.
Limited,
/// Performs all optimizations that do not involve a tradeoff
/// of code size for speed.
Standard,
/// Performs all optimizations at the Standard level, as well
/// as function inlining, loop vectorization, some loop unrolling,
/// and various other optimizations.
Aggressive
}
/// This enum indicates to gccjit the format of the output
/// code that is written out by compile_to_file.
#[repr(C)]
pub enum OutputKind {
/// Outputs an assembly file (.S)
Assembler,
/// Outputs an object file (.o)
ObjectFile,
/// Outputs a dynamic library (.so)
DynamicLibrary,
/// Outputs an executable
Executable
}
/// Represents a successful compilation of a context. This type
/// provides the means to access compiled functions and globals.
/// JIT compiled functions are exposted to Rust as an extern "C" function
/// pointer.
pub struct CompileResult {
ptr: *mut gccjit_sys::gcc_jit_result
}
impl CompileResult {
/// Gets a function pointer to a JIT compiled function. If the function
/// does not exist (wasn't compiled by the Context that produced this
/// CompileResult), this function returns a null pointer.
///
/// It is the caller's responsibility to ensure that this pointer is not used
/// past the lifetime of the CompileResult object. Second, it is
/// the caller's responsibility to check whether or not the pointer
/// is null. It is also expected that the caller of this function
/// will transmute this pointer to a function pointer type.
pub fn get_function<S: AsRef<str>>(&self, name: S) -> *mut () {
let c_str = CString::new(name.as_ref()).unwrap();
unsafe {
let func = gccjit_sys::gcc_jit_result_get_code(self.ptr,
c_str.as_ptr());
mem::transmute(func)
}
}
/// Gets a pointer to a global variable that lives on the JIT heap.
///
/// It is the caller's responsibility
/// to ensure that the pointer is not used past the lifetime of the
/// CompileResult object. It is also the caller's responsibility to
/// check whether or not the returned pointer is null.
pub fn get_global<S: AsRef<str>>(&self, name: S) -> *mut () {
let c_str = CString::new(name.as_ref()).unwrap();
unsafe {
let ptr = gccjit_sys::gcc_jit_result_get_global(self.ptr, c_str.as_ptr());
mem::transmute(ptr)
}
}
}
impl Drop for CompileResult {
fn drop(&mut self) {
unsafe {
gccjit_sys::gcc_jit_result_release(self.ptr);
}
}
}
pub struct Case<'ctx> {
marker: PhantomData<&'ctx Case<'ctx>>,
ptr: *mut gccjit_sys::gcc_jit_case,
}
impl<'ctx> ToObject<'ctx> for Case<'ctx> {
fn to_object(&self) -> Object<'ctx> {
unsafe {
let ptr = gccjit_sys::gcc_jit_case_as_object(self.ptr);
object::from_ptr(ptr)
}
}
}
/// Wrapper around a GCC JIT context object that keeps
/// the state of the JIT compiler. In GCCJIT, this object
/// is responsible for all memory management of JIT data
/// structures, and as such anything made from this context
/// must have a lifetime strictly less than this object.
///
/// It's possible to create a child context from a parent context.
/// In that case, the child context must have a lifetime strictly
/// less than the parent context.
#[derive(Debug)]
pub struct Context<'ctx> {
marker: PhantomData<&'ctx Context<'ctx>>,
ptr: *mut gccjit_sys::gcc_jit_context
}
impl Default for Context<'static> {
fn default() -> Context<'static> {
unsafe {
Context {
marker: PhantomData,
ptr: gccjit_sys::gcc_jit_context_acquire(),
}
}
}
}
impl<'ctx> Context<'ctx> {
/// Sets the program name reported by the JIT.
pub fn set_program_name<S: AsRef<str>>(&self, name: S) {
let name_ref = name.as_ref();
let c_str = CString::new(name_ref).unwrap();
unsafe {
gccjit_sys::gcc_jit_context_set_str_option(self.ptr,
GCC_JIT_STR_OPTION_PROGNAME,
c_str.as_ptr());
}
}
pub fn add_command_line_option<S: AsRef<str>>(&self, name: S) {
let c_str = CString::new(name.as_ref()).unwrap();
unsafe {
gccjit_sys::gcc_jit_context_add_command_line_option(self.ptr, c_str.as_ptr())
}
}
pub fn add_driver_option<S: AsRef<str>>(&self, name: S) {
let c_str = CString::new(name.as_ref()).unwrap();
unsafe {
gccjit_sys::gcc_jit_context_add_driver_option(self.ptr, c_str.as_ptr())
}
}
/// Sets the optimization level that the JIT compiler will use.
/// The higher the optimization level, the longer compilation will
/// take.
pub fn set_optimization_level(&self, level: OptimizationLevel) {
unsafe {
gccjit_sys::gcc_jit_context_set_int_option(self.ptr,
GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
level as i32);
}
}
#[cfg(feature="master")]
pub fn set_output_ident(&self, ident: &str) {
let c_str = CString::new(ident).unwrap();
unsafe {
gccjit_sys::gcc_jit_context_set_output_ident(self.ptr, c_str.as_ptr());
}
}
#[cfg(feature="master")]
pub fn set_special_chars_allowed_in_func_names(&self, value: &str) {
let c_str = CString::new(value).unwrap();
unsafe {
gccjit_sys::gcc_jit_context_set_str_option(self.ptr, GCC_JIT_STR_OPTION_SPECIAL_CHARS_IN_FUNC_NAMES, c_str.as_ptr());
}
}
pub fn set_debug_info(&self, value: bool) {
unsafe {
gccjit_sys::gcc_jit_context_set_bool_option(self.ptr,
GCC_JIT_BOOL_OPTION_DEBUGINFO,
value as i32);
}
}
pub fn set_keep_intermediates(&self, value: bool) {
unsafe {
gccjit_sys::gcc_jit_context_set_bool_option(self.ptr,
GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES,
value as i32);
}
}
pub fn set_print_errors_to_stderr(&self, enabled: bool) {
unsafe {
gccjit_sys::gcc_jit_context_set_bool_print_errors_to_stderr(self.ptr, enabled as c_int);
}
}
pub fn set_dump_everything(&self, value: bool) {
unsafe {
gccjit_sys::gcc_jit_context_set_bool_option(self.ptr,
GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING,
value as i32);
}
}
pub fn set_dump_initial_gimple(&self, value: bool) {
unsafe {
gccjit_sys::gcc_jit_context_set_bool_option(self.ptr,
GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
value as i32);
}
}
/// When set to true, dumps the code that the JIT generates to standard
/// out during compilation.
pub fn set_dump_code_on_compile(&self, value: bool) {
unsafe {
gccjit_sys::gcc_jit_context_set_bool_option(self.ptr,
GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
value as i32);
}
}
pub fn set_allow_unreachable_blocks(&self, value: bool) {
unsafe {
gccjit_sys::gcc_jit_context_set_bool_allow_unreachable_blocks(self.ptr, value as i32);
}
}
/// Compiles the context and returns a CompileResult that contains
/// the means to access functions and globals that have currently
/// been JIT compiled.
pub fn compile(&self) -> CompileResult {
unsafe {
CompileResult {
ptr: gccjit_sys::gcc_jit_context_compile(self.ptr)
}
}
}
/// Compiles the context and saves the result to a file. The
/// type of the file is controlled by the OutputKind parameter.
pub fn compile_to_file<S: AsRef<str>>(&self, kind: OutputKind, file: S) {
unsafe {
let file_ref = file.as_ref();
let cstr = CString::new(file_ref).unwrap();
gccjit_sys::gcc_jit_context_compile_to_file(self.ptr,
mem::transmute::<OutputKind, gccjit_sys::gcc_jit_output_kind>(kind),
cstr.as_ptr());
}
}
#[cfg(feature="master")]
pub fn get_target_info(&self) -> TargetInfo {
unsafe {
target_info::from_ptr(gccjit_sys::gcc_jit_context_get_target_info(self.ptr))
}
}
/// Creates a new child context from this context. The child context
/// is a fully-featured context, but it has a lifetime that is strictly
/// less than the lifetime that spawned it.
pub fn new_child_context<'b>(&'b self) -> Context<'b> {
unsafe {
Context {
marker: PhantomData,
ptr: gccjit_sys::gcc_jit_context_new_child_context(self.ptr)
}
}
}
pub fn new_case<S: ToRValue<'ctx>, T: ToRValue<'ctx>>(&self, min_value: S, max_value: T, dest_block: Block<'ctx>) -> Case<'ctx> {
let min_value = min_value.to_rvalue();
let max_value = max_value.to_rvalue();
let result = unsafe {
Case {
marker: PhantomData,
ptr: gccjit_sys::gcc_jit_context_new_case(self.ptr, rvalue::get_ptr(&min_value), rvalue::get_ptr(&max_value),
block::get_ptr(&dest_block)),
}
};
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
result
}
/// Creates a new location for use by gdb when debugging a JIT compiled
/// program. The filename, line, and col are used by gdb to "show" your
/// source when in a debugger.
pub fn new_location<'a, S: AsRef<str>>(&'a self,
filename: S,
line: i32,
col: i32) -> Location<'a> {
unsafe {
let filename_ref = filename.as_ref();
let cstr = CString::new(filename_ref).unwrap();
let ptr = gccjit_sys::gcc_jit_context_new_location(self.ptr,
cstr.as_ptr(),
line,
col);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
location::from_ptr(ptr)
}
}
pub fn new_global<'a, S: AsRef<str>>(&self, loc: Option<Location<'a>>, kind: GlobalKind, ty: Type<'a>, name: S) -> LValue<'a> {
unsafe {
let loc_ptr = match loc {
Some(loc) => location::get_ptr(&loc),
None => ptr::null_mut()
};
let cstr = CString::new(name.as_ref()).unwrap();
let ptr = gccjit_sys::gcc_jit_context_new_global(
self.ptr,
loc_ptr,
mem::transmute::<GlobalKind, gccjit_sys::gcc_jit_global_kind>(kind),
types::get_ptr(&ty),
cstr.as_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
lvalue::from_ptr(ptr)
}
}
/// Constructs a new type for any type that implements the Typeable trait.
/// This library only provides a handful of implementations of Typeable
/// for some primitive types - utilizers of this library are encouraged
/// to provide their own types that implement Typeable for ease of type
/// creation.
pub fn new_type<'a, T: types::Typeable>(&'a self) -> types::Type<'a> {
<T as types::Typeable>::get_type(self)
}
pub fn new_c_type<'a>(&'a self, c_type: CType) -> types::Type<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_get_type(get_ptr(self), c_type.to_sys());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
types::from_ptr(ptr)
}
}
pub fn new_int_type<'a>(&'a self, num_bytes: i32, signed: bool) -> types::Type<'a> {
unsafe {
let ctx_ptr = get_ptr(self);
let ptr = gccjit_sys::gcc_jit_context_get_int_type(ctx_ptr, num_bytes, signed as i32);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
types::from_ptr(ptr)
}
}
/// Constructs a new field with an optional source location, type, and name.
/// This field can be used to compose unions or structs.
pub fn new_field<'a, S: AsRef<str>>(&'a self,
loc: Option<Location<'a>>,
ty: types::Type<'a>,
name: S) -> Field<'a> {
let name_ref = name.as_ref();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let cstr = CString::new(name_ref).unwrap();
let ptr = gccjit_sys::gcc_jit_context_new_field(self.ptr,
loc_ptr,
types::get_ptr(&ty),
cstr.as_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
field::from_ptr(ptr)
}
}
/// Constructs a new array type with a given base element type and a
/// size.
pub fn new_array_type<'a>(&'a self,
loc: Option<Location<'a>>,
ty: types::Type<'a>,
num_elements: u64) -> types::Type<'a> {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_array_type(self.ptr,
loc_ptr,
types::get_ptr(&ty),
num_elements as c_ulong);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
types::from_ptr(ptr)
}
}
pub fn new_vector_type<'a>(&'a self, ty: types::Type<'a>, num_units: u64) -> types::Type<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_type_get_vector(types::get_ptr(&ty), num_units as _);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
types::from_ptr(ptr)
}
}
/// Constructs a new struct type with the given name, optional source location,
/// and a list of fields. The returned struct is concrete and new fields cannot
/// be added to it.
pub fn new_struct_type<'a, S: AsRef<str>>(&'a self,
loc: Option<Location<'a>>,
name: S,
fields: &[Field<'a>]) -> Struct<'a> {
let name_ref = name.as_ref();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
let num_fields = fields.len() as i32;
let mut fields_ptrs : Vec<_> = fields.iter()
.map(|x| unsafe { field::get_ptr(x) })
.collect();
unsafe {
let cname = CString::new(name_ref).unwrap();
let ptr = gccjit_sys::gcc_jit_context_new_struct_type(self.ptr,
loc_ptr,
cname.as_ptr(),
num_fields,
fields_ptrs.as_mut_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
structs::from_ptr(ptr)
}
}
/// Constructs a new struct type whose fields are not known. Fields can
/// be added to this struct later, but only once.
pub fn new_opaque_struct_type<'a, S: AsRef<str>>(&'a self,
loc: Option<Location<'a>>,
name: S) -> Struct<'a> {
let name_ref = name.as_ref();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let cstr = CString::new(name_ref).unwrap();
let ptr = gccjit_sys::gcc_jit_context_new_opaque_struct(self.ptr,
loc_ptr,
cstr.as_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
structs::from_ptr(ptr)
}
}
/// Creates a new union type from a set of fields.
pub fn new_union_type<'a, S: AsRef<str>>(&'a self,
loc: Option<Location<'a>>,
name: S,
fields: &[Field<'a>]) -> types::Type<'a> {
let name_ref = name.as_ref();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
let num_fields = fields.len() as i32;
let mut fields_ptrs : Vec<_> = fields.iter()
.map(|x| unsafe { field::get_ptr(x) })
.collect();
unsafe {
let cname = CString::new(name_ref).unwrap();
let ptr = gccjit_sys::gcc_jit_context_new_union_type(self.ptr,
loc_ptr,
cname.as_ptr(),
num_fields,
fields_ptrs.as_mut_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
types::from_ptr(ptr)
}
}
/// Creates a new function pointer type with the given return type
/// parameter types, and an optional location. The last flag can
/// make the function variadic, although Rust can't really handle
/// the varargs calling convention.
pub fn new_function_pointer_type<'a>(&'a self,
loc: Option<Location<'a>>,
return_type: types::Type<'a>,
param_types: &[types::Type<'a>],
is_variadic: bool) -> types::Type<'a> {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
let num_types = param_types.len() as i32;
let mut types_ptrs : Vec<_> = param_types.iter()
.map(|x| unsafe { types::get_ptr(x) })
.collect();
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_function_ptr_type(self.ptr,
loc_ptr,
types::get_ptr(&return_type),
num_types,
types_ptrs.as_mut_ptr(),
is_variadic as i32);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
types::from_ptr(ptr)
}
}
/// Creates a new function with the given function kind, return type, parameters, name,
/// and whether or not the function is variadic.
pub fn new_function<'a, S: AsRef<str>>(&'a self,
loc: Option<Location<'a>>,
kind: FunctionType,
return_ty: types::Type<'a>,
params: &[Parameter<'a>],
name: S,
is_variadic: bool) -> Function<'a> {
let name_ref = name.as_ref();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
let num_params = params.len() as i32;
let mut params_ptrs : Vec<_> = params.iter()
.map(|x| unsafe { parameter::get_ptr(x) })
.collect();
unsafe {
let cstr = CString::new(name_ref).unwrap();
let ptr = gccjit_sys::gcc_jit_context_new_function(self.ptr,
loc_ptr,
mem::transmute::<FunctionType, gccjit_sys::gcc_jit_function_kind>(kind),
types::get_ptr(&return_ty),
cstr.as_ptr(),
num_params,
params_ptrs.as_mut_ptr(),
is_variadic as i32);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
function::from_ptr(ptr)
}
}
/// Creates a new binary operation between two RValues and produces a new RValue.
pub fn new_binary_op<'a, L: ToRValue<'a>, R: ToRValue<'a>>(&'a self,
loc: Option<Location<'a>>,
op: BinaryOp,
ty: types::Type<'a>,
left: L,
right: R) -> RValue<'a> {
let left_rvalue = left.to_rvalue();
let right_rvalue = right.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_binary_op(self.ptr,
loc_ptr,
mem::transmute::<BinaryOp, gccjit_sys::gcc_jit_binary_op>(op),
types::get_ptr(&ty),
rvalue::get_ptr(&left_rvalue),
rvalue::get_ptr(&right_rvalue));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates a unary operation on one RValue and produces a result RValue.
pub fn new_unary_op<'a, T: ToRValue<'a>>(&'a self,
loc: Option<Location<'a>>,
op: UnaryOp,
ty: types::Type<'a>,
target: T) -> RValue<'a> {
let rvalue = target.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_unary_op(self.ptr,
loc_ptr,
mem::transmute::<UnaryOp, gccjit_sys::gcc_jit_unary_op>(op),
types::get_ptr(&ty),
rvalue::get_ptr(&rvalue));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
pub fn new_comparison<'a, L: ToRValue<'a>, R: ToRValue<'a>>(&'a self,
loc: Option<Location<'a>>,
op: ComparisonOp,
left: L,
right: R) -> RValue<'a> {
let left_rvalue = left.to_rvalue();
let right_rvalue = right.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_comparison(self.ptr,
loc_ptr,
mem::transmute::<ComparisonOp, gccjit_sys::gcc_jit_comparison>(op),
rvalue::get_ptr(&left_rvalue),
rvalue::get_ptr(&right_rvalue));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates a function call to a function object with a given number of parameters.
/// The RValue that is returned is the result of the function call.
/// Note that due to the way that Rust's generics work, it is currently
/// not possible to be generic over different types of arguments (RValues
/// together with LValues and Parameters, for example), so in order to
/// mix the types of the arguments it may be necessary to call to_rvalue()
/// before calling this function.
#[must_use]
pub fn new_call<'a>(&'a self,
loc: Option<Location<'a>>,
func: Function<'a>,
args: &[RValue<'a>]) -> RValue<'a> {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
let num_params = args.len() as i32;
let mut params_ptrs : Vec<_> = args.iter()
.map(|x| unsafe { rvalue::get_ptr(x) })
.collect();
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_call(self.ptr,
loc_ptr,
function::get_ptr(&func),
num_params,
params_ptrs.as_mut_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates an indirect function call that dereferences a function pointer and
/// attempts to invoke it with the given arguments. The RValue that is returned
/// is the result of the function call.
pub fn new_call_through_ptr<'a, F: ToRValue<'a>>(&'a self,
loc: Option<Location<'a>>,
fun_ptr: F,
args: &[RValue<'a>]) -> RValue<'a> {
let fun_ptr_rvalue = fun_ptr.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
let num_params = args.len() as i32;
let mut params_ptrs : Vec<_> = args.iter()
.map(|x| x.to_rvalue())
.map(|x| unsafe { rvalue::get_ptr(&x) })
.collect();
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_call_through_ptr(self.ptr,
loc_ptr,
rvalue::get_ptr(&fun_ptr_rvalue),
num_params,
params_ptrs.as_mut_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Cast an RValue to a specific type. I don't know what happens when the cast fails yet.
pub fn new_cast<'a, T: ToRValue<'a>>(&'a self,
loc: Option<Location<'a>>,
value: T,
dest_type: types::Type<'a>) -> RValue<'a> {
let rvalue = value.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_cast(self.ptr,
loc_ptr,
rvalue::get_ptr(&rvalue),
types::get_ptr(&dest_type));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Bitcast an RValue to a specific type.
pub fn new_bitcast<'a, T: ToRValue<'a>>(&'a self,
loc: Option<Location<'a>>,
value: T,
dest_type: types::Type<'a>) -> RValue<'a> {
let rvalue = value.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_bitcast(self.ptr,
loc_ptr,
rvalue::get_ptr(&rvalue),
types::get_ptr(&dest_type));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates an LValue from an array pointer and an offset. The LValue can be the target
/// of an assignment, or it can be converted into an RValue (i.e. loaded).
pub fn new_array_access<'a, A: ToRValue<'a>, I: ToRValue<'a>>(&'a self,
loc: Option<Location<'a>>,
array_ptr: A,
index: I) -> LValue<'a> {
let array_rvalue = array_ptr.to_rvalue();
let idx_rvalue = index.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_array_access(self.ptr,
loc_ptr,
rvalue::get_ptr(&array_rvalue),
rvalue::get_ptr(&idx_rvalue));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
lvalue::from_ptr(ptr)
}
}
/// Creates a new RValue from a given long value.
pub fn new_rvalue_from_long<'a>(&'a self,
ty: types::Type<'a>,
value: i64) -> RValue<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_rvalue_from_long(self.ptr,
types::get_ptr(&ty),
value as _);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
pub fn new_rvalue_from_vector<'a>(&'a self, loc: Option<Location<'a>>, vec_type: types::Type<'a>, elements: &[RValue<'a>]) -> RValue<'a> {
unsafe {
let loc_ptr = match loc {
Some(loc) => location::get_ptr(&loc),
None => ptr::null_mut()
};
let ptr = gccjit_sys::gcc_jit_context_new_rvalue_from_vector(self.ptr, loc_ptr, types::get_ptr(&vec_type), elements.len() as _, elements.as_ptr() as *mut *mut _);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
#[cfg(feature="master")]
pub fn new_rvalue_vector_perm<'a>(&'a self, loc: Option<Location<'a>>, elements1: RValue<'a>, elements2: RValue<'a>, mask: RValue<'a>) -> RValue<'a> {
unsafe {
let loc_ptr = match loc {
Some(loc) => location::get_ptr(&loc),
None => ptr::null_mut()
};
let ptr = gccjit_sys::gcc_jit_context_new_rvalue_vector_perm(self.ptr, loc_ptr, rvalue::get_ptr(&elements1), rvalue::get_ptr(&elements2), rvalue::get_ptr(&mask));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
//pub fn gcc_jit_context_new_union_constructor(ctxt: *mut gcc_jit_context, loc: *mut gcc_jit_location, typ: *mut gcc_jit_type, field: *mut gcc_jit_field, value: *mut gcc_jit_rvalue) -> *mut gcc_jit_rvalue;
pub fn new_struct_constructor<'a>(&'a self, loc: Option<Location<'a>>, struct_type: types::Type<'a>, fields: Option<&[Field<'a>]>, values: &[RValue<'a>]) -> RValue<'a> {
unsafe {
let loc_ptr = match loc {
Some(loc) => location::get_ptr(&loc),
None => ptr::null_mut()
};
let fields_ptr =
match fields {
Some(fields) => fields.as_ptr(),
None => ptr::null_mut(),
};
let ptr = gccjit_sys::gcc_jit_context_new_struct_constructor(self.ptr, loc_ptr, types::get_ptr(&struct_type), values.len() as _, fields_ptr as *mut *mut _, values.as_ptr() as *mut *mut _);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
pub fn new_array_constructor<'a>(&'a self, loc: Option<Location<'a>>, array_type: types::Type<'a>, elements: &[RValue<'a>]) -> RValue<'a> {
unsafe {
let loc_ptr = match loc {
Some(loc) => location::get_ptr(&loc),
None => ptr::null_mut()
};
let ptr = gccjit_sys::gcc_jit_context_new_array_constructor(self.ptr, loc_ptr, types::get_ptr(&array_type), elements.len() as _, elements.as_ptr() as *mut *mut _);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
#[cfg(feature="master")]
pub fn new_vector_access<'a>(&'a self, loc: Option<Location<'a>>, vector: RValue<'a>, index: RValue<'a>) -> LValue<'a> {
unsafe {
let loc_ptr = match loc {
Some(loc) => location::get_ptr(&loc),
None => ptr::null_mut()
};
let ptr = gccjit_sys::gcc_jit_context_new_vector_access(self.ptr, loc_ptr, rvalue::get_ptr(&vector), rvalue::get_ptr(&index));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
lvalue::from_ptr(ptr)
}
}
#[cfg(feature="master")]
pub fn convert_vector<'a>(&'a self, loc: Option<Location<'a>>, vector: RValue<'a>, type_: Type<'a>) -> RValue<'a> {
unsafe {
let loc_ptr = match loc {
Some(loc) => location::get_ptr(&loc),
None => ptr::null_mut()
};
let ptr = gccjit_sys::gcc_jit_context_convert_vector(self.ptr, loc_ptr, rvalue::get_ptr(&vector), types::get_ptr(&type_));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates a new RValue from a given int value.
pub fn new_rvalue_from_int<'a>(&'a self,
ty: types::Type<'a>,
value: i32) -> RValue<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_rvalue_from_int(self.ptr,
types::get_ptr(&ty),
value);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates a new RValue from a given double value.
pub fn new_rvalue_from_double<'a>(&'a self,
ty: types::Type<'a>,
value: f64) -> RValue<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_rvalue_from_double(self.ptr,
types::get_ptr(&ty),
value);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates a zero element for a given type.
pub fn new_rvalue_zero<'a>(&'a self,
ty: types::Type<'a>) -> RValue<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_zero(self.ptr,
types::get_ptr(&ty));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates a one element for a given type.
pub fn new_rvalue_one<'a>(&'a self,
ty: types::Type<'a>) -> RValue<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_one(self.ptr,
types::get_ptr(&ty));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates an RValue for a raw pointer. This function
/// requires that the lifetime of the pointer be greater
/// than that of the jitted program.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn new_rvalue_from_ptr<'a>(&'a self,
ty: types::Type<'a>,
value: *mut ()) -> RValue<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_rvalue_from_ptr(self.ptr,
types::get_ptr(&ty),
mem::transmute::<*mut (), *mut c_void>(value));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates a null RValue.
pub fn new_null<'a>(&'a self,
ty: types::Type<'a>) -> RValue<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_null(self.ptr,
types::get_ptr(&ty));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Creates a string literal RValue.
pub fn new_string_literal<'a, S: AsRef<str>>(&'a self,
value: S) -> RValue<'a> {
unsafe {
let cstr = CString::new(value.as_ref()).unwrap();
let ptr = gccjit_sys::gcc_jit_context_new_string_literal(self.ptr,
cstr.as_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
#[cfg(feature="master")]
/// Creates a new RValue from a sizeof(type).
pub fn new_sizeof<'a>(&'a self, ty: types::Type<'a>) -> RValue<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_sizeof(self.ptr, types::get_ptr(&ty));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
#[cfg(feature="master")]
/// Creates a new RValue from a _Alignof(type).
pub fn new_alignof<'a>(&'a self, ty: types::Type<'a>) -> RValue<'a> {
unsafe {
let ptr = gccjit_sys::gcc_jit_context_new_alignof(self.ptr, types::get_ptr(&ty));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
rvalue::from_ptr(ptr)
}
}
/// Dumps a small C file to the path that can be used to reproduce a series
/// of API calls. You should only ever need to call this if you are debugging
/// an issue in gccjit itself or this library.
pub fn dump_reproducer_to_file<S: AsRef<str>>(&self,
path: S) {
unsafe {
let path_ref = path.as_ref();
let cstr = CString::new(path_ref).unwrap();
gccjit_sys::gcc_jit_context_dump_reproducer_to_file(self.ptr,
cstr.as_ptr());
}
}
pub fn dump_to_file<S: AsRef<str>>(&self, path: S, update_locations: bool) {
unsafe {
let path_ref = path.as_ref();
let cstr = CString::new(path_ref).unwrap();
gccjit_sys::gcc_jit_context_dump_to_file(self.ptr, cstr.as_ptr(), update_locations as c_int);
}
}
/// Creates a new parameter with a given type, name, and location.
pub fn new_parameter<'a, S: AsRef<str>>(&'a self,
loc: Option<Location<'a>>,
ty: types::Type<'a>,
name: S) -> Parameter<'a> {
let name_ref = name.as_ref();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
let cstr = CString::new(name_ref).unwrap();
let ptr = gccjit_sys::gcc_jit_context_new_param(self.ptr,
loc_ptr,
types::get_ptr(&ty),
cstr.as_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
parameter::from_ptr(ptr)
}
}
/// Get a builtin function from gcc. It's not clear what functions are
/// builtin and you'll likely need to consult the GCC internal docs
/// for a full list.
pub fn get_builtin_function<'a, S: AsRef<str>>(&'a self, name: S) -> Function<'a> {
let name_ref = name.as_ref();
unsafe {
let cstr = CString::new(name_ref).unwrap();
let ptr = gccjit_sys::gcc_jit_context_get_builtin_function(self.ptr,
cstr.as_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
function::from_ptr(ptr)
}
}
#[cfg(feature="master")]
/// Get a target-dependant builtin function from gcc. It's not clear what functions are
/// builtin and you'll likely need to consult the GCC internal docs
/// for a full list.
pub fn get_target_builtin_function<'a, S: AsRef<str>>(&'a self, name: S) -> Function<'a> {
let name_ref = name.as_ref();
unsafe {
let cstr = CString::new(name_ref).unwrap();
let ptr = gccjit_sys::gcc_jit_context_get_target_builtin_function(self.ptr,
cstr.as_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
function::from_ptr(ptr)
}
}
pub fn get_first_error(&self) -> Result<Option<&'ctx str>, Utf8Error> {
unsafe {
let str = gccjit_sys::gcc_jit_context_get_first_error(self.ptr);
if str.is_null() {
Ok(None)
}
else {
Ok(Some(CStr::from_ptr(str).to_str()?))
}
}
}
pub fn get_last_error(&self) -> Result<Option<&'ctx str>, Utf8Error> {
unsafe {
let str = gccjit_sys::gcc_jit_context_get_last_error(self.ptr);
if str.is_null() {
Ok(None)
}
else {
Ok(Some(CStr::from_ptr(str).to_str()?))
}
}
}
// TODO: use the logfile argument.
pub fn set_logfile<S: AsRef<str>>(&self, _logfile: S) {
use std::os::raw::c_void;
extern {
static stderr: *mut c_void;
}
unsafe {
gccjit_sys::gcc_jit_context_set_logfile(self.ptr, stderr as *mut _, 0, 0);
}
}
pub fn add_top_level_asm(&self, loc: Option<Location<'ctx>>, asm_stmts: &str) {
let asm_stmts = CString::new(asm_stmts).unwrap();
let loc_ptr =
match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
unsafe {
gccjit_sys::gcc_jit_context_add_top_level_asm(self.ptr, loc_ptr, asm_stmts.as_ptr());
}
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.get_last_error() {
panic!("{}", error);
}
}
}
impl<'ctx> Drop for Context<'ctx> {
fn drop(&mut self) {
unsafe {
gccjit_sys::gcc_jit_context_release(self.ptr);
}
}
}
#[doc(hidden)]
pub unsafe fn get_ptr<'ctx>(ctx: &'ctx Context<'ctx>) -> *mut gccjit_sys::gcc_jit_context {
ctx.ptr
}
pub unsafe fn from_ptr<'ctx>(ptr: *mut gccjit_sys::gcc_jit_context) -> Context<'ctx> {
Context {
marker: PhantomData,
ptr
}
}
#[cfg(test)]
mod tests {
use super::super::*;
use std::mem;
#[test]
fn create_context() {
let _ctx = Context::default();
}
#[test]
fn create_child_context() {
let ctx = Context::default();
let _child = ctx.new_child_context();
}
#[test]
fn create_location() {
let ctx = Context::default();
let _location = ctx.new_location("hello.rs", 1, 32);
}
#[test]
fn create_type() {
let ctx = Context::default();
let _int_type = ctx.new_type::<i32>();
}
#[test]
fn create_field() {
let ctx = Context::default();
let int_type = ctx.new_type::<i32>();
let _int_field = ctx.new_field(None, int_type, "x");
}
#[test]
fn basic_function() {
let context = Context::default();
let int_ty = context.new_type::<i32>();
let parameter = context.new_parameter(None, int_ty, "x");
let fun = context.new_function(None, FunctionType::Exported, int_ty, &[parameter], "square", false);
let block = fun.new_block("main_block");
let parm = fun.get_param(0).to_rvalue();
let square = parm * parm;
block.end_with_return(None, square);
let result = context.compile();
unsafe {
let func_ptr = result.get_function("square");
assert!(!func_ptr.is_null());
let func : extern "C" fn(i32) -> i32 = mem::transmute(func_ptr);
assert_eq!(func(4), 16);
assert_eq!(func(9), 81);
assert_eq!(func(-2), 4);
}
}
/* Uncomment these tests periodically to remind yourself of
* 1) why rust is awesome and 2) make sure that you've set up
* lifetimes correctly so that these invariant violations are
* caught at compile time.
#[test]
fn invalid_type_lifetime() {
panic!("this shouldn't compile!");
let ty = {
let ctx = Context::default();
ctx.new_type::<i32>()
};
}
#[test]
fn create_incorrect_child_context() {
let child = {
let mut ctx = Context::default();
ctx.new_child_context()
};
}*/
}
#[derive(Clone, Copy)]
pub enum CType {
Bool,
Char,
UChar,
SChar,
Short,
UShort,
Int,
UInt,
Long,
ULong,
LongLong,
ULongLong,
SizeT,
Int8t,
Int16t,
Int32t,
Int64t,
Int128t,
UInt8t,
UInt16t,
UInt32t,
UInt64t,
UInt128t,
ConstCharPtr,
BFloat16,
Float16,
Float32,
Float64,
Float128,
}
impl CType {
pub(crate) fn to_sys(self) -> gccjit_sys::gcc_jit_types {
use gccjit_sys::gcc_jit_types::*;
use self::CType::*;
match self {
Bool => GCC_JIT_TYPE_BOOL,
Char => GCC_JIT_TYPE_CHAR,
UChar => GCC_JIT_TYPE_UNSIGNED_CHAR,
SChar => GCC_JIT_TYPE_SIGNED_CHAR,
Short => GCC_JIT_TYPE_SHORT,
UShort => GCC_JIT_TYPE_UNSIGNED_SHORT,
Int => GCC_JIT_TYPE_INT,
UInt => GCC_JIT_TYPE_UNSIGNED_INT,
Long => GCC_JIT_TYPE_LONG,
ULong => GCC_JIT_TYPE_UNSIGNED_LONG,
LongLong => GCC_JIT_TYPE_LONG_LONG,
ULongLong => GCC_JIT_TYPE_UNSIGNED_LONG_LONG,
SizeT => GCC_JIT_TYPE_SIZE_T,
Int8t => GCC_JIT_TYPE_INT8_T,
Int16t => GCC_JIT_TYPE_INT16_T,
Int32t => GCC_JIT_TYPE_INT32_T,
Int64t => GCC_JIT_TYPE_INT64_T,
Int128t => GCC_JIT_TYPE_INT128_T,
UInt8t => GCC_JIT_TYPE_UINT8_T,
UInt16t => GCC_JIT_TYPE_UINT16_T,
UInt32t => GCC_JIT_TYPE_UINT32_T,
UInt64t => GCC_JIT_TYPE_UINT64_T,
UInt128t => GCC_JIT_TYPE_UINT128_T,
ConstCharPtr => GCC_JIT_TYPE_CONST_CHAR_PTR,
BFloat16 => GCC_JIT_TYPE_BFLOAT16,
Float16 => GCC_JIT_TYPE_FLOAT16,
Float32 => GCC_JIT_TYPE_FLOAT32,
Float64 => GCC_JIT_TYPE_FLOAT64,
Float128 => GCC_JIT_TYPE_FLOAT128,
}
}
}