diff --git a/src/builder.rs b/src/builder.rs index 084a3d03e2..accb513fe0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,5 +1,7 @@ //! A `Builder` enables you to build instructions. +#[llvm_versions(18..)] +use llvm_sys::core::LLVMBuildCallWithOperandBundles; use llvm_sys::core::{ LLVMAddCase, LLVMAddClause, LLVMAddDestination, LLVMBuildAShr, LLVMBuildAdd, LLVMBuildAddrSpaceCast, LLVMBuildAggregateRet, LLVMBuildAlloca, LLVMBuildAnd, LLVMBuildArrayAlloca, LLVMBuildArrayMalloc, @@ -42,6 +44,8 @@ use crate::context::AsContextRef; use crate::debug_info::DILocation; use crate::support::to_c_str; use crate::types::{AsTypeRef, BasicType, FloatMathType, FunctionType, IntMathType, PointerMathType, PointerType}; +#[llvm_versions(18..)] +use crate::values::operand_bundle::OperandBundle; #[llvm_versions(..=14)] use crate::values::CallableValue; use crate::values::{ @@ -308,6 +312,57 @@ impl<'ctx> Builder<'ctx> { self.build_call_help(function.get_type(), function.as_value_ref(), args, name) } + /// Build a function call instruction, with attached operand bundles. + /// + /// # Example + /// + /// ``` + /// use inkwell::context::Context; + /// use inkwell::values::OperandBundle; + /// + /// let context = Context::create(); + /// let module = context.create_module("call_with_op_bundles"); + /// let builder = context.create_builder(); + /// let i32_type = context.i32_type(); + /// + /// // declare i32 @func(i32) + /// let fn_type = i32_type.fn_type(&[i32_type.into()], false); + /// let fn_value = module.add_function("func", fn_type, None); + /// + /// let basic_block = context.append_basic_block(fn_value, "entry"); + /// builder.position_at_end(basic_block); + /// + /// // %func_ret = call i32 @func(i32 0) [ "tag"(i32 0) ] + /// let ret_val = builder.build_direct_call_with_operand_bundles( + /// fn_value, + /// &[i32_type.const_zero().into()], + /// &[OperandBundle::create("tag", &[i32_type.const_zero().into()])], + /// "func_ret" + /// ) + /// .unwrap() + /// .try_as_basic_value() + /// .unwrap_left(); + /// builder.build_return(Some(&ret_val)).unwrap(); + /// + /// # module.verify().unwrap(); + /// ``` + #[llvm_versions(18..)] + pub fn build_direct_call_with_operand_bundles( + &self, + function: FunctionValue<'ctx>, + args: &[BasicMetadataValueEnum<'ctx>], + operand_bundles: &[OperandBundle<'ctx>], + name: &str, + ) -> Result, BuilderError> { + self.build_call_with_operand_bundles_help( + function.get_type(), + function.as_value_ref(), + args, + operand_bundles, + name, + ) + } + /// Call a function pointer. Because a pointer does not carry a type, the type of the function /// must be specified explicitly. /// @@ -352,6 +407,28 @@ impl<'ctx> Builder<'ctx> { self.build_call_help(function_type, function_pointer.as_value_ref(), args, name) } + /// Build a call instruction to a function pointer, with attached operand bundles. + /// + /// See [Builder::build_direct_call_with_operand_bundles] for a usage example + /// with operand bundles. + #[llvm_versions(18..)] + pub fn build_indirect_call_with_operand_bundles( + &self, + function_type: FunctionType<'ctx>, + function_pointer: PointerValue<'ctx>, + args: &[BasicMetadataValueEnum<'ctx>], + operand_bundles: &[OperandBundle<'ctx>], + name: &str, + ) -> Result, BuilderError> { + self.build_call_with_operand_bundles_help( + function_type, + function_pointer.as_value_ref(), + args, + operand_bundles, + name, + ) + } + #[llvm_versions(15..)] fn build_call_help( &self, @@ -388,6 +465,49 @@ impl<'ctx> Builder<'ctx> { unsafe { Ok(CallSiteValue::new(value)) } } + #[llvm_versions(18..)] + fn build_call_with_operand_bundles_help( + &self, + function_type: FunctionType<'ctx>, + fn_val_ref: LLVMValueRef, + args: &[BasicMetadataValueEnum<'ctx>], + operand_bundles: &[OperandBundle<'ctx>], + name: &str, + ) -> Result, BuilderError> { + use llvm_sys::prelude::LLVMOperandBundleRef; + + if self.positioned.get() != PositionState::Set { + return Err(BuilderError::UnsetPosition); + } + // LLVM gets upset when void return calls are named because they don't return anything + let name = match function_type.get_return_type() { + None => "", + Some(_) => name, + }; + + let fn_ty_ref = function_type.as_type_ref(); + + let c_string = to_c_str(name); + let mut args: Vec = args.iter().map(|val| val.as_value_ref()).collect(); + let mut operand_bundles: Vec = + operand_bundles.iter().map(|val| val.as_mut_ptr()).collect(); + + let value = unsafe { + LLVMBuildCallWithOperandBundles( + self.builder, + fn_ty_ref, + fn_val_ref, + args.as_mut_ptr(), + args.len() as u32, + operand_bundles.as_mut_ptr(), + operand_bundles.len() as u32, + c_string.as_ptr(), + ) + }; + + unsafe { Ok(CallSiteValue::new(value)) } + } + /// An invoke is similar to a normal function call, but used to /// call functions that may throw an exception, and then respond to the exception. /// diff --git a/src/values/call_site_value.rs b/src/values/call_site_value.rs index 07602f8d36..7d307ef34e 100644 --- a/src/values/call_site_value.rs +++ b/src/values/call_site_value.rs @@ -11,6 +11,8 @@ use llvm_sys::prelude::LLVMValueRef; use llvm_sys::LLVMTypeKind; use crate::attributes::{Attribute, AttributeLoc}; +#[llvm_versions(18..)] +use crate::values::operand_bundle::OperandBundleIter; use crate::values::{AsValueRef, BasicValueEnum, FunctionValue, InstructionValue, Value}; use super::{AnyValue, InstructionOpcode}; @@ -592,6 +594,47 @@ impl<'ctx> CallSiteValue<'ctx> { unsafe { LLVMSetInstrParamAlignment(self.as_value_ref(), loc.get_index(), alignment) } } + + /// Iterate over operand bundles. + /// + /// # Example + /// + /// ``` + /// use inkwell::context::Context; + /// use inkwell::values::OperandBundle; + /// + /// let context = Context::create(); + /// let module = context.create_module("op_bundles"); + /// let builder = context.create_builder(); + /// + /// let void_type = context.void_type(); + /// let i32_type = context.i32_type(); + /// let fn_type = void_type.fn_type(&[], false); + /// let fn_value = module.add_function("func", fn_type, None); + /// + /// let basic_block = context.append_basic_block(fn_value, "entry"); + /// builder.position_at_end(basic_block); + /// + /// // Recursive call + /// let callinst = builder.build_direct_call_with_operand_bundles( + /// fn_value, + /// &[], + /// &[OperandBundle::create("tag0", &[i32_type.const_zero().into()]), OperandBundle::create("tag1", &[])], + /// "call" + /// ).unwrap(); + /// + /// builder.build_return(None).unwrap(); + /// # module.verify().unwrap(); + /// + /// let mut op_bundles_iter = callinst.get_operand_bundles(); + /// assert_eq!(op_bundles_iter.len(), 2); + /// let tags: Vec = op_bundles_iter.map(|ob| ob.get_tag().unwrap().into()).collect(); + /// assert_eq!(tags, vec!["tag0", "tag1"]); + /// ``` + #[llvm_versions(18..)] + pub fn get_operand_bundles(&self) -> OperandBundleIter<'_, 'ctx> { + OperandBundleIter::new(self) + } } unsafe impl AsValueRef for CallSiteValue<'_> { diff --git a/src/values/mod.rs b/src/values/mod.rs index 953b4449c3..7a90c205c3 100644 --- a/src/values/mod.rs +++ b/src/values/mod.rs @@ -20,6 +20,9 @@ mod struct_value; mod traits; mod vec_value; +#[cfg(feature = "llvm18-0")] +pub(crate) mod operand_bundle; + #[cfg(not(any( feature = "llvm15-0", feature = "llvm16-0", @@ -36,6 +39,9 @@ mod callable_value; )))] pub use crate::values::callable_value::CallableValue; +#[llvm_versions(18..)] +pub use crate::values::operand_bundle::OperandBundle; + use crate::support::{to_c_str, LLVMString}; pub use crate::values::array_value::ArrayValue; pub use crate::values::basic_value_use::BasicValueUse; diff --git a/src/values/operand_bundle.rs b/src/values/operand_bundle.rs new file mode 100644 index 0000000000..d573b744c2 --- /dev/null +++ b/src/values/operand_bundle.rs @@ -0,0 +1,197 @@ +use crate::context::Context; +use crate::support::to_c_str; +use crate::values::{AnyValueEnum, AsValueRef, BasicValueEnum, CallSiteValue}; +use llvm_sys::core::{ + LLVMCreateOperandBundle, LLVMDisposeOperandBundle, LLVMGetNumOperandBundleArgs, LLVMGetNumOperandBundles, + LLVMGetOperandBundleArgAtIndex, LLVMGetOperandBundleAtIndex, LLVMGetOperandBundleTag, +}; +use llvm_sys::prelude::{LLVMOperandBundleRef, LLVMValueRef}; +use std::cell::Cell; +use std::ffi::CStr; +use std::marker::PhantomData; + +/// One of an instruction's operand bundles. +#[derive(Debug)] +pub struct OperandBundle<'ctx> { + bundle: Cell, + _marker: PhantomData<&'ctx Context>, +} + +/// Iterator over an instruction's operand bundles. +#[derive(Debug)] +pub struct OperandBundleIter<'a, 'ctx> { + instruction: &'a CallSiteValue<'ctx>, + current: u32, + size: u32, +} + +/// Iterator over an operand bundle's arguments. +#[derive(Debug)] +pub struct OperandBundleArgsIter<'a, 'ctx> { + bundle: &'a OperandBundle<'ctx>, + current: u32, + size: u32, +} + +impl<'ctx> OperandBundle<'ctx> { + /// Get an operand bundle from a [LLVMOperandBundleRef]. + /// + /// # Safety + /// + /// The ref must be valid and represent an operand bundle. + pub unsafe fn new(bundle: LLVMOperandBundleRef) -> Self { + Self { + bundle: Cell::new(bundle), + _marker: PhantomData, + } + } + + /// Create a new operand bundle. + /// + /// # Example + /// + /// ``` + /// use inkwell::context::Context; + /// use inkwell::values::OperandBundle; + /// + /// let context = Context::create(); + /// let i32_type = context.i32_type(); + /// + /// let op_bundle = OperandBundle::create("tag", &[i32_type.const_zero().into()]); + /// + /// assert_eq!(op_bundle.get_tag().unwrap(), "tag"); + /// let arg = op_bundle.get_args().nth(0).unwrap().into_int_value(); + /// assert!(arg.is_const()); + /// assert_eq!(arg.get_zero_extended_constant().unwrap(), 0); + /// ``` + pub fn create(tag: &str, args: &[AnyValueEnum<'ctx>]) -> Self { + let c_tag = to_c_str(tag); + let mut args: Vec = args.iter().map(|value| value.as_value_ref()).collect(); + + unsafe { + let bundle = LLVMCreateOperandBundle(c_tag.as_ptr(), tag.len(), args.as_mut_ptr(), args.len() as u32); + Self::new(bundle) + } + } + + /// Acquire the underlying raw pointer belonging to this `OperandBundle` type. + pub fn as_mut_ptr(&self) -> LLVMOperandBundleRef { + self.bundle.get() + } + + /// Get this operand bundle's tag. + /// + /// # Example + /// + /// ``` + /// use inkwell::values::OperandBundle; + /// + /// let op_bundle = OperandBundle::create("tag", &[]); + /// assert_eq!(op_bundle.get_tag().unwrap(), "tag"); + /// ``` + pub fn get_tag(&self) -> Result<&str, std::str::Utf8Error> { + unsafe { + let mut size = 0usize; + let tag = LLVMGetOperandBundleTag(self.bundle.get(), &mut size as *mut usize); + CStr::from_ptr(tag).to_str() + } + } + + /// Iterate over this operand bundle's arguments. + /// + /// # Example + /// + /// ``` + /// use inkwell::context::Context; + /// use inkwell::values::OperandBundle; + /// + /// let context = Context::create(); + /// let i32_type = context.i32_type(); + /// let f32_type = context.f32_type(); + /// + /// let op_bundle = OperandBundle::create("tag", &[i32_type.const_zero().into(), f32_type.const_float(1.23).into()]); + /// assert_eq!(op_bundle.get_args().count(), 2); + /// assert_eq!(op_bundle.get_args().len(), 2); + /// ``` + pub fn get_args(&self) -> OperandBundleArgsIter<'_, 'ctx> { + OperandBundleArgsIter::new(self) + } +} + +impl Drop for OperandBundle<'_> { + fn drop(&mut self) { + unsafe { LLVMDisposeOperandBundle(self.as_mut_ptr()) } + } +} + +impl<'a, 'ctx> OperandBundleIter<'a, 'ctx> { + pub(crate) fn new(instruction: &'a CallSiteValue<'ctx>) -> Self { + let size = unsafe { LLVMGetNumOperandBundles(instruction.as_value_ref()) }; + + Self { + instruction, + current: 0, + size, + } + } +} + +impl<'ctx> Iterator for OperandBundleIter<'_, 'ctx> { + type Item = OperandBundle<'ctx>; + + fn next(&mut self) -> Option { + if self.current < self.size { + let bundle = unsafe { + OperandBundle::new(LLVMGetOperandBundleAtIndex( + self.instruction.as_value_ref(), + self.current, + )) + }; + self.current += 1; + Some(bundle) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.size - self.current) as usize; + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for OperandBundleIter<'_, '_> {} + +impl<'a, 'ctx> OperandBundleArgsIter<'a, 'ctx> { + fn new(bundle: &'a OperandBundle<'ctx>) -> Self { + let size = unsafe { LLVMGetNumOperandBundleArgs(bundle.as_mut_ptr()) }; + Self { + bundle, + current: 0, + size, + } + } +} + +impl<'ctx> Iterator for OperandBundleArgsIter<'_, 'ctx> { + type Item = BasicValueEnum<'ctx>; + + fn next(&mut self) -> Option { + if self.current < self.size { + unsafe { + let arg = LLVMGetOperandBundleArgAtIndex(self.bundle.as_mut_ptr(), self.current); + self.current += 1; + Some(BasicValueEnum::new(arg)) + } + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.size - self.current) as usize; + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for OperandBundleArgsIter<'_, '_> {} diff --git a/tests/all/test_values.rs b/tests/all/test_values.rs index ccf723676e..3910360e63 100644 --- a/tests/all/test_values.rs +++ b/tests/all/test_values.rs @@ -4,6 +4,8 @@ use inkwell::comdat::ComdatSelectionKind; use inkwell::context::Context; use inkwell::module::Linkage::*; use inkwell::types::{AnyTypeEnum, StringRadix, VectorType}; +#[llvm_versions(18..)] +use inkwell::values::OperandBundle; use inkwell::values::{AnyValue, InstructionOpcode::*, FIRST_CUSTOM_METADATA_KIND_ID}; use inkwell::{AddressSpace, DLLStorageClass, GlobalVisibility, ThreadLocalMode}; @@ -98,6 +100,53 @@ fn test_call_site_tail_call_attributes() { assert!(call_site.is_tail_call()); } +#[llvm_versions(18..)] +#[test] +fn test_call_site_operand_bundles() { + let context = Context::create(); + let module = context.create_module("my_mod"); + let builder = context.create_builder(); + + let void_type = context.void_type(); + let i32_type = context.i32_type(); + let fn_type = void_type.fn_type(&[], false); + let fn_value = module.add_function("my_fn", fn_type, None); + let entry_bb = context.append_basic_block(fn_value, "entry"); + + builder.position_at_end(entry_bb); + let call_site = builder + .build_direct_call_with_operand_bundles( + fn_value, + &[], + &[ + OperandBundle::create("tag0", &[i32_type.const_zero().into(), i32_type.const_zero().into()]), + OperandBundle::create("tag1", &[]), + ], + "call", + ) + .unwrap(); + builder.build_return(None).unwrap(); + + assert!(module.verify().is_ok()); + + let mut op_bundle_iter = call_site.get_operand_bundles(); + assert_eq!(op_bundle_iter.len(), 2); + + let op_bundle0 = op_bundle_iter.next().unwrap(); + let op_bundle1 = op_bundle_iter.next().unwrap(); + assert!(op_bundle_iter.next().is_none()); + + assert_eq!(op_bundle0.get_tag().unwrap(), "tag0"); + assert_eq!(op_bundle1.get_tag().unwrap(), "tag1"); + + assert_eq!(op_bundle1.get_args().len(), 0); + assert!(op_bundle1.get_args().next().is_none()); + + let args_iter = op_bundle0.get_args(); + assert_eq!(args_iter.len(), 2); + args_iter.for_each(|arg| assert!(arg.into_int_value().is_const())); +} + #[test] fn test_set_get_name() { let context = Context::create();