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;
#[derive(Debug)]
pub struct OperandBundle<'ctx> {
bundle: Cell<LLVMOperandBundleRef>,
_marker: PhantomData<&'ctx Context>,
}
#[derive(Debug)]
pub struct OperandBundleIter<'a, 'ctx> {
instruction: &'a CallSiteValue<'ctx>,
current: u32,
size: u32,
}
#[derive(Debug)]
pub struct OperandBundleArgsIter<'a, 'ctx> {
bundle: &'a OperandBundle<'ctx>,
current: u32,
size: u32,
}
impl<'ctx> OperandBundle<'ctx> {
pub unsafe fn new(bundle: LLVMOperandBundleRef) -> Self {
Self {
bundle: Cell::new(bundle),
_marker: PhantomData,
}
}
pub fn create(tag: &str, args: &[AnyValueEnum<'ctx>]) -> Self {
let c_tag = to_c_str(tag);
let mut args: Vec<LLVMValueRef> = 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)
}
}
pub fn as_mut_ptr(&self) -> LLVMOperandBundleRef {
self.bundle.get()
}
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()
}
}
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<Self::Item> {
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<usize>) {
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<Self::Item> {
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<usize>) {
let remaining = (self.size - self.current) as usize;
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for OperandBundleArgsIter<'_, '_> {}