Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
60 changes: 56 additions & 4 deletions src/values/basic_value_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ 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].
///
/// Note that [Metadata] variants are only constructed for LLVM 17+.
Comment thread
Yoric marked this conversation as resolved.
Outdated
#[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 +34,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 +61,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 +93,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
4 changes: 2 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
48 changes: 39 additions & 9 deletions src/values/instruction_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ 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,
LLVMGetVolatile, LLVMHasMetadata, LLVMInstructionClone, LLVMInstructionEraseFromParent,
LLVMInstructionRemoveFromParent, LLVMIsAAllocaInst, LLVMIsABasicBlock, LLVMIsAGetElementPtrInst, LLVMIsALoadInst,
LLVMIsAStoreInst, LLVMIsATerminatorInst, LLVMIsConditional, LLVMIsTailCall,
LLVMSetAlignment, LLVMSetMetadata, LLVMSetOperand, LLVMSetVolatile, LLVMValueAsBasicBlock,
};
#[llvm_versions(10..)]
use llvm_sys::core::{LLVMGetAtomicRMWBinOp, LLVMIsAAtomicCmpXchgInst, LLVMIsAAtomicRMWInst};
#[llvm_versions(17..)]
use llvm_sys::core::{LLVMGetTypeKind, LLVMIsAValueAsMetadata, LLVMTypeOf};
use llvm_sys::core::{LLVMGetOrdering, LLVMSetOrdering};
use llvm_sys::prelude::LLVMValueRef;
use llvm_sys::LLVMOpcode;
#[llvm_versions(17..)]
use llvm_sys::LLVMTypeKind;

use std::{ffi::CStr, fmt, fmt::Display};

Expand Down Expand Up @@ -684,16 +688,42 @@ 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")));
}

match self.try_ingest_metadata(operand) {
// This is indeed metadata.
Ok(Some(metadata)) => return Some(Operand::Metadata(metadata)),
// This is null metadata, which we can't convert to an operand.
Err(_) => return None,
Comment thread
Yoric marked this conversation as resolved.
Outdated
// Not metadata at all.
Ok(None) => {}
}

Some(Operand::Value(unsafe { BasicValueEnum::new(operand) }))
}

#[llvm_versions(17..)] // LLVMIsAValueAsMetadata was introduced un 17.0.
unsafe fn try_ingest_metadata(self, operand: LLVMValueRef) -> Result<Option<MetadataValue<'ctx>>, ()> {
Comment thread
Yoric marked this conversation as resolved.
Outdated
assert!(!operand.is_null());
if unsafe { LLVMGetTypeKind(LLVMTypeOf(operand)) == LLVMTypeKind::LLVMMetadataTypeKind } {
// We may be dealing with null metadata, which would break memory invariants.
if LLVMIsAValueAsMetadata(operand).is_null() {
return Err(());
}
return Ok(Some(MetadataValue::new(operand)));
}
Ok(None)
}
#[llvm_versions(..17)] // LLVMIsAValueAsMetadata was introduced un 17.0.
unsafe fn try_ingest_metadata(self, _: LLVMValueRef) -> Result<Option<MetadataValue<'ctx>>, ()> {
Ok(None)
}


/// Get an instruction value operand iterator.
pub fn get_operands(self) -> OperandIter<'ctx> {
OperandIter {
Expand Down
60 changes: 59 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,63 @@ fn test_metadata_kinds() {
]);
}

#[llvm_versions(17..)]
#[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!(instruction.get_operand(0).is_some());
assert!(instruction.get_operand(1).is_none());
assert!(instruction.get_operand(2).is_none());
assert!(instruction.get_operand(3).is_some());
}

#[test]
fn test_find_instruction_with_name() {
use inkwell::context::Context;
Expand Down