use llvm_sys::{
core::{
LLVMAddIncoming, LLVMBuildBr, LLVMBuildCondBr, LLVMBuildFAdd, LLVMBuildFCmp, LLVMBuildFMul,
LLVMBuildFSub, LLVMBuildPhi, LLVMBuildRet, LLVMBuildUIToFP, LLVMCreateBuilderInContext,
LLVMDisposeBuilder, LLVMGetInsertBlock, 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.bb_ref());
}
}
/// Get the BasicBlock the IRBuilder currently inputs into.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn get_insert_block(&self) -> BasicBlock<'llvm> {
let bb_ref = unsafe { LLVMGetInsertBlock(self.builder) };
assert!(!bb_ref.is_null());
BasicBlock::new(bb_ref)
}
/// 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 [fcmpult](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 [fcmpone](https://llvm.org/docs/LangRef.html#fcmp-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn fcmpone(&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::LLVMRealONE,
lhs.value_ref(),
rhs.value_ref(),
b"fcmpone\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());
}
/// Emit an unconditional [br](https://llvm.org/docs/LangRef.html#br-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn br(&self, dest: BasicBlock<'llvm>) {
let br_ref = unsafe { LLVMBuildBr(self.builder, dest.bb_ref()) };
assert!(!br_ref.is_null());
}
/// Emit a conditional [br](https://llvm.org/docs/LangRef.html#br-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn cond_br(&self, cond: Value<'llvm>, then: BasicBlock<'llvm>, else_: BasicBlock<'llvm>) {
let br_ref = unsafe {
LLVMBuildCondBr(
self.builder,
cond.value_ref(),
then.bb_ref(),
else_.bb_ref(),
)
};
assert!(!br_ref.is_null());
}
/// Emit a [phi](https://llvm.org/docs/LangRef.html#phi-instruction) instruction.
///
/// # Panics
///
/// Panics if LLVM API returns a `null` pointer.
pub fn phi(
&self,
phi_type: Type<'llvm>,
incoming: &[(Value<'llvm>, BasicBlock<'llvm>)],
) -> Value<'llvm> {
let phi_ref =
unsafe { LLVMBuildPhi(self.builder, phi_type.type_ref(), b"phi\0".as_ptr().cast()) };
assert!(!phi_ref.is_null());
for (val, bb) in incoming {
debug_assert_eq!(
val.type_of().kind(),
phi_type.kind(),
"Type of incoming phi value must be the same as the type used to build the phi node."
);
unsafe {
LLVMAddIncoming(phi_ref, &mut val.value_ref() as _, &mut bb.bb_ref() as _, 1);
}
}
Value::new(phi_ref)
}
}
impl Drop for IRBuilder<'_> {
fn drop(&mut self) {
unsafe { LLVMDisposeBuilder(self.builder) }
}
}