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

Provide get_gep_source_element_type and InstructionValue -> CallSiteValue #506

Merged
merged 11 commits into from
Aug 4, 2024
14 changes: 13 additions & 1 deletion src/values/call_site_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use llvm_sys::LLVMTypeKind;
use crate::attributes::{Attribute, AttributeLoc};
use crate::values::{AsValueRef, BasicValueEnum, FunctionValue, InstructionValue, Value};

use super::AnyValue;
use super::{AnyValue, InstructionOpcode};

/// A value resulting from a function call. It may have function attributes applied to it.
///
Expand Down Expand Up @@ -605,3 +605,15 @@ impl Display for CallSiteValue<'_> {
write!(f, "{}", self.print_to_string())
}
}

impl<'ctx> TryFrom<InstructionValue<'ctx>> for CallSiteValue<'ctx> {
type Error = ();

fn try_from(value: InstructionValue<'ctx>) -> Result<Self, Self::Error> {
if value.get_opcode() == InstructionOpcode::Call {
unsafe { Ok(CallSiteValue::new(value.as_value_ref())) }
} else {
Err(())
}
}
}
19 changes: 17 additions & 2 deletions src/values/instruction_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use either::{
Either,
Either::{Left, Right},
};
#[llvm_versions(14..)]
use llvm_sys::core::LLVMGetGEPSourceElementType;
use llvm_sys::core::{
LLVMGetAlignment, LLVMGetAllocatedType, LLVMGetFCmpPredicate, LLVMGetICmpPredicate, LLVMGetInstructionOpcode,
LLVMGetInstructionParent, LLVMGetMetadata, LLVMGetNextInstruction, LLVMGetNumOperands, LLVMGetOperand,
LLVMGetOperandUse, LLVMGetPreviousInstruction, LLVMGetVolatile, LLVMHasMetadata, LLVMInstructionClone,
LLVMInstructionEraseFromParent, LLVMInstructionRemoveFromParent, LLVMIsAAllocaInst, LLVMIsABasicBlock,
LLVMIsALoadInst, LLVMIsAStoreInst, LLVMIsATerminatorInst, LLVMIsConditional, LLVMIsTailCall, LLVMSetAlignment,
LLVMSetMetadata, LLVMSetOperand, LLVMSetVolatile, LLVMValueAsBasicBlock,
LLVMIsAGetElementPtrInst, LLVMIsALoadInst, LLVMIsAStoreInst, LLVMIsATerminatorInst, LLVMIsConditional,
LLVMIsTailCall, LLVMSetAlignment, LLVMSetMetadata, LLVMSetOperand, LLVMSetVolatile, LLVMValueAsBasicBlock,
};
use llvm_sys::core::{LLVMGetOrdering, LLVMSetOrdering};
#[llvm_versions(10..)]
Expand Down Expand Up @@ -121,6 +123,9 @@ impl<'ctx> InstructionValue<'ctx> {
fn is_a_alloca_inst(self) -> bool {
!unsafe { LLVMIsAAllocaInst(self.as_value_ref()) }.is_null()
}
fn is_a_getelementptr_inst(self) -> bool {
!unsafe { LLVMIsAGetElementPtrInst(self.as_value_ref()) }.is_null()
}
#[llvm_versions(10..)]
fn is_a_atomicrmw_inst(self) -> bool {
!unsafe { LLVMIsAAtomicRMWInst(self.as_value_ref()) }.is_null()
Expand Down Expand Up @@ -398,6 +403,16 @@ impl<'ctx> InstructionValue<'ctx> {
Ok(unsafe { BasicTypeEnum::new(LLVMGetAllocatedType(self.as_value_ref())) })
}

// SubTypes: Only apply to GetElementPtr instruction
/// Returns the source element type of the given GEP.
#[llvm_versions(14..)]
pub fn get_gep_source_element_type(self) -> Result<BasicTypeEnum<'ctx>, &'static str> {
if !self.is_a_getelementptr_inst() {
return Err("Value is not a GEP.");
}
Ok(unsafe { BasicTypeEnum::new(LLVMGetGEPSourceElementType(self.as_value_ref())) })
}

// SubTypes: Only apply to memory access and alloca instructions
/// Returns alignment on a memory access instruction or alloca.
pub fn get_alignment(self) -> Result<u32, &'static str> {
Expand Down
18 changes: 18 additions & 0 deletions src/values/struct_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,24 @@ impl<'ctx> StructValue<'ctx> {
pub fn replace_all_uses_with(self, other: StructValue<'ctx>) {
self.struct_value.replace_all_uses_with(other.as_value_ref())
}

/// Determines whether or not a `StructValue` is a constant.
///
/// # Example
///
/// ```no_run
/// use inkwell::{context::Context, values::BasicValue};
///
/// let context = Context::create();
/// let i64_type = context.i64_type();
/// let i64_val = i64_type.const_int(23, false).as_basic_value_enum();
/// let struct_val = context.const_struct(&[i64_val, i64_val], false);
///
/// assert!(struct_val.is_const());
/// ```
pub fn is_const(self) -> bool {
self.struct_value.is_const()
}
}

unsafe impl AsValueRef for StructValue<'_> {
Expand Down
24 changes: 22 additions & 2 deletions tests/all/test_instruction_values.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use inkwell::context::Context;
use inkwell::types::{AnyTypeEnum, BasicType};
use inkwell::values::{BasicValue, InstructionOpcode::*};
use inkwell::types::{AnyType, AnyTypeEnum, BasicType};
use inkwell::values::{BasicValue, CallSiteValue, InstructionOpcode::*};
use inkwell::{AddressSpace, AtomicOrdering, AtomicRMWBinOp, FloatPredicate, IntPredicate};

#[test]
Expand Down Expand Up @@ -311,6 +311,24 @@ fn test_instructions() {
.build_conditional_branch(i64_type.const_zero(), basic_block, basic_block)
.unwrap();

#[cfg(any(
feature = "llvm15-0",
feature = "llvm16-0",
feature = "llvm17-0",
feature = "llvm18-0"
))]
{
let gep_instr = unsafe { builder.build_gep(i64_type, alloca_val, &vec![], "gep").unwrap() };
assert_eq!(
gep_instr
.as_instruction_value()
.unwrap()
.get_gep_source_element_type()
.unwrap()
.as_any_type_enum(),
i64_type.as_any_type_enum()
);
}
assert_eq!(
alloca_val.as_instruction().unwrap().get_allocated_type(),
Ok(i64_type.as_basic_type_enum())
Expand All @@ -321,6 +339,8 @@ fn test_instructions() {
assert!(!store_instruction.is_conditional());
assert!(!return_instruction.is_conditional());
assert!(cond_br_instruction.is_conditional());
assert!(TryInto::<CallSiteValue>::try_into(free_instruction).is_ok());
assert!(TryInto::<CallSiteValue>::try_into(return_instruction).is_err());
assert_eq!(store_instruction.get_opcode(), Store);
assert_eq!(ptr_val.as_instruction().unwrap().get_opcode(), PtrToInt);
assert_eq!(ptr.as_instruction().unwrap().get_opcode(), IntToPtr);
Expand Down