aboutsummaryrefslogblamecommitdiffhomepage
path: root/src/llvm/value.rs
blob: 98d2e779d684b10ff617f8f4f1dfc2d29fc113ab (plain) (tree)
1
2
3
4
5
6
7
8

                 


                                                              
                                                                                             
                                                                                                 
                                                                       



                                



                             
                      




























                                                              











                                                                









































































                                                                                                 

                                





                                                            

                                                                                                 



                                                    


                                                                           
























                                                                                       






                                                                        









                                                                  














































                                                                                                 
#![allow(unused)]

use llvm_sys::{
    analysis::{LLVMVerifierFailureAction, LLVMVerifyFunction},
    core::{
        LLVMAddIncoming, LLVMAppendExistingBasicBlock, LLVMCountBasicBlocks, LLVMCountParams,
        LLVMDumpValue, LLVMGetParam, LLVMGetValueKind, LLVMGetValueName2, LLVMGlobalGetValueType,
        LLVMIsAFunction, LLVMIsAPHINode, LLVMSetValueName2, LLVMTypeOf,
    },
    prelude::LLVMValueRef,
    LLVMTypeKind, LLVMValueKind,
};
use std::ffi::CStr;
use std::marker::PhantomData;
use std::ops::Deref;

use super::BasicBlock;
use super::Type;

/// Wrapper for a LLVM Value Reference.
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Value<'llvm>(LLVMValueRef, PhantomData<&'llvm ()>);

impl<'llvm> Value<'llvm> {
    /// Create a new Value instance.
    ///
    /// # Panics
    ///
    /// Panics if `value_ref` is a null pointer.
    pub(super) fn new(value_ref: LLVMValueRef) -> Self {
        assert!(!value_ref.is_null());
        Value(value_ref, PhantomData)
    }

    /// Get the raw LLVM value reference.
    #[inline]
    pub(super) fn value_ref(&self) -> LLVMValueRef {
        self.0
    }

    /// Get the LLVM value kind for the given value reference.
    pub(super) fn kind(&self) -> LLVMValueKind {
        unsafe { LLVMGetValueKind(self.value_ref()) }
    }

    /// Check if value is `function` type.
    pub(super) fn is_function(&self) -> bool {
        let cast = unsafe { LLVMIsAFunction(self.value_ref()) };
        !cast.is_null()
    }

    /// Check if value is `phinode` type.
    pub(super) fn is_phinode(&self) -> bool {
        let cast = unsafe { LLVMIsAPHINode(self.value_ref()) };
        !cast.is_null()
    }

    /// Dump the LLVM Value to stdout.
    pub fn dump(&self) {
        unsafe { LLVMDumpValue(self.value_ref()) };
    }

    /// Get a type reference representing for the given value reference.
    ///
    /// # Panics
    ///
    /// Panics if LLVM API returns a `null` pointer.
    pub fn type_of(&self) -> Type<'llvm> {
        let type_ref = unsafe { LLVMTypeOf(self.value_ref()) };
        Type::new(type_ref)
    }

    /// Set the name for the given value reference.
    ///
    /// # Panics
    ///
    /// Panics if LLVM API returns a `null` pointer.
    pub fn set_name(&self, name: &str) {
        unsafe { LLVMSetValueName2(self.value_ref(), name.as_ptr().cast(), name.len()) };
    }

    /// Get the name for the given value reference.
    ///
    /// # Panics
    ///
    /// Panics if LLVM API returns a `null` pointer.
    pub fn get_name(&self) -> &'llvm str {
        let name = unsafe {
            let mut len: libc::size_t = 0;
            let name = LLVMGetValueName2(self.0, &mut len as _);
            assert!(!name.is_null());

            CStr::from_ptr(name)
        };

        // TODO: Does this string live for the time of the LLVM context?!
        name.to_str()
            .expect("Expected valid UTF8 string from LLVM API")
    }

    /// Check if value is of `f64` type.
    pub fn is_f64(&self) -> bool {
        self.type_of().kind() == LLVMTypeKind::LLVMDoubleTypeKind
    }

    /// Check if value is of integer type.
    pub fn is_int(&self) -> bool {
        self.type_of().kind() == LLVMTypeKind::LLVMIntegerTypeKind
    }
}

