aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/llvm/module.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/llvm/module.rs')
-rw-r--r--src/llvm/module.rs157
1 files changed, 157 insertions, 0 deletions
diff --git a/src/llvm/module.rs b/src/llvm/module.rs
new file mode 100644
index 0000000..e0aad96
--- /dev/null
+++ b/src/llvm/module.rs
@@ -0,0 +1,157 @@
+use llvm_sys::{
+ core::{
+ LLVMAddFunction, LLVMAppendBasicBlockInContext, LLVMContextCreate, LLVMContextDispose,
+ LLVMDisposeModule, LLVMDoubleTypeInContext, LLVMDumpModule, LLVMGetNamedFunction,
+ LLVMModuleCreateWithNameInContext,
+ },
+ prelude::{LLVMBool, LLVMContextRef, LLVMModuleRef, LLVMTypeRef},
+ LLVMTypeKind,
+};
+
+use std::convert::TryFrom;
+use std::marker::PhantomData;
+
+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 {
+ 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 (ctx, module) = unsafe {
+ let c = LLVMContextCreate();
+ let m = LLVMModuleCreateWithNameInContext(b"module\0".as_ptr().cast(), c);
+ assert!(!c.is_null() && !m.is_null());
+ (c, m)
+ };
+
+ Module { 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
+ }
+
+ /// 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(block, PhantomData)
+ }
+}
+
+impl Drop for Module {
+ fn drop(&mut self) {
+ unsafe {
+ LLVMDisposeModule(self.module);
+ LLVMContextDispose(self.ctx);
+ }
+ }
+}