blob: 1502350a897925c7ce915d4f20d0be55ee5b85c1 [file] [log] [blame] [edit]
use std::marker::PhantomData;
use std::ffi::CString;
use std::fmt;
use std::ptr;
use std::mem;
use std::os::raw::c_int;
use asm::ExtendedAsm;
use block;
use context::{Case, Context};
use object::{self, ToObject, Object};
use function::{self, Function};
use location::{self, Location};
use rvalue::{self, ToRValue};
use lvalue::{self, ToLValue};
/// BinaryOp is a enum representing the various binary operations
/// that gccjit knows how to codegen.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum BinaryOp {
Plus,
Minus,
Mult,
Divide,
Modulo,
BitwiseAnd,
BitwiseXor,
BitwiseOr,
LogicalAnd,
LogicalOr,
LShift,
RShift
}
/// UnaryOp is an enum representing the various unary operations
/// that gccjit knows how to codegen.
#[repr(C)]
#[derive(Clone, Copy)]
pub enum UnaryOp {
Minus,
BitwiseNegate,
LogicalNegate,
Abs
}
/// ComparisonOp is an enum representing the various comparisons that
/// gccjit is capable of doing.
#[repr(C)]
#[derive(Debug)]
pub enum ComparisonOp {
Equals,
NotEquals,
LessThan,
LessThanEquals,
GreaterThan,
GreaterThanEquals
}
/// Block represents a basic block in gccjit. Blocks are created by functions.
/// A basic block consists of a series of instructions terminated by a terminator
/// instruction, which can be either a jump to one block, a conditional branch to
/// two blocks (true/false branches), a return, or a void return.
#[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> {
unsafe {
let ptr = gccjit_sys::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> {
unsafe {
let ptr = gccjit_sys::gcc_jit_block_get_function(self.ptr);
function::from_ptr(ptr)
}
}
/// Evaluates the rvalue parameter and discards its result. Equivalent
/// to (void)<expr> in C.
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()
};
unsafe {
gccjit_sys::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_block: Block<'ctx>, catch_block: Block<'ctx>) {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
gccjit_sys::gcc_jit_block_add_try_catch(self.ptr, loc_ptr, try_block.ptr, catch_block.ptr);
}
}
#[cfg(feature="master")]
pub fn add_try_finally(&self, loc: Option<Location<'ctx>>, try_block: Block<'ctx>, finally_block: Block<'ctx>) {
let loc_ptr = match loc {
Some(loc) => unsafe { location::get_ptr(&loc) },
None => ptr::null_mut()
};
unsafe {
gccjit_sys::gcc_jit_block_add_try_finally(self.ptr, loc_ptr, try_block.ptr, finally_block.ptr);
}
}
/// Assigns the value of an rvalue to an lvalue directly. Equivalent
/// to <lvalue> = <rvalue> in C.
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()
};
unsafe {
gccjit_sys::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);
}
}
/// Performs a binary operation on an LValue and an RValue, assigning
/// the result of the binary operation to the LValue upon completion.
/// Equivalent to the *=, +=, -=, etc. operator family in C.
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()
};
unsafe {
gccjit_sys::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));
}
}
/// Adds a comment to a block. It's unclear from the documentation what
/// this actually means.
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()
};
unsafe {
let cstr = CString::new(message_ref).unwrap();
gccjit_sys::gcc_jit_block_add_comment(self.ptr,
loc_ptr,
cstr.as_ptr());
}
}
/// Terminates a block by branching to one of two blocks, depending
/// on the value of a conditional RValue.
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()
};
unsafe {
gccjit_sys::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);
}
}
/// Terminates a block by unconditionally jumping to another block.
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()
};
unsafe {
gccjit_sys::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);
}
}
/// Terminates a block by returning from the containing function, setting
/// the rvalue to be the return value of the function. This is equivalent
/// to C's "return <expr>". This function can only be used to terminate
/// a block within a function whose return type is not void.
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()
};
unsafe {
gccjit_sys::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);
}
}
}
/// Terminates a block by returning from the containing function, returning
/// no value. This is equivalent to C's bare "return" with no expression.
/// This function can only be used to terminate a block within a function
/// that returns void.
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()
};
unsafe {
gccjit_sys::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()
};
unsafe {
gccjit_sys::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);
}
}
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(),
};
unsafe {
ExtendedAsm::from_ptr(gccjit_sys::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(),
};
unsafe {
ExtendedAsm::from_ptr(gccjit_sys::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))
}
}
}
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
}