/// Wrapper for a LLVM Value Reference specialized for contexts where function values are needed.
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct FnValue<'llvm>(Value<'llvm>);

impl<'llvm> Deref for FnValue<'llvm> {
    type Target = Value<'llvm>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<'llvm> FnValue<'llvm> {
    /// Create a new FnValue instance.
    ///
    /// # Panics
    ///
    /// Panics if `value_ref` is a null pointer.
    pub(super) fn new(value_ref: LLVMValueRef) -> Self {
        let value = Value::new(value_ref);
        debug_assert!(
            value.is_function(),
            "Expected a fn value when constructing FnValue!"
        );

        FnValue(value)
    }

    /// Get a type reference representing the function type (return + args) of the given function
    /// value.
    ///
    /// # Panics
    ///
    /// Panics if LLVM API returns a `null` pointer.
    pub fn fn_type(&self) -> Type<'llvm> {
        // https://github.com/llvm/llvm-project/issues/72798
        let type_ref = unsafe { LLVMGlobalGetValueType(self.value_ref()) };
        Type::new(type_ref)
    }

    /// Get the number of function arguments for the given function value.
    pub fn args(&self) -> usize {
        unsafe { LLVMCountParams(self.value_ref()) as usize }
    }

    /// Get a value reference for the function argument at index `idx`.
    ///
    /// # Panics
    ///
    /// Panics if LLVM API returns a `null` pointer or indexed out of bounds.
    pub fn arg(&self, idx: usize) -> Value<'llvm> {
        assert!(idx < self.args());

        let value_ref = unsafe { LLVMGetParam(self.value_ref(), idx as libc::c_uint) };
        Value::new(value_ref)
    }

    /// Get the number of Basic Blocks for the given function value.
    pub fn basic_blocks(&self) -> usize {
        unsafe { LLVMCountBasicBlocks(self.value_ref()) as usize }
    }

    /// Append a Basic Block to the end of the function value.
    pub fn append_basic_block(&self, bb: BasicBlock<'llvm>) {
        unsafe {
            LLVMAppendExistingBasicBlock(self.value_ref(), bb.bb_ref());
        }
    }

    /// Verify that the given function is valid.
    pub fn verify(&self) -> bool {
        unsafe {
            LLVMVerifyFunction(
                self.value_ref(),
                LLVMVerifierFailureAction::LLVMPrintMessageAction,
            ) == 0
        }
    }
}

/// Wrapper for a LLVM Value Reference specialized for contexts where phi values are needed.
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct PhiValue<'llvm>(Value<'llvm>);

impl<'llvm> Deref for PhiValue<'llvm> {
    type Target = Value<'llvm>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<'llvm> PhiValue<'llvm> {
    /// Create a new PhiValue instance.
    ///
    /// # Panics
    ///
    /// Panics if `value_ref` is a null pointer.
    pub(super) fn new(value_ref: LLVMValueRef) -> Self {
        let value = Value::new(value_ref);
        debug_assert!(
            value.is_phinode(),
            "Expected a phinode value when constructing PhiValue!"
        );

        PhiValue(value)
    }

    /// Add an incoming value to the end of a PHI list.
    pub fn add_incoming(&self, ival: Value<'llvm>, ibb: BasicBlock<'llvm>) {
        debug_assert_eq!(
            ival.type_of().kind(),
            self.type_of().kind(),
            "Type of incoming phi value must be the same as the type used to build the phi node."
        );

        unsafe {
            LLVMAddIncoming(
                self.value_ref(),
                &mut ival.value_ref() as _,
                &mut ibb.bb_ref() as _,
                1,
            );
        }
    }
}