Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
56 changes: 53 additions & 3 deletions src/values/basic_value_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand All @@ -13,6 +13,8 @@ pub enum Operand<'ctx> {
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 UsedValue::Block."),
Self::Metadata(_) => panic!("Called unwrap_value() on UsedValue::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 UsedValue::Value."),
Self::Block(block) => block,
Self::Metadata(_) => panic!("Called unwrap_value() on UsedValue::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 UsedValue::Value."),
Self::Block(_) => panic!("Called unwrap_value() on UsedValue::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
27 changes: 17 additions & 10 deletions src/values/instruction_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ 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, LLVMIsAValueAsMetadata, 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::{LLVMOpcode, LLVMTypeKind};

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

Expand Down Expand Up @@ -684,14 +684,21 @@ 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 } {
// We may be dealing with null metadata, which would break memory invariants.
if LLVMIsAValueAsMetadata(operand).is_null() {
return None;
}
return Some(Operand::Metadata(MetadataValue::new(operand)));
}

Some(Operand::Value(unsafe { BasicValueEnum::new(operand) }))
Comment thread
Yoric marked this conversation as resolved.
}

/// Get an instruction value operand iterator.
Expand Down
59 changes: 58 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,62 @@ 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!(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
Loading