blob: 3f01769f50e5e7e83ee6c93055fc5bc3cb19324e [file] [log] [blame] [edit]
use std::marker::PhantomData;
use std::fmt;
use std::ptr;
use block::Block;
use block;
use context::Context;
use location::Location;
use location;
#[cfg(feature="master")]
use lvalue::{AttributeValue, Visibility};
use lvalue::LValue;
use lvalue;
use object::{ToObject, Object};
use object;
use parameter::Parameter;
use parameter;
use rvalue::{self, RValue};
use std::ffi::CString;
use types::Type;
use types;
/// FunctionType informs gccjit what sort of function a new function will be.
/// An exported function is a function that will be exported using the CompileResult
/// interface, able to be called outside of the jit. An internal function is
/// a function that cannot be called outside of jitted code. An extern function
/// is a function with external linkage, and always inline is a function that is
/// always inlined wherever it is called and cannot be accessed outside of the jit.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FunctionType {
/// Defines a function that is "exported" by the JIT and can be called from
/// Rust.
Exported,
/// Defines a function that is internal to the JIT and cannot be called
/// from Rust, but can be called from jitted code.
Internal,
/// Defines a function with external linkage.
Extern,
/// Defines a function that should always be inlined whenever it is called.
/// A function with this type cannot be called from Rust. If the optimization
/// level is None, this function will not actually be inlined, but it still
/// can only be called from within jitted code.
AlwaysInline
}
#[cfg(feature="master")]
#[derive(Clone, Debug)]
pub enum FnAttribute<'a> {
Alias(&'a str),
AlwaysInline,
Inline,
NoInline,
Target(&'a str),
Used,
Visibility(Visibility),
Cold,
ReturnsTwice,
Pure,
Const,
Weak,
NonNull(Vec<std::ffi::c_int>),
}
#[cfg(feature="master")]
impl<'a> FnAttribute<'a> {
fn get_value(&self) -> AttributeValue {
match *self {
FnAttribute::Alias(value) | FnAttribute::Target(value) => AttributeValue::String(value),
FnAttribute::Visibility(visibility) => AttributeValue::String(visibility.as_str()),
FnAttribute::AlwaysInline
| FnAttribute::Inline
| FnAttribute::NoInline
| FnAttribute::Used
| FnAttribute::Cold
| FnAttribute::ReturnsTwice
| FnAttribute::Pure
| FnAttribute::Const
| FnAttribute::Weak => AttributeValue::None,
FnAttribute::NonNull(ref value) => {
debug_assert!(
value.iter().all(|attr| *attr > 0),
"all values must be > 0 for non-null attribute",
);
AttributeValue::IntArray(value)
}
}
}
fn as_sys(&self) -> gccjit_sys::gcc_jit_fn_attribute {
match *self {
FnAttribute::Alias(_) => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_ALIAS,
FnAttribute::AlwaysInline => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE,
FnAttribute::Inline => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_INLINE,
FnAttribute::NoInline => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_NOINLINE,
FnAttribute::Target(_) => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_TARGET,
FnAttribute::Used => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_USED,
FnAttribute::Visibility(_) => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_VISIBILITY,
FnAttribute::Cold => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_COLD,
FnAttribute::ReturnsTwice => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE,
FnAttribute::Pure => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_PURE,
FnAttribute::Const => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_CONST,
FnAttribute::Weak => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_WEAK,
FnAttribute::NonNull(_) => gccjit_sys::gcc_jit_fn_attribute::GCC_JIT_FN_ATTRIBUTE_NONNULL,
}
}
}
/// Function is gccjit's representation of a function. Functions are constructed
/// by constructing basic blocks and connecting them together. Locals are declared
/// at the function level.
#[derive(Copy, Clone, Eq, Hash, PartialEq)]
pub struct Function<'ctx> {
marker: PhantomData<&'ctx Context<'ctx>>,
ptr: *mut gccjit_sys::gcc_jit_function
}
impl<'ctx> ToObject<'ctx> for Function<'ctx> {
fn to_object(&self) -> Object<'ctx> {
unsafe {
let ptr = gccjit_sys::gcc_jit_function_as_object(self.ptr);
object::from_ptr(ptr)
}
}
}
impl<'ctx> fmt::Debug for Function<'ctx> {
fn fmt<'a>(&self, fmt: &mut fmt::Formatter<'a>) -> Result<(), fmt::Error> {
let obj = self.to_object();
obj.fmt(fmt)
}
}
impl<'ctx> Function<'ctx> {
pub fn get_param(&self, idx: i32) -> Parameter<'ctx> {
unsafe {
let ptr = gccjit_sys::gcc_jit_function_get_param(self.ptr, idx);
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{} ({:?})", error, self);
}
parameter::from_ptr(ptr)
}
}
pub fn get_param_count(&self) -> usize {
unsafe {
gccjit_sys::gcc_jit_function_get_param_count(self.ptr) as usize
}
}
pub fn get_return_type(&self) -> Type<'ctx> {
unsafe {
types::from_ptr(gccjit_sys::gcc_jit_function_get_return_type(self.ptr))
}
}
pub fn get_address(&self, loc: Option<Location<'ctx>>) -> RValue<'ctx> {
unsafe {
let loc_ptr = match loc {
Some(loc) => location::get_ptr(&loc),
None => ptr::null_mut()
};
let ptr = gccjit_sys::gcc_jit_function_get_address(self.ptr, loc_ptr);
rvalue::from_ptr(ptr)
}
}
pub fn dump_to_dot<S: AsRef<str>>(&self, path: S) {
unsafe {
let cstr = CString::new(path.as_ref()).unwrap();
gccjit_sys::gcc_jit_function_dump_to_dot(self.ptr, cstr.as_ptr());
}
}
pub fn new_block<S: AsRef<str>>(&self, name: S) -> Block<'ctx> {
unsafe {
let cstr = CString::new(name.as_ref()).unwrap();
let ptr = gccjit_sys::gcc_jit_function_new_block(self.ptr,
cstr.as_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{} ({:?})", error, self);
}
block::from_ptr(ptr)
}
}
#[cfg(feature="master")]
pub fn set_personality_function(&self, personality_func: Function<'ctx>) {
unsafe {
gccjit_sys::gcc_jit_function_set_personality_function(self.ptr, personality_func.ptr);
}
}
pub fn new_local<S: AsRef<str>>(&self,
loc: Option<Location<'ctx>>,
ty: Type<'ctx>,
name: S) -> LValue<'ctx> {
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_function_new_local(self.ptr,
loc_ptr,
types::get_ptr(&ty),
cstr.as_ptr());
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{} ({:?})", error, self);
}
lvalue::from_ptr(ptr)
}
}
#[cfg(feature="master")]
pub fn new_temp(&self, loc: Option<Location<'ctx>>, ty: Type<'ctx>) -> LValue<'ctx> {
unsafe {
let loc_ptr = match loc {
Some(loc) => location::get_ptr(&loc),
None => ptr::null_mut()
};
let ptr = gccjit_sys::gcc_jit_function_new_temp(self.ptr, loc_ptr, types::get_ptr(&ty));
#[cfg(debug_assertions)]
if let Ok(Some(error)) = self.to_object().get_context().get_last_error() {
panic!("{} ({:?})", error, self);
}
lvalue::from_ptr(ptr)
}
}
#[cfg(feature="master")]
pub fn add_attribute<'a>(&self, attribute: FnAttribute<'a>) {
let value = attribute.get_value();
match value {
AttributeValue::Int(value) => {
// Basically the same as `IntArray` but for only one element.
let value = &[value];
unsafe {
gccjit_sys::gcc_jit_function_add_integer_array_attribute(
self.ptr,
attribute.as_sys(),
value.as_ptr(),
value.len() as _,
);
}
}
AttributeValue::IntArray(value) => {
unsafe {
gccjit_sys::gcc_jit_function_add_integer_array_attribute(
self.ptr,
attribute.as_sys(),
value.as_ptr(),
value.len() as _,
);
}
}
AttributeValue::None => {
unsafe {
gccjit_sys::gcc_jit_function_add_attribute(self.ptr, attribute.as_sys());
}
},
AttributeValue::String(string) => {
let cstr = CString::new(string).unwrap();
unsafe {
gccjit_sys::gcc_jit_function_add_string_attribute(self.ptr, attribute.as_sys(), cstr.as_ptr());
}
},
}
}
}
pub unsafe fn from_ptr<'ctx>(ptr: *mut gccjit_sys::gcc_jit_function) -> Function<'ctx> {
Function {
marker: PhantomData,
ptr
}
}
pub unsafe fn get_ptr<'ctx>(loc: &Function<'ctx>) -> *mut gccjit_sys::gcc_jit_function {
loc.ptr
}