Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 54 additions & 4 deletions src/values/basic_value_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>),
Comment thread
Yoric marked this conversation as resolved.
}

impl<'ctx> Operand<'ctx> {
Expand All @@ -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]
Expand All @@ -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<MetadataValue<'ctx>> {
match self {
Self::Metadata(md) => Some(md),
_ => None,
}
}

/// Expect [BasicValueEnum], panic with the message if it is not.
#[inline]
#[must_use]
Expand All @@ -72,20 +91,51 @@ 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.
#[inline]
#[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,
}
Comment thread
Yoric marked this conversation as resolved.
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/values/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}

Expand Down Expand Up @@ -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),
}
}

Expand Down Expand Up @@ -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
Expand Down
22 changes: 13 additions & 9 deletions src/values/instruction_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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.
Expand Down
10 changes: 8 additions & 2 deletions src/values/metadata_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BasicMetadataValueEnum<'ctx>> {
pub fn get_node_values(self) -> Vec<Option<BasicMetadataValueEnum<'ctx>>> {
if self.is_string() {
return Vec::new();
}
Expand All @@ -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()
}

Expand Down
62 changes: 61 additions & 1 deletion tests/all/test_instruction_values.rs
Original file line number Diff line number Diff line change
@@ -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]
Expand Down Expand Up @@ -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;
Expand Down
18 changes: 15 additions & 3 deletions tests/all/test_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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!(
Expand Down
Loading