Skip to content

Commit

Permalink
Provide get_gep_source_element_type and InstructionValue -> CallSiteV…
Browse files Browse the repository at this point in the history
…alue (#506)

* Provide get_gep_source_element_type and InstructionValue -> CallSiteValue

* restrict new api to llvm 14+

* restrict test to llvm versions

* add llvm14 also for test

* temporarily remove callsite test

* Revert "temporarily remove callsite test"

This reverts commit baaa982.

* Similar to other value types, provide `is_const`  method for StructValue

* build_gep is only available in LLVM 15+

---------

Co-authored-by: Dan Kolsoi <[email protected]>
  • Loading branch information
vaivaswatha and TheDan64 authored Aug 4, 2024
1 parent 89e06af commit a8361b1
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 5 deletions.
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

0 comments on commit a8361b1

Please sign in to comment.