inkwell 0.9.0

Inkwell aims to help you pen your own programming languages by safely wrapping llvm-sys.
Documentation
use llvm_sys::core::{LLVMGetNextUse, LLVMGetUsedValue, LLVMGetUser, LLVMIsABasicBlock, LLVMValueAsBasicBlock};
use llvm_sys::prelude::LLVMUseRef;

use std::marker::PhantomData;

use crate::basic_block::BasicBlock;
use crate::values::{AnyValueEnum, BasicValueEnum};

/// Either [BasicValueEnum] or [BasicBlock].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Operand<'ctx> {
    /// Represents a [BasicValueEnum].
    Value(BasicValueEnum<'ctx>),
    /// Represents a [BasicBlock].
    Block(BasicBlock<'ctx>),
}

impl<'ctx> Operand<'ctx> {
    /// Determines if the [Operand] is a [BasicValueEnum].
    #[inline]
    #[must_use]
    pub fn is_value(self) -> bool {
        matches!(self, Self::Value(_))
    }

    /// Determines if the [Operand] is a [BasicBlock].
    #[inline]
    #[must_use]
    pub fn is_block(self) -> bool {
        matches!(self, Self::Block(_))
    }

    /// If the [Operand] is a [BasicValueEnum], map it into [Option::Some].
    #[inline]
    #[must_use]
    pub fn value(self) -> Option<BasicValueEnum<'ctx>> {
        match self {
            Self::Value(value) => Some(value),
            _ => None,
        }
    }

    /// If the [Operand] is a [BasicBlock], map it into [Option::Some].
    #[inline]
    #[must_use]
    pub fn block(self) -> Option<BasicBlock<'ctx>> {
        match self {
            Self::Block(block) => Some(block),
            _ => None,
        }
    }

    /// Expect [BasicValueEnum], panic with the message if it is not.
    #[inline]
    #[must_use]
    #[track_caller]
    pub fn expect_value(self, msg: &str) -> BasicValueEnum<'ctx> {
        match self {
            Self::Value(value) => value,
            _ => panic!("{msg}"),
        }
    }

    /// Expect [BasicBlock], panic with the message if it is not.
    #[inline]
    #[must_use]
    #[track_caller]
    pub fn expect_block(self, msg: &str) -> BasicBlock<'ctx> {
        match self {
            Self::Block(block) => block,
            _ => panic!("{msg}"),
        }
    }

    /// Unwrap [BasicValueEnum]. Will panic if it is not.
    #[inline]
    #[must_use]
    #[track_caller]
    pub fn unwrap_value(self) -> BasicValueEnum<'ctx> {
        self.expect_value("Called unwrap_value() on UsedValue::Block.")
    }

    /// Unwrap [BasicBlock]. Will panic if it is not.
    #[inline]
    #[must_use]
    #[track_caller]
    pub fn unwrap_block(self) -> BasicBlock<'ctx> {
        self.expect_block("Called unwrap_block() on UsedValue::Value.")
    }
}

/// A usage of a `BasicValue` in another value.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BasicValueUse<'ctx>(LLVMUseRef, PhantomData<&'ctx ()>);

impl<'ctx> BasicValueUse<'ctx> {
    /// Get a value from an [LLVMUseRef].
    ///
    /// # Safety
    ///
    /// The ref must be valid and of type basic value.
    pub unsafe fn new(use_: LLVMUseRef) -> Self {
        debug_assert!(!use_.is_null());

        BasicValueUse(use_, PhantomData)
    }

    /// Gets the next use of a `BasicBlock`, `InstructionValue` or `BasicValue` if any.
    ///
    /// The following example,
    ///
    /// ```no_run
    /// use inkwell::AddressSpace;
    /// use inkwell::context::Context;
    /// use inkwell::values::BasicValue;
    ///
    /// let context = Context::create();
    /// let module = context.create_module("ivs");
    /// let builder = context.create_builder();
    /// let void_type = context.void_type();
    /// let f32_type = context.f32_type();
    /// #[cfg(feature = "typed-pointers")]
    /// let f32_ptr_type = f32_type.ptr_type(AddressSpace::default());
    /// #[cfg(not(feature = "typed-pointers"))]
    /// let f32_ptr_type = context.ptr_type(AddressSpace::default());
    /// let fn_type = void_type.fn_type(&[f32_ptr_type.into()], false);
    ///
    /// let function = module.add_function("take_f32_ptr", fn_type, None);
    /// let basic_block = context.append_basic_block(function, "entry");
    ///
    /// builder.position_at_end(basic_block);
    ///
    /// let arg1 = function.get_first_param().unwrap().into_pointer_value();
    /// let f32_val = f32_type.const_float(std::f64::consts::PI);
    /// let store_instruction = builder.build_store(arg1, f32_val).unwrap();
    /// let free_instruction = builder.build_free(arg1).unwrap();
    /// let return_instruction = builder.build_return(None).unwrap();
    ///
    /// let arg1_first_use = arg1.get_first_use().unwrap();
    ///
    /// assert!(arg1_first_use.get_next_use().is_some());
    /// ```
    ///
    /// will generate LLVM IR roughly like (varying slightly across LLVM versions):
    ///
    /// ```ir
    /// ; ModuleID = 'ivs'
    /// source_filename = "ivs"
    ///
    /// define void @take_f32_ptr(float* %0) {
    /// entry:
    ///   store float 0x400921FB60000000, float* %0
    ///   %1 = bitcast float* %0 to i8*
    ///   tail call void @free(i8* %1)
    ///   ret void
    /// }
    ///
    /// declare void @free(i8*)
    /// ```
    ///
    /// which makes the arg1 (%0) uses clear:
    /// 1) In the store instruction
    /// 2) In the pointer bitcast
    pub fn get_next_use(self) -> Option<Self> {
        let use_ = unsafe { LLVMGetNextUse(self.0) };

        if use_.is_null() {
            return None;
        }

        unsafe { Some(Self::new(use_)) }
    }

