From a8361b1e47a7a4b3431b483044d195ce50cc0a67 Mon Sep 17 00:00:00 2001 From: Vaivaswatha N Date: Sun, 4 Aug 2024 08:58:12 +0530 Subject: [PATCH] Provide get_gep_source_element_type and InstructionValue -> CallSiteValue (#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 baaa9822b9f9e13a60134a32351f1cade2793ba1. * Similar to other value types, provide `is_const` method for StructValue * build_gep is only available in LLVM 15+ --------- Co-authored-by: Dan Kolsoi --- src/values/call_site_value.rs | 14 +++++++++++++- src/values/instruction_value.rs | 19 +++++++++++++++++-- src/values/struct_value.rs | 18 ++++++++++++++++++ tests/all/test_instruction_values.rs | 24 ++++++++++++++++++++++-- 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/src/values/call_site_value.rs b/src/values/call_site_value.rs index 833866ce01a..07602f8d369 100644 --- a/src/values/call_site_value.rs +++ b/src/values/call_site_value.rs @@ -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. /// @@ -605,3 +605,15 @@ impl Display for CallSiteValue<'_> { write!(f, "{}", self.print_to_string()) } } + +impl<'ctx> TryFrom> for CallSiteValue<'ctx> { + type Error = (); + + fn try_from(value: InstructionValue<'ctx>) -> Result { + if value.get_opcode() == InstructionOpcode::Call { + unsafe { Ok(CallSiteValue::new(value.as_value_ref())) } + } else { + Err(()) + } + } +} diff --git a/src/values/instruction_value.rs b/src/values/instruction_value.rs index 3a654261fb7..1de4cc206ea 100644 --- a/src/values/instruction_value.rs +++ b/src/values/instruction_value.rs @@ -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..)] @@ -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() @@ -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, &'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 { diff --git a/src/values/struct_value.rs b/src/values/struct_value.rs index 57892b2673a..74a81ebe568 100644 --- a/src/values/struct_value.rs +++ b/src/values/struct_value.rs @@ -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<'_> { diff --git a/tests/all/test_instruction_values.rs b/tests/all/test_instruction_values.rs index 539bbc1396d..c68c2d79c05 100644 --- a/tests/all/test_instruction_values.rs +++ b/tests/all/test_instruction_values.rs @@ -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] @@ -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()) @@ -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::::try_into(free_instruction).is_ok()); + assert!(TryInto::::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);