diff --git a/src/values/basic_value_use.rs b/src/values/basic_value_use.rs index ce77b521638..41d4a84e7f4 100644 --- a/src/values/basic_value_use.rs +++ b/src/values/basic_value_use.rs @@ -4,15 +4,17 @@ use llvm_sys::prelude::LLVMUseRef; use std::marker::PhantomData; use crate::basic_block::BasicBlock; -use crate::values::{AnyValueEnum, BasicValueEnum}; +use crate::values::{AnyValueEnum, BasicValueEnum, MetadataValue}; -/// Either [BasicValueEnum] or [BasicBlock]. +/// Either [BasicValueEnum], a [BasicBlock] or a [Metadata]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Operand<'ctx> { /// Represents a [BasicValueEnum]. Value(BasicValueEnum<'ctx>), /// Represents a [BasicBlock]. Block(BasicBlock<'ctx>), + /// Represents a [MetadataValue]. + Metadata(MetadataValue<'ctx>), } impl<'ctx> Operand<'ctx> { @@ -30,6 +32,13 @@ impl<'ctx> Operand<'ctx> { matches!(self, Self::Block(_)) } + /// Determines if the [Operand] is a [Metadata]. + #[inline] + #[must_use] + pub fn is_metadata(self) -> bool { + matches!(self, Self::Metadata(_)) + } + /// If the [Operand] is a [BasicValueEnum], map it into [Option::Some]. #[inline] #[must_use] @@ -50,6 +59,16 @@ impl<'ctx> Operand<'ctx> { } } + /// If the [Operand] is a [Metadata], map it into [Option::Some]. + #[inline] + #[must_use] + pub fn metadata(self) -> Option> { + match self { + Self::Metadata(md) => Some(md), + _ => None, + } + } + /// Expect [BasicValueEnum], panic with the message if it is not. #[inline] #[must_use] @@ -72,12 +91,27 @@ impl<'ctx> Operand<'ctx> { } } + /// Expect [Metadata], panic with the message if it is not. + #[inline] + #[must_use] + #[track_caller] + pub fn expect_metadata(self, msg: &str) -> MetadataValue<'ctx> { + match self { + Self::Metadata(md) => md, + _ => panic!("{msg}"), + } + } + /// Unwrap [BasicValueEnum]. Will panic if it is not. #[inline] #[must_use] #[track_caller] pub fn unwrap_value(self) -> BasicValueEnum<'ctx> { - self.expect_value("Called unwrap_value() on UsedValue::Block.") + match self { + Self::Value(value) => value, + Self::Block(_) => panic!("Called unwrap_value() on Operand::Block."), + Self::Metadata(_) => panic!("Called unwrap_value() on Operand::Metadata."), + } } /// Unwrap [BasicBlock]. Will panic if it is not. @@ -85,7 +119,23 @@ impl<'ctx> Operand<'ctx> { #[must_use] #[track_caller] pub fn unwrap_block(self) -> BasicBlock<'ctx> { - self.expect_block("Called unwrap_block() on UsedValue::Value.") + match self { + Self::Value(_) => panic!("Called unwrap_value() on Operand::Value."), + Self::Block(block) => block, + Self::Metadata(_) => panic!("Called unwrap_value() on Operand::Metadata."), + } + } + + /// Unwrap [Metadata]. Will panic if it is not. + #[inline] + #[must_use] + #[track_caller] + pub fn unwrap_metadata(self) -> MetadataValue<'ctx> { + match self { + Self::Value(_) => panic!("Called unwrap_value() on Operand::Value."), + Self::Block(_) => panic!("Called unwrap_value() on Operand::Block."), + Self::Metadata(md) => md, + } } } diff --git a/src/values/enums.rs b/src/values/enums.rs index 4234ca1ee2d..f8fcbcc1b75 100644 --- a/src/values/enums.rs +++ b/src/values/enums.rs @@ -118,7 +118,7 @@ impl<'ctx> AnyValueEnum<'ctx> { AnyValueEnum::InstructionValue(InstructionValue::new(value)) }, LLVMTypeKind::LLVMMetadataTypeKind => panic!("Metadata values are not supported as AnyValue's."), - _ => panic!("The given type is not supported."), + other => panic!("The given type is not supported: {:?}", other), } } @@ -292,7 +292,7 @@ impl<'ctx> BasicValueEnum<'ctx> { LLVMTypeKind::LLVMScalableVectorTypeKind => { BasicValueEnum::ScalableVectorValue(ScalableVectorValue::new(value)) }, - _ => unreachable!("The given type is not a basic type."), + other => panic!("The given type is not a basic type: {:?}", other), } } @@ -461,6 +461,7 @@ impl<'ctx> AggregateValueEnum<'ctx> { impl<'ctx> BasicMetadataValueEnum<'ctx> { pub(crate) unsafe fn new(value: LLVMValueRef) -> Self { + assert!(!value.is_null()); match LLVMGetTypeKind(LLVMTypeOf(value)) { LLVMTypeKind::LLVMFloatTypeKind | LLVMTypeKind::LLVMFP128TypeKind diff --git a/src/values/instruction_value.rs b/src/values/instruction_value.rs index d0a12a334da..0f8d2a6865d 100644 --- a/src/values/instruction_value.rs +++ b/src/values/instruction_value.rs @@ -3,17 +3,18 @@ use llvm_sys::core::LLVMGetGEPSourceElementType; use llvm_sys::core::{ LLVMGetAlignment, LLVMGetAllocatedType, LLVMGetFCmpPredicate, LLVMGetICmpPredicate, LLVMGetIndices, LLVMGetInstructionOpcode, LLVMGetInstructionParent, LLVMGetMetadata, LLVMGetNextInstruction, LLVMGetNumIndices, - LLVMGetNumOperands, LLVMGetOperand, LLVMGetOperandUse, LLVMGetPreviousInstruction, LLVMGetVolatile, - LLVMHasMetadata, LLVMInstructionClone, LLVMInstructionEraseFromParent, LLVMInstructionRemoveFromParent, - LLVMIsAAllocaInst, LLVMIsABasicBlock, LLVMIsAGetElementPtrInst, LLVMIsALoadInst, LLVMIsAStoreInst, - LLVMIsATerminatorInst, LLVMIsConditional, LLVMIsTailCall, LLVMSetAlignment, LLVMSetMetadata, LLVMSetOperand, - LLVMSetVolatile, LLVMValueAsBasicBlock, + LLVMGetNumOperands, LLVMGetOperand, LLVMGetOperandUse, LLVMGetPreviousInstruction, LLVMGetTypeKind, + LLVMGetVolatile, LLVMHasMetadata, LLVMInstructionClone, LLVMInstructionEraseFromParent, + LLVMInstructionRemoveFromParent, LLVMIsAAllocaInst, LLVMIsABasicBlock, LLVMIsAGetElementPtrInst, LLVMIsALoadInst, + LLVMIsAStoreInst, LLVMIsATerminatorInst, LLVMIsConditional, LLVMIsTailCall, LLVMSetAlignment, LLVMSetMetadata, + LLVMSetOperand, LLVMSetVolatile, LLVMTypeOf, LLVMValueAsBasicBlock, }; #[llvm_versions(10..)] use llvm_sys::core::{LLVMGetAtomicRMWBinOp, LLVMIsAAtomicCmpXchgInst, LLVMIsAAtomicRMWInst}; use llvm_sys::core::{LLVMGetOrdering, LLVMSetOrdering}; use llvm_sys::prelude::LLVMValueRef; use llvm_sys::LLVMOpcode; +use llvm_sys::LLVMTypeKind; use std::{ffi::CStr, fmt, fmt::Display}; @@ -684,14 +685,17 @@ impl<'ctx> InstructionValue<'ctx> { } let is_basic_block = unsafe { !LLVMIsABasicBlock(operand).is_null() }; - if is_basic_block { let bb = unsafe { BasicBlock::new(LLVMValueAsBasicBlock(operand)) }; - Some(Operand::Block(bb.expect("BasicBlock should always be valid"))) - } else { - Some(Operand::Value(unsafe { BasicValueEnum::new(operand) })) + return Some(Operand::Block(bb.expect("BasicBlock should always be valid"))); } + + if unsafe { LLVMGetTypeKind(LLVMTypeOf(operand)) == LLVMTypeKind::LLVMMetadataTypeKind } { + return Some(Operand::Metadata(MetadataValue::new(operand))); + } + + Some(Operand::Value(unsafe { BasicValueEnum::new(operand) })) } /// Get an instruction value operand iterator. diff --git a/src/values/metadata_value.rs b/src/values/metadata_value.rs index 48b37c9579e..e490b9c4c26 100644 --- a/src/values/metadata_value.rs +++ b/src/values/metadata_value.rs @@ -101,7 +101,7 @@ impl<'ctx> MetadataValue<'ctx> { // SubTypes: Node only one day // REVIEW: BasicMetadataValueEnum only if you can put metadata in metadata... - pub fn get_node_values(self) -> Vec> { + pub fn get_node_values(self) -> Vec>> { if self.is_string() { return Vec::new(); } @@ -117,7 +117,13 @@ impl<'ctx> MetadataValue<'ctx> { }; vec.iter() - .map(|val| unsafe { BasicMetadataValueEnum::new(*val) }) + .map(|val| { + if val.is_null() { + None + } else { + Some(unsafe { BasicMetadataValueEnum::new(*val) }) + } + }) .collect() } diff --git a/tests/all/test_instruction_values.rs b/tests/all/test_instruction_values.rs index d62d7840ae9..dafabc2fe5e 100644 --- a/tests/all/test_instruction_values.rs +++ b/tests/all/test_instruction_values.rs @@ -1,11 +1,12 @@ use inkwell::context::Context; +use inkwell::debug_info::AsDIScope; #[cfg(not(feature = "typed-pointers"))] use inkwell::types::AnyType; use inkwell::types::{AnyTypeEnum, BasicType}; use inkwell::values::{BasicValue, CallSiteValue, InstructionOpcode::*}; #[llvm_versions(10..)] use inkwell::AtomicRMWBinOp; -use inkwell::{AddressSpace, AtomicOrdering, FloatPredicate, IntPredicate}; +use inkwell::{debug_info, AddressSpace, AtomicOrdering, FloatPredicate, IntPredicate}; #[test] #[ignore] @@ -661,6 +662,65 @@ fn test_metadata_kinds() { ]); } +#[test] +fn test_metadata_as_operand() { + // clang can introduce instructions such as + // call void @llvm.dbg.declare(metadata i32* %2, metadata !16, metadata !DIExpression()), !dbg !17 + // we want to make sure that looking at the operands works. + + let context = Context::create(); + let module = context.create_module("testing"); + + let void_type = context.void_type(); + let i32_type = context.i32_type(); + let fn_type = void_type.fn_type(&[], false); + let function = module.add_function("fun", fn_type, None); + let block = context.append_basic_block(function, "block"); + + let (debug_info_builder, compile_unit) = module.create_debug_info_builder( + true, + debug_info::DWARFSourceLanguage::C, + "test.ll", + "/tmp", + "test", + false, + "", + 0, + "split", + debug_info::DWARFEmissionKind::Full, + 0, + false, + false, + "/", + "18.1", + ); + + let address_space = AddressSpace::default(); + let debug_loc = debug_info_builder.create_debug_location(&context, 5, 32, compile_unit.as_debug_info_scope(), None); + #[allow(deprecated)] + let i32_ptr = i32_type.ptr_type(address_space).const_null(); + let di_i32_type = debug_info_builder.create_basic_type("i32", 32, 0, 0).unwrap(); + let var_info = debug_info_builder.create_auto_variable( + compile_unit.as_debug_info_scope(), + "var", + compile_unit.get_file(), + 42, + di_i32_type.as_type(), + false, + 0, + 32, + ); + let instruction = debug_info_builder.insert_declare_at_end(i32_ptr, Some(var_info), None, debug_loc, block); + assert_eq!(instruction.get_num_operands(), 4); + assert_eq!(instruction.get_operands().count(), 4); + for (i, operand) in instruction.get_operands().enumerate() { + assert!(operand.is_some()); + // A cheap (and certainly insufficient) test that we can walk through + // the operand without segfaulting. + eprintln!("operand {i} is {:?}", operand); + } +} + #[test] fn test_find_instruction_with_name() { use inkwell::context::Context; diff --git a/tests/all/test_values.rs b/tests/all/test_values.rs index 568d852357b..427513f3e3a 100644 --- a/tests/all/test_values.rs +++ b/tests/all/test_values.rs @@ -917,7 +917,11 @@ fn test_metadata() { let md_node_child = context.metadata_node(&[bool_val.into(), f32_val.into()]); let md_node = context.metadata_node(&[bool_val.into(), f32_val.into(), md_string.into(), md_node_child.into()]); - let node_values = md_node.get_node_values(); + let node_values: Vec<_> = md_node + .get_node_values() + .iter() + .map(|v| v.expect("Node value should not have been none")) + .collect(); assert_eq!(md_node.get_string_value(), None); assert_eq!(node_values.len(), 4); @@ -938,7 +942,11 @@ fn test_metadata() { assert_eq!(global_md.len(), 1); - let md = global_md[0].get_node_values(); + let md: Vec<_> = md_node + .get_node_values() + .iter() + .map(|v| v.expect("Node value should not have been none")) + .collect(); assert_eq!(md.len(), 4); assert_eq!(md[0].into_int_value(), bool_val); @@ -992,7 +1000,11 @@ fn test_metadata() { assert!(ret_instr.has_metadata()); assert!(ret_instr.get_metadata(1).is_none()); - let md_node_values = ret_instr.get_metadata(2).unwrap().get_node_values(); + let md_node_values: Vec<_> = md_node + .get_node_values() + .iter() + .map(|v| v.expect("Node value should not have been none")) + .collect(); assert_eq!(md_node_values.len(), 1); assert_eq!(