Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement safe API for operand bundles. #524

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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