use llvm_sys::{
core::{
LLVMBuildFAdd, LLVMBuildFCmp, LLVMBuildFMul, LLVMBuildFSub, LLVMBuildRet, LLVMBuildUIToFP,
LLVMCreateBuilderInContext, LLVMDisposeBuilder, LLVMPositionBuilderAtEnd,
},
prelude::{LLVMBuilderRef, LLVMValueRef},
LLVMRealPredicate,
};
use std::marker::PhantomData;
use super::{BasicBlock, FnValue, Module, Type, Value};
// Definition of LLVM C API functions using our `repr(transparent)` types.
extern "C" {
fn LLVMBuildCall2(
arg1: LLVMBuilderRef,
arg2: Type<'_>,
Fn: FnValue<'_>,
Args: *mut Value<'_>,
NumArgs: ::libc::c_uint,
Name: *const ::libc::c_char,
) -> LLVMValueRef;
}
/// Wrapper for a LLVM IR Builder.
pub struct IRBuilder<'llvm> {
builder: LLVMBuilderRef,
_ctx: PhantomData<&'llvm ()>,
}
impl<'llvm> IRBuilder<'llvm> {
/// Create a new LLVM IR Builder with the `module`s context.
///
/// # Panics
///
/// Panics if creating the IR Builder fails.
pub fn with_ctx(module: &'llvm Module) -> IRBuilder<'llvm> {
let builder = unsafe { LLVMCreateBuilderInContext(module.ctx()) };
assert!(!builder.is_null());
IRBuilder {
builder,
_ctx: PhantomData,
}
}
/// Position the IR Builder at the end of the given Basic Block.
pub fn pos_at_end(&self, bb: BasicBlock<'llvm>) {
unsafe {
LLVMPositionBuilderAtEnd(self.builder, bb.0);
}
}
/// Emit a [fadd](https://llvm.org/docs/LangRef.html#fadd-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn fadd(&self, lhs: Value<'llvm>, rhs: Value<'llvm>) -> Value<'llvm> {
debug_assert!(lhs.is_f64(), "fadd: Expected f64 as lhs operand!");
debug_assert!(rhs.is_f64(), "fadd: Expected f64 as rhs operand!");
let value_ref = unsafe {
LLVMBuildFAdd(
self.builder,
lhs.value_ref(),
rhs.value_ref(),
b"fadd\0".as_ptr().cast(),
)
};
Value::new(value_ref)
}
/// Emit a [fsub](https://llvm.org/docs/LangRef.html#fsub-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn fsub(&self, lhs: Value<'llvm>, rhs: Value<'llvm>) -> Value<'llvm> {
debug_assert!(lhs.is_f64(), "fsub: Expected f64 as lhs operand!");
debug_assert!(rhs.is_f64(), "fsub: Expected f64 as rhs operand!");
let value_ref = unsafe {
LLVMBuildFSub(
self.builder,
lhs.value_ref(),
rhs.value_ref(),
b"fsub\0".as_ptr().cast(),
)
};
Value::new(value_ref)
}
/// Emit a [fmul](https://llvm.org/docs/LangRef.html#fmul-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn fmul(&self, lhs: Value<'llvm>, rhs: Value<'llvm>) -> Value<'llvm> {
debug_assert!(lhs.is_f64(), "fmul: Expected f64 as lhs operand!");
debug_assert!(rhs.is_f64(), "fmul: Expected f64 as rhs operand!");
let value_ref = unsafe {
LLVMBuildFMul(
self.builder,
lhs.value_ref(),
rhs.value_ref(),
b"fmul\0".as_ptr().cast(),
)
};
Value::new(value_ref)
}
/// Emit a [fcmult](https://llvm.org/docs/LangRef.html#fcmp-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn fcmpult(&self, lhs: Value<'llvm>, rhs: Value<'llvm>) -> Value<'llvm> {
debug_assert!(lhs.is_f64(), "fcmplt: Expected f64 as lhs operand!");
debug_assert!(rhs.is_f64(), "fcmplt: Expected f64 as rhs operand!");
let value_ref = unsafe {
LLVMBuildFCmp(
self.builder,
LLVMRealPredicate::LLVMRealULT,
lhs.value_ref(),
rhs.value_ref(),
b"fcmplt\0".as_ptr().cast(),
)
};
Value::new(value_ref)
}
/// Emit a [uitofp](https://llvm.org/docs/LangRef.html#uitofp-to-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn uitofp(&self, val: Value<'llvm>, dest_type: Type<'llvm>) -> Value<'llvm> {
debug_assert!(val.is_int(), "uitofp: Expected integer operand!");
let value_ref = unsafe {
LLVMBuildUIToFP(
self.builder,
val.value_ref(),
dest_type.type_ref(),
b"uitofp\0".as_ptr().cast(),
)
};
Value::new(value_ref)
}
/// Emit a [call](https://llvm.org/docs/LangRef.html#call-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn call(&self, fn_value: FnValue<'llvm>, args: &mut [Value<'llvm>]) -> Value<'llvm> {
let value_ref = unsafe {
LLVMBuildCall2(
self.builder,
fn_value.ret_type(),
fn_value,
args.as_mut_ptr(),
args.len() as libc::c_uint,
b"call\0".as_ptr().cast(),
)
};
Value::new(value_ref)
}
/// Emit a [ret](https://llvm.org/docs/LangRef.html#ret-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn ret(&self, ret: Value<'llvm>) {
let ret = unsafe { LLVMBuildRet(self.builder, ret.value_ref()) };
assert!(!ret.is_null());
}
}
impl Drop for IRBuilder<'_> {
fn drop(&mut self) {
unsafe { LLVMDisposeBuilder(self.builder) }
}
}