use std::ffi::CString;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_int;
use std::ptr;
use asm::ExtendedAsm;
use block;
use context::{Case, Context};
use function::{self, Function};
use location::{self, Location};
use lvalue::{self, ToLValue};
use object::{self, Object, ToObject};
#[cfg(feature = "master")]
use region::{self, Region};
use rvalue::{self, ToRValue};
use crate::with_lib;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum BinaryOp {
Plus,
Minus,
Mult,
Divide,
Modulo,
BitwiseAnd,
BitwiseXor,
BitwiseOr,
LogicalAnd,
LogicalOr,
LShift,
RShift,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub enum UnaryOp {
Minus,
BitwiseNegate,
LogicalNegate,
Abs,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub enum ComparisonOp {
Equals,
NotEquals,
LessThan,
LessThanEquals,
GreaterThan,
GreaterThanEquals,
}
#[derive(Copy, Clone, Eq, Hash, PartialEq)]
pub struct Block<'ctx> {
marker: PhantomData<&'ctx Context<'ctx>>,
ptr: *mut gccjit_sys::gcc_jit_block,
}
impl<'ctx> ToObject<'ctx> for Block<'ctx> {
fn to_object(&self) -> Object<'ctx> {
with_lib(|lib| unsafe {
let ptr = lib.gcc_jit_block_as_object(self.ptr);
object::from_ptr(ptr)
})
}
}
impl<'ctx> fmt::Debug for Block<'ctx> {
fn fmt<'a>(&self, fmt: &mut fmt::Formatter<'a>) -> Result<(), fmt::Error> {
let obj = self.to_object();
obj.fmt(fmt)
}
}
impl<'ctx> Block<'ctx> {
pub fn get_function(&self) -> Function<'ctx> {
with_lib(|lib| unsafe {
let ptr = lib.gcc_jit_block_get_function(self.ptr);
function::from_ptr(ptr)
})
}
#[cfg(feature = "master")]
pub fn get_successors(&self) -> Vec<Block<'ctx>> {
with_lib(|lib| unsafe {
let count = lib.gcc_jit_block_get_successor_count(self.ptr);
(0..count)
.map(|index| from_ptr(lib.gcc_jit_block_get_successor(self.ptr, index)))
.collect()
})
}
pub fn add_eval<T: ToRValue<'ctx>>(&self, loc: Option<Location<'ctx>>, value: T) {
let rvalue = value.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_add_eval(self.ptr, loc_ptr, rvalue::get_ptr(&rvalue));
});
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{}", error);
}
}
#[cfg(feature = "master")]
pub fn add_try_catch(
&self,
loc: Option<Location<'ctx>>,
try_region: Region<'ctx>,
catch_region: Region<'ctx>,
) {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_add_try_catch(
self.ptr,
loc_ptr,
region::get_ptr(&try_region),
region::get_ptr(&catch_region),
);
});
}
#[cfg(feature = "master")]
pub fn add_try_finally(
&self,
loc: Option<Location<'ctx>>,
try_region: Region<'ctx>,
finally_region: Region<'ctx>,
) {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_add_try_finally(
self.ptr,
loc_ptr,
region::get_ptr(&try_region),
region::get_ptr(&finally_region),
);
});
}
#[cfg(feature = "master")]
pub fn add_cleanup(
&self,
loc: Option<Location<'ctx>>,
try_region: Region<'ctx>,
cleanup_region: Region<'ctx>,
) {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_add_cleanup(
self.ptr,
loc_ptr,
region::get_ptr(&try_region),
region::get_ptr(&cleanup_region),
);
});
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{}", error);
}
}
pub fn add_assignment<L: ToLValue<'ctx>, R: ToRValue<'ctx>>(
&self,
loc: Option<Location<'ctx>>,
assign_target: L,
value: R,
) {
let lvalue = assign_target.to_lvalue();
let rvalue = value.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_add_assignment(
self.ptr,
loc_ptr,
lvalue::get_ptr(&lvalue),
rvalue::get_ptr(&rvalue),
);
});
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{}", error);
}
}
pub fn add_assignment_op<L: ToLValue<'ctx>, R: ToRValue<'ctx>>(
&self,
loc: Option<Location<'ctx>>,
assign_target: L,
op: BinaryOp,
value: R,
) {
let lvalue = assign_target.to_lvalue();
let rvalue = value.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_add_assignment_op(
self.ptr,
loc_ptr,
lvalue::get_ptr(&lvalue),
mem::transmute::<BinaryOp, gccjit_sys::gcc_jit_binary_op>(op),
rvalue::get_ptr(&rvalue),
);
});
}
pub fn add_comment<S: AsRef<str>>(&self, loc: Option<Location<'ctx>>, message: S) {
let message_ref = message.as_ref();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
let cstr = CString::new(message_ref).unwrap();
lib.gcc_jit_block_add_comment(self.ptr, loc_ptr, cstr.as_ptr());
});
}
pub fn end_with_conditional<T: ToRValue<'ctx>>(
&self,
loc: Option<Location<'ctx>>,
cond: T,
on_true: Block<'ctx>,
on_false: Block<'ctx>,
) {
let cond_rvalue = cond.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_end_with_conditional(
self.ptr,
loc_ptr,
rvalue::get_ptr(&cond_rvalue),
on_true.ptr,
on_false.ptr,
);
});
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{}", error);
}
}
pub fn end_with_jump(&self, loc: Option<Location<'ctx>>, target: Block<'ctx>) {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_end_with_jump(self.ptr, loc_ptr, target.ptr);
});
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{}", error);
}
}
pub fn end_with_return<T: ToRValue<'ctx>>(&self, loc: Option<Location<'ctx>>, ret: T) {
let ret_rvalue = ret.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_end_with_return(self.ptr, loc_ptr, rvalue::get_ptr(&ret_rvalue));
});
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{}", error);
}
}
pub fn end_with_void_return(&self, loc: Option<Location<'ctx>>) {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_end_with_void_return(self.ptr, loc_ptr);
});
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{}", error);
}
}
pub fn end_with_switch<T: ToRValue<'ctx>>(
&self,
loc: Option<Location<'ctx>>,
expr: T,
default_block: Block<'ctx>,
cases: &[Case<'ctx>],
) {
let expr = expr.to_rvalue();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_end_with_switch(
self.ptr,
loc_ptr,
rvalue::get_ptr(&expr),
block::get_ptr(&default_block),
cases.len() as c_int,
cases.as_ptr() as *mut *mut _,
);
});
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{}", error);
}
}
#[cfg(feature = "master")]
pub fn end_with_fallthrough(&self, loc: Option<Location<'ctx>>) {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
lib.gcc_jit_block_end_with_fallthrough(self.ptr, loc_ptr);
});
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{}", error);
}
}
pub fn add_extended_asm(
&self,
loc: Option<Location<'ctx>>,
asm_template: &str,
) -> ExtendedAsm<'ctx> {
let asm_template = CString::new(asm_template).unwrap();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
ExtendedAsm::from_ptr(lib.gcc_jit_block_add_extended_asm(
self.ptr,
loc_ptr,
asm_template.as_ptr(),
))
})
}
pub fn end_with_extended_asm_goto(
&self,
loc: Option<Location<'ctx>>,
asm_template: &str,
goto_blocks: &[Block<'ctx>],
fallthrough_block: Option<Block<'ctx>>,
) -> ExtendedAsm<'ctx> {
let asm_template = CString::new(asm_template).unwrap();
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut(),
};
let fallthrough_block_ptr = match fallthrough_block {
Some(ref block) => unsafe { get_ptr(block) },
None => ptr::null_mut(),
};
with_lib(|lib| unsafe {
ExtendedAsm::from_ptr(lib.gcc_jit_block_end_with_extended_asm_goto(
self.ptr,
loc_ptr,
asm_template.as_ptr(),
goto_blocks.len() as c_int,
goto_blocks.as_ptr() as *mut _,
fallthrough_block_ptr,
))
})
}
}
#[cfg(feature = "master")]
pub fn clone_blocks<'ctx>(blocks: &[Block<'ctx>]) -> Vec<Block<'ctx>> {
if blocks.is_empty() {
return Vec::new();
}
with_lib(|lib| {
let mut src: Vec<_> = blocks
.iter()
.map(|block| unsafe { get_ptr(block) })
.collect();
let mut dst: Vec<*mut gccjit_sys::gcc_jit_block> = vec![ptr::null_mut(); blocks.len()];
unsafe {
lib.gcc_jit_blocks_clone(src.len() as c_int, src.as_mut_ptr(), dst.as_mut_ptr());
dst.into_iter().map(|ptr| from_ptr(ptr)).collect()
}
})
}
pub unsafe fn from_ptr<'ctx>(ptr: *mut gccjit_sys::gcc_jit_block) -> Block<'ctx> {
Block {
marker: PhantomData,
ptr,
}
}
pub unsafe fn get_ptr<'ctx>(block: &Block<'ctx>) -> *mut gccjit_sys::gcc_jit_block {
block.ptr
}