Skip to content

Commit

Permalink
Implement safe API for operand bundles. (#524)
Browse files Browse the repository at this point in the history
Co-authored-by: Dan Kolsoi <[email protected]>
  • Loading branch information
airwoodix and TheDan64 authored Sep 19, 2024
1 parent 885c5f7 commit 22c0477
Show file tree
Hide file tree
Showing 5 changed files with 415 additions and 0 deletions.
120 changes: 120 additions & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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::{
Expand Down Expand Up @@ -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<CallSiteValue<'ctx>, 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.
///
Expand Down Expand Up @@ -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<CallSiteValue<'ctx>, 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,
Expand Down Expand Up @@ -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<CallSiteValue<'ctx>, 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<LLVMValueRef> = args.iter().map(|val| val.as_value_ref()).collect();
let mut operand_bundles: Vec<LLVMOperandBundleRef> =
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.
///
Expand Down
43 changes: 43 additions & 0 deletions src/values/call_site_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<String> = 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<'_> {
Expand Down
6 changes: 6 additions & 0 deletions src/values/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 22c0477

Please sign in to comment.