    /// Gets the user (an `AnyValueEnum`) of this use.
    ///
    /// ```no_run
    /// use inkwell::AddressSpace;
    /// use inkwell::context::Context;
    /// use inkwell::values::BasicValue;
    ///
    /// let context = Context::create();
    /// let module = context.create_module("ivs");
    /// let builder = context.create_builder();
    /// let void_type = context.void_type();
    /// let f32_type = context.f32_type();
    /// #[cfg(feature = "typed-pointers")]
    /// let f32_ptr_type = f32_type.ptr_type(AddressSpace::default());
    /// #[cfg(not(feature = "typed-pointers"))]
    /// let f32_ptr_type = context.ptr_type(AddressSpace::default());
    /// let fn_type = void_type.fn_type(&[f32_ptr_type.into()], false);
    ///
    /// let function = module.add_function("take_f32_ptr", fn_type, None);
    /// let basic_block = context.append_basic_block(function, "entry");
    ///
    /// builder.position_at_end(basic_block);
    ///
    /// let arg1 = function.get_first_param().unwrap().into_pointer_value();
    /// let f32_val = f32_type.const_float(std::f64::consts::PI);
    /// let store_instruction = builder.build_store(arg1, f32_val).unwrap();
    /// let free_instruction = builder.build_free(arg1).unwrap();
    /// let return_instruction = builder.build_return(None).unwrap();
    ///
    /// let store_operand_use0 = store_instruction.get_operand_use(0).unwrap();
    /// let store_operand_use1 = store_instruction.get_operand_use(1).unwrap();
    ///
    /// assert_eq!(store_operand_use0.get_user(), store_instruction);
    /// assert_eq!(store_operand_use1.get_user(), store_instruction);
    /// ```
    pub fn get_user(self) -> AnyValueEnum<'ctx> {
        unsafe { AnyValueEnum::new(LLVMGetUser(self.0)) }
    }

    /// Gets the used value (a `BasicValueEnum` or `BasicBlock`) of this use.
    ///
    /// ```no_run
    /// use inkwell::AddressSpace;
    /// use inkwell::context::Context;
    /// use inkwell::values::BasicValue;
    ///
    /// let context = Context::create();
    /// let module = context.create_module("ivs");
    /// let builder = context.create_builder();
    /// let void_type = context.void_type();
    /// let f32_type = context.f32_type();
    /// #[cfg(feature = "typed-pointers")]
    /// let f32_ptr_type = f32_type.ptr_type(AddressSpace::default());
    /// #[cfg(not(feature = "typed-pointers"))]
    /// let f32_ptr_type = context.ptr_type(AddressSpace::default());
    /// let fn_type = void_type.fn_type(&[f32_ptr_type.into()], false);
    ///
    /// let function = module.add_function("take_f32_ptr", fn_type, None);
    /// let basic_block = context.append_basic_block(function, "entry");
    ///
    /// builder.position_at_end(basic_block);
    ///
    /// let arg1 = function.get_first_param().unwrap().into_pointer_value();
    /// let f32_val = f32_type.const_float(std::f64::consts::PI);
    /// let store_instruction = builder.build_store(arg1, f32_val).unwrap();
    /// let free_instruction = builder.build_free(arg1).unwrap();
    /// let return_instruction = builder.build_return(None).unwrap();
    ///
    /// let free_operand0 = free_instruction.get_operand(0).unwrap().unwrap_value();
    /// let free_operand0_instruction = free_operand0.as_instruction_value().unwrap();
    /// let bitcast_use_value = free_operand0_instruction
    ///     .get_first_use()
    ///     .unwrap()
    ///     .get_used_value()
    ///     .value()
    ///     .unwrap();
    ///
    /// assert_eq!(bitcast_use_value, free_operand0);
    /// ```
    pub fn get_used_value(self) -> Operand<'ctx> {
        let used_value = unsafe { LLVMGetUsedValue(self.0) };

        let is_basic_block = unsafe { !LLVMIsABasicBlock(used_value).is_null() };

        if is_basic_block {
            let bb = unsafe { BasicBlock::new(LLVMValueAsBasicBlock(used_value)) };

            Operand::Block(bb.expect("BasicBlock should always be valid"))
        } else {
            unsafe { Operand::Value(BasicValueEnum::new(used_value)) }
        }
    }
}