use llvm_sys::{
core::{
LLVMAddFunction, LLVMAppendBasicBlockInContext, LLVMCreateBasicBlockInContext,
LLVMDisposeModule, LLVMDoubleTypeInContext, LLVMDumpModule, LLVMGetNamedFunction,
LLVMModuleCreateWithNameInContext,
},
orc2::{
LLVMOrcCreateNewThreadSafeContext, LLVMOrcCreateNewThreadSafeModule,
LLVMOrcDisposeThreadSafeContext, LLVMOrcThreadSafeContextGetContext,
LLVMOrcThreadSafeContextRef, LLVMOrcThreadSafeModuleRef,
},
prelude::{LLVMBool, LLVMContextRef, LLVMModuleRef, LLVMTypeRef},
LLVMTypeKind,
};
use std::convert::TryFrom;
use super::{BasicBlock, FnValue, Type};
use crate::SmallCStr;
// Definition of LLVM C API functions using our `repr(transparent)` types.
extern "C" {
fn LLVMFunctionType(
ReturnType: Type<'_>,
ParamTypes: *mut Type<'_>,
ParamCount: ::libc::c_uint,
IsVarArg: LLVMBool,
) -> LLVMTypeRef;
}
/// Wrapper for a LLVM Module with its own LLVM Context.
pub struct Module {
tsctx: LLVMOrcThreadSafeContextRef,
ctx: LLVMContextRef,
module: LLVMModuleRef,
}
impl<'llvm> Module {
/// Create a new Module instance.
///
/// # Panics
///
/// Panics if creating the context or the module fails.
pub fn new() -> Self {
let (tsctx, ctx, module) = unsafe {
// We generate a thread safe context because we are going to jit this IR module and
// there is no method to create a thread safe context wrapper from an existing context
// reference (at the time of writing this).
//
// ThreadSafeContext has shared ownership (start with ref count 1).
// We must explicitly dispose our reference (dec ref count).
let tc = LLVMOrcCreateNewThreadSafeContext();
assert!(!tc.is_null());
let c = LLVMOrcThreadSafeContextGetContext(tc);
let m = LLVMModuleCreateWithNameInContext(b"module\0".as_ptr().cast(), c);
assert!(!c.is_null() && !m.is_null());
(tc, c, m)
};
Module { tsctx, ctx, module }
}
/// Get the raw LLVM context reference.
#[inline]
pub(super) fn ctx(&self) -> LLVMContextRef {
self.ctx
}
/// Get the raw LLVM module reference.
#[inline]
pub(super) fn module(&self) -> LLVMModuleRef {
self.module
}
/// Consume the module and turn in into a raw LLVM ThreadSafeModule reference.
///
/// If ownership of the raw reference is not transferred (eg to the JIT), memory will be leaked
/// in case the reference is disposed explicitly with LLVMOrcDisposeThreadSafeModule.
#[inline]
pub(super) fn into_raw_thread_safe_module(mut self) -> LLVMOrcThreadSafeModuleRef {
let m = std::mem::replace(&mut self.module, std::ptr::null_mut());
// ThreadSafeModule has unique ownership.
// Takes ownership of module and increments ThreadSafeContext ref count.
//
// We must not reference/dispose `m` after this call, but we need to dispose our `tsctx`
// reference.
let tm = unsafe { LLVMOrcCreateNewThreadSafeModule(m, self.tsctx) };
assert!(!tm.is_null());
tm
}
/// Dump LLVM IR emitted into the Module to stdout.
pub fn dump(&self) {
unsafe { LLVMDumpModule(self.module) };
}
/// Get a type reference representing a `f64` float.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn type_f64(&self) -> Type<'llvm> {
let type_ref = unsafe { LLVMDoubleTypeInContext(self.ctx) };
Type::new(type_ref)
}
/// Get a type reference representing a `fn(args) -> ret` function.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn type_fn(&'llvm self, args: &mut [Type<'llvm>], ret: Type<'llvm>) -> Type<'llvm> {
let type_ref = unsafe {
LLVMFunctionType(
ret,
args.as_mut_ptr(),
args.len() as libc::c_uint,
0, /* IsVarArg */
)
};
Type::new(type_ref)
}
/// Add a function with the given `name` and `fn_type` to the module and return a value
/// reference representing the function.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer or `name` could not be converted to a
/// [`SmallCStr`].
pub fn add_fn(&'llvm self, name: &str, fn_type: Type<'llvm>) -> FnValue<'llvm> {
debug_assert_eq!(
fn_type.kind(),
LLVMTypeKind::LLVMFunctionTypeKind,
"Expected a function type when adding a function!"
);
let name = SmallCStr::try_from(name)
.expect("Failed to convert 'name' argument to small C string!");
let value_ref = unsafe { LLVMAddFunction(self.module, name.as_ptr(), fn_type.type_ref()) };
FnValue::new(value_ref)
}
/// Get a function value reference to the function with the given `name` if it was previously
/// added to the module with [`add_fn`][Module::add_fn].
///
/// # Panics
///
/// Panics if `name` could not be converted to a [`SmallCStr`].
pub fn get_fn(&'llvm self, name: &str) -> Option<FnValue<'llvm>> {
let name = SmallCStr::try_from(name)
.expect("Failed to convert 'name' argument to small C string!");
let value_ref = unsafe { LLVMGetNamedFunction(self.module, name.as_ptr()) };
(!value_ref.is_null()).then(|| FnValue::new(value_ref))
}
/// Append a Basic Block to the end of the function referenced by the value reference
/// `fn_value`.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn append_basic_block(&'llvm self, fn_value: FnValue<'llvm>) -> BasicBlock<'llvm> {
let block = unsafe {
LLVMAppendBasicBlockInContext(
self.ctx,
fn_value.value_ref(),
b"block\0".as_ptr().cast(),
)
};
assert!(!block.is_null());
BasicBlock::new(block)
}
/// Create a free-standing Basic Block without adding it to a function.
/// This can be added to a function at a later point in time with
/// [`FnValue::append_basic_block`].
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn create_basic_block(&self) -> BasicBlock<'llvm> {
let block = unsafe { LLVMCreateBasicBlockInContext(self.ctx, b"block\0".as_ptr().cast()) };
assert!(!block.is_null());
BasicBlock::new(block)
}
}
impl Drop for Module {
fn drop(&mut self) {
unsafe {
// In case we turned the module into a ThreadSafeModule, we must not dispose the module
// reference because ThreadSafeModule took ownership!
if !self.module.is_null() {
LLVMDisposeModule(self.module);
}
// Dispose ThreadSafeContext reference (dec ref count) in any case.
LLVMOrcDisposeThreadSafeContext(self.tsctx);
}
}
}