use std::{marker::PhantomData, slice};
use gimli::DwTag;
use llvm_sys::{
core::{LLVMGetNumOperands, LLVMGetOperand, LLVMReplaceMDNodeOperandWith, LLVMValueAsMetadata},
debuginfo::{
LLVMDIFileGetFilename, LLVMDIFlags, LLVMDIScopeGetFile, LLVMDISubprogramGetLine,
LLVMDITypeGetFlags, LLVMDITypeGetLine, LLVMDITypeGetName, LLVMDITypeGetOffsetInBits,
LLVMGetDINodeTag,
},
prelude::{LLVMMetadataRef, LLVMValueRef},
};
use crate::llvm::{
LLVMContext, LLVMGetMDString,
types::ir::{MDNode, Metadata},
};
fn mdstring<'a>(mdstring: LLVMValueRef) -> &'a [u8] {
let mut len = 0;
let ptr = unsafe { LLVMGetMDString(mdstring, &mut len) };
unsafe { slice::from_raw_parts(ptr.cast(), len as usize) }
}
unsafe fn di_node_tag(metadata_ref: LLVMMetadataRef) -> DwTag {
DwTag(unsafe { LLVMGetDINodeTag(metadata_ref) })
}
pub(crate) struct DIFile<'ctx> {
pub(super) metadata_ref: LLVMMetadataRef,
_marker: PhantomData<&'ctx ()>,
}
impl DIFile<'_> {
pub(crate) unsafe fn from_metadata_ref(metadata_ref: LLVMMetadataRef) -> Self {
Self {
metadata_ref,
_marker: PhantomData,
}
}
pub(crate) fn filename(&self) -> Option<&[u8]> {
let mut len = 0;
let ptr = unsafe { LLVMDIFileGetFilename(self.metadata_ref, &mut len) };
(!ptr.is_null()).then(|| unsafe { slice::from_raw_parts(ptr.cast(), len as usize) })
}
}
#[repr(u32)]
enum DITypeOperand {
Name = 2,
}
unsafe fn di_type_name<'a>(metadata_ref: LLVMMetadataRef) -> Option<&'a [u8]> {
let mut len = 0;
let ptr = unsafe { LLVMDITypeGetName(metadata_ref, &mut len) };
(!ptr.is_null()).then(|| unsafe { slice::from_raw_parts(ptr.cast(), len) })
}
pub(crate) struct DIType<'ctx> {
pub(super) metadata_ref: LLVMMetadataRef,
pub(super) value_ref: LLVMValueRef,
_marker: PhantomData<&'ctx ()>,
}
impl DIType<'_> {
pub(crate) unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self {
let metadata_ref = unsafe { LLVMValueAsMetadata(value_ref) };
Self {
metadata_ref,
value_ref,
_marker: PhantomData,
}
}
pub(crate) fn offset_in_bits(&self) -> u64 {
unsafe { LLVMDITypeGetOffsetInBits(self.metadata_ref) }
}
}
impl<'ctx> From<DIDerivedType<'ctx>> for DIType<'ctx> {
fn from(di_derived_type: DIDerivedType<'_>) -> Self {
unsafe { Self::from_value_ref(di_derived_type.value_ref) }
}
}
pub(crate) struct DIDerivedType<'ctx> {
metadata_ref: LLVMMetadataRef,
value_ref: LLVMValueRef,
_marker: PhantomData<&'ctx ()>,
}
impl DIDerivedType<'_> {
pub(crate) unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self {
let metadata_ref = unsafe { LLVMValueAsMetadata(value_ref) };
Self {
metadata_ref,
value_ref,
_marker: PhantomData,
}
}
pub(crate) fn replace_name(&mut self, context: &LLVMContext, name: &[u8]) {
super::ir::replace_name(
self.value_ref,
context.as_mut_ptr(),
DITypeOperand::Name as u32,
name,
)
}
pub(crate) fn tag(&self) -> DwTag {
unsafe { di_node_tag(self.metadata_ref) }
}
}
#[repr(u32)]
enum DICompositeTypeOperand {
#[cfg(feature = "llvm-20")]
Elements = 4,
#[cfg(not(feature = "llvm-20"))]
Elements = 6,
}
pub(crate) struct DICompositeType<'ctx> {
metadata_ref: LLVMMetadataRef,
value_ref: LLVMValueRef,
_marker: PhantomData<&'ctx ()>,
}
impl DICompositeType<'_> {
pub(crate) unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self {
let metadata_ref = unsafe { LLVMValueAsMetadata(value_ref) };
Self {
metadata_ref,
value_ref,
_marker: PhantomData,
}
}
pub(crate) fn elements(&self) -> impl Iterator<Item = Metadata<'_>> {
let elements =
unsafe { LLVMGetOperand(self.value_ref, DICompositeTypeOperand::Elements as u32) };
let operands = if elements.is_null() {
0
} else {
unsafe { LLVMGetNumOperands(elements) }
};
(0..operands).map(move |i| unsafe {
Metadata::from_value_ref(LLVMGetOperand(elements, i.cast_unsigned()))
})
}
pub(crate) fn name(&self) -> Option<&[u8]> {
unsafe { di_type_name(self.metadata_ref) }
}
pub(crate) fn file(&self) -> DIFile<'_> {
unsafe {
let metadata = LLVMDIScopeGetFile(self.metadata_ref);
DIFile::from_metadata_ref(metadata)
}
}
pub(crate) fn flags(&self) -> LLVMDIFlags {
unsafe { LLVMDITypeGetFlags(self.metadata_ref) }
}
pub(crate) fn line(&self) -> u32 {
unsafe { LLVMDITypeGetLine(self.metadata_ref) }
}
pub(crate) fn replace_elements(&mut self, mdnode: MDNode<'_>) {
unsafe {
LLVMReplaceMDNodeOperandWith(
self.value_ref,
DICompositeTypeOperand::Elements as u32,
LLVMValueAsMetadata(mdnode.value_ref),
)
}
}
pub(crate) fn replace_name(&mut self, context: &LLVMContext, name: &[u8]) {
super::ir::replace_name(
self.value_ref,
context.as_mut_ptr(),
DITypeOperand::Name as u32,
name,
)
}
pub(crate) fn tag(&self) -> DwTag {
unsafe { di_node_tag(self.metadata_ref) }
}
}
#[repr(u32)]
enum DISubprogramOperand {
Scope = 1,
Name = 2,
LinkageName = 3,
Ty = 4,
Unit = 5,
RetainedNodes = 7,
}
pub(crate) struct DISubprogram<'ctx> {
pub value_ref: LLVMValueRef,
_marker: PhantomData<&'ctx ()>,
}
impl DISubprogram<'_> {
pub(crate) unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self {
DISubprogram {
value_ref,
_marker: PhantomData,
}
}
pub(crate) fn name(&self) -> Option<&[u8]> {
let operand = unsafe { LLVMGetOperand(self.value_ref, DISubprogramOperand::Name as u32) };
(!operand.is_null()).then(|| mdstring(operand))
}
pub(crate) fn linkage_name(&self) -> Option<&[u8]> {
let operand =
unsafe { LLVMGetOperand(self.value_ref, DISubprogramOperand::LinkageName as u32) };
(!operand.is_null()).then(|| mdstring(operand))
}
pub(crate) fn ty(&self) -> LLVMMetadataRef {
unsafe {
LLVMValueAsMetadata(LLVMGetOperand(
self.value_ref,
DISubprogramOperand::Ty as u32,
))
}
}
pub(crate) fn file(&self) -> LLVMMetadataRef {
unsafe { LLVMDIScopeGetFile(LLVMValueAsMetadata(self.value_ref)) }
}
pub(crate) fn line(&self) -> u32 {
unsafe { LLVMDISubprogramGetLine(LLVMValueAsMetadata(self.value_ref)) }
}
pub(crate) fn type_flags(&self) -> i32 {
unsafe { LLVMDITypeGetFlags(LLVMValueAsMetadata(self.value_ref)) }
}
pub(crate) fn replace_name(&mut self, context: &LLVMContext, name: &[u8]) {
super::ir::replace_name(
self.value_ref,
context.as_mut_ptr(),
DISubprogramOperand::Name as u32,
name,
)
}
pub(crate) fn scope(&self) -> Option<LLVMMetadataRef> {
unsafe {
let operand = LLVMGetOperand(self.value_ref, DISubprogramOperand::Scope as u32);
(!operand.is_null()).then(|| LLVMValueAsMetadata(operand))
}
}
pub(crate) fn unit(&self) -> Option<LLVMMetadataRef> {
unsafe {
let operand = LLVMGetOperand(self.value_ref, DISubprogramOperand::Unit as u32);
(!operand.is_null()).then(|| LLVMValueAsMetadata(operand))
}
}
pub(crate) fn set_unit(&mut self, unit: LLVMMetadataRef) {
unsafe {
LLVMReplaceMDNodeOperandWith(self.value_ref, DISubprogramOperand::Unit as u32, unit)
};
}
pub(crate) fn retained_nodes(&self) -> Option<LLVMMetadataRef> {
unsafe {
let nodes = LLVMGetOperand(self.value_ref, DISubprogramOperand::RetainedNodes as u32);
(!nodes.is_null()).then(|| LLVMValueAsMetadata(nodes))
}
}
pub(crate) fn set_retained_nodes(&mut self, nodes: LLVMMetadataRef) {
unsafe {
LLVMReplaceMDNodeOperandWith(
self.value_ref,
DISubprogramOperand::RetainedNodes as u32,
nodes,
)
};
}
}