aboutsummaryrefslogblamecommitdiffhomepage
path: root/src/llvm/builder.rs
blob: 8f581f925ef9726c67cb8d7633a550a92a8a5cb1 (plain) (tree)
1
2
3
4
5

               


                                                                                                   













































                                                                          
                                                                


         











                                                                 



























































                                                                                       
                                                                                          



















                                                                                 




















                                                                                          














































                                                                                              























































                                                                                                     






                                                   
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) }
    }
}