use crate::module::AddrSpace;
use either::Either;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::fmt::{self, Display};
use std::hash::Hash;
use std::ops::Deref;
use std::sync::Arc;
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
#[allow(non_camel_case_types)]
pub enum Type {
VoidType,
IntegerType { bits: u32 },
PointerType {
pointee_type: TypeRef,
addr_space: AddrSpace,
},
FPType(FPType),
FuncType {
result_type: TypeRef,
param_types: Vec<TypeRef>,
is_var_arg: bool,
},
VectorType {
element_type: TypeRef,
num_elements: usize,
#[cfg(feature="llvm-11-or-greater")]
scalable: bool,
},
ArrayType {
element_type: TypeRef,
num_elements: usize,
},
StructType {
element_types: Vec<TypeRef>,
is_packed: bool,
},
NamedStructType {
name: String,
},
X86_MMXType,
#[cfg(feature="llvm-12-or-greater")]
X86_AMXType,
MetadataType,
LabelType,
TokenType,
}
impl Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Type::VoidType => write!(f, "void"),
Type::IntegerType { bits } => write!(f, "i{}", bits),
Type::PointerType { pointee_type, .. } => write!(f, "{}*", pointee_type),
Type::FPType(fpt) => write!(f, "{}", fpt),
Type::FuncType {
result_type,
param_types,
is_var_arg,
} => {
write!(f, "{} (", result_type)?;
for (i, param_ty) in param_types.iter().enumerate() {
if i == param_types.len() - 1 {
write!(f, "{}", param_ty)?;
} else {
write!(f, "{}, ", param_ty)?;
}
}
if *is_var_arg {
write!(f, ", ...")?;
}
write!(f, ")")?;
Ok(())
},
Type::VectorType {
element_type,
num_elements,
#[cfg(feature="llvm-11-or-greater")]
scalable,
} => {
#[cfg(feature="llvm-11-or-greater")]
if *scalable {
write!(f, "<vscale x {} x {}>", num_elements, element_type)
} else {
write!(f, "<{} x {}>", num_elements, element_type)
}
#[cfg(feature="llvm-10-or-lower")]
write!(f, "<{} x {}>", num_elements, element_type)
},
Type::ArrayType {
element_type,
num_elements,
} => write!(f, "[{} x {}]", num_elements, element_type),
Type::StructType {
element_types,
is_packed,
} => {
if *is_packed {
write!(f, "<")?;
}
write!(f, "{{ ")?;
for (i, element_ty) in element_types.iter().enumerate() {
if i == element_types.len() - 1 {
write!(f, "{}", element_ty)?;
} else {
write!(f, "{}, ", element_ty)?;
}
}
write!(f, " }}")?;
if *is_packed {
write!(f, ">")?;
}
Ok(())
},
Type::NamedStructType { name } => write!(f, "%{}", name),
Type::X86_MMXType => write!(f, "x86_mmx"),
#[cfg(feature="llvm-12-or-greater")]
Type::X86_AMXType => write!(f, "x86_amx"),
Type::MetadataType => write!(f, "metadata"),
Type::LabelType => write!(f, "label"),
Type::TokenType => write!(f, "token"),
}
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
#[allow(non_camel_case_types)]
pub enum FPType {
Half,
#[cfg(feature="llvm-11-or-greater")]
BFloat,
Single,
Double,
FP128,
X86_FP80,
PPC_FP128,
}
impl From<FPType> for Type {
fn from(fpt: FPType) -> Type {
Type::FPType(fpt)
}
}
impl Display for FPType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FPType::Half => write!(f, "half"),
#[cfg(feature="llvm-11-or-greater")]
FPType::BFloat => write!(f, "bfloat"),
FPType::Single => write!(f, "float"),
FPType::Double => write!(f, "double"),
FPType::FP128 => write!(f, "fp128"),
FPType::X86_FP80 => write!(f, "x86_fp80"),
FPType::PPC_FP128 => write!(f, "ppc_fp128"),
}
}
}
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct TypeRef(Arc<Type>);
impl AsRef<Type> for TypeRef {
fn as_ref(&self) -> &Type {
self.0.as_ref()
}
}
impl Deref for TypeRef {
type Target = Type;
fn deref(&self) -> &Type {
self.0.deref()
}
}
impl Display for TypeRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.0)
}
}
impl TypeRef {
fn new(ty: Type) -> Self {
Self(Arc::new(ty))
}
}
pub trait Typed {
fn get_type(&self, types: &Types) -> TypeRef;
}
impl Typed for TypeRef {
fn get_type(&self, _types: &Types) -> TypeRef {
self.clone()
}
}
impl Typed for Type {
fn get_type(&self, types: &Types) -> TypeRef {
types.get_for_type(&self)
}
}
impl Typed for FPType {
fn get_type(&self, types: &Types) -> TypeRef {
types.fp(*self)
}
}
impl<A, B> Typed for Either<A, B>
where
A: Typed,
B: Typed,
{
fn get_type(&self, types: &Types) -> TypeRef {
match self {
Either::Left(x) => types.type_of(x),
Either::Right(y) => types.type_of(y),
}
}
}
#[derive(Clone)]
pub(crate) struct TypesBuilder {
void_type: TypeRef,
int_types: TypeCache<u32>,
pointer_types: TypeCache<(TypeRef, AddrSpace)>,
fp_types: TypeCache<FPType>,
func_types: TypeCache<(TypeRef, Vec<TypeRef>, bool)>,
vec_types: TypeCache<(TypeRef, usize, bool)>,
arr_types: TypeCache<(TypeRef, usize)>,
struct_types: TypeCache<(Vec<TypeRef>, bool)>,
named_struct_types: TypeCache<String>,
named_struct_defs: HashMap<String, NamedStructDef>,
x86_mmx_type: TypeRef,
#[cfg(feature="llvm-12-or-greater")]
x86_amx_type: TypeRef,
metadata_type: TypeRef,
label_type: TypeRef,
token_type: TypeRef,
llvm_type_map: HashMap<LLVMTypeRef, TypeRef>,
}
impl TypesBuilder {
pub fn new() -> Self {
Self {
void_type: TypeRef::new(Type::VoidType),
int_types: TypeCache::new(),
pointer_types: TypeCache::new(),
fp_types: TypeCache::new(),
func_types: TypeCache::new(),
vec_types: TypeCache::new(),
arr_types: TypeCache::new(),
struct_types: TypeCache::new(),
named_struct_types: TypeCache::new(),
named_struct_defs: HashMap::new(),
x86_mmx_type: TypeRef::new(Type::X86_MMXType),
#[cfg(feature="llvm-12-or-greater")]
x86_amx_type: TypeRef::new(Type::X86_AMXType),
metadata_type: TypeRef::new(Type::MetadataType),
label_type: TypeRef::new(Type::LabelType),
token_type: TypeRef::new(Type::TokenType),
llvm_type_map: HashMap::new(),
}
}
pub fn build(self) -> Types {
Types {
void_type: self.void_type,
int_types: self.int_types,
pointer_types: self.pointer_types,
fp_types: self.fp_types,
func_types: self.func_types,
vec_types: self.vec_types,
arr_types: self.arr_types,
struct_types: self.struct_types,
named_struct_types: self.named_struct_types,
named_struct_defs: self.named_struct_defs,
x86_mmx_type: self.x86_mmx_type,
#[cfg(feature="llvm-12-or-greater")]
x86_amx_type: self.x86_amx_type,
metadata_type: self.metadata_type,
label_type: self.label_type,
token_type: self.token_type,
}
}
}
#[allow(dead_code)]
impl TypesBuilder {
pub fn void(&self) -> TypeRef {
self.void_type.clone()
}
pub fn int(&mut self, bits: u32) -> TypeRef {
self.int_types
.lookup_or_insert(bits, || Type::IntegerType { bits })
}
pub fn bool(&mut self) -> TypeRef {
self.int(1)
}
pub fn i8(&mut self) -> TypeRef {
self.int(8)
}
pub fn i16(&mut self) -> TypeRef {
self.int(16)
}
pub fn i32(&mut self) -> TypeRef {
self.int(32)
}
pub fn i64(&mut self) -> TypeRef {
self.int(64)
}
pub fn pointer_to(&mut self, pointee_type: TypeRef) -> TypeRef {
self.pointer_in_addr_space(pointee_type, 0)
}
pub fn pointer_in_addr_space(
&mut self,
pointee_type: TypeRef,
addr_space: AddrSpace,
) -> TypeRef {
self.pointer_types
.lookup_or_insert((pointee_type.clone(), addr_space), || Type::PointerType {
pointee_type,
addr_space,
})
}
pub fn fp(&mut self, fpt: FPType) -> TypeRef {
self.fp_types.lookup_or_insert(fpt, || Type::FPType(fpt))
}
pub fn single(&mut self) -> TypeRef {
self.fp(FPType::Single)
}
pub fn double(&mut self) -> TypeRef {
self.fp(FPType::Double)
}
pub fn func_type(
&mut self,
result_type: TypeRef,
param_types: Vec<TypeRef>,
is_var_arg: bool,
) -> TypeRef {
self.func_types.lookup_or_insert(
(result_type.clone(), param_types.clone(), is_var_arg),
|| Type::FuncType {
result_type,
param_types,
is_var_arg,
},
)
}
#[cfg(feature="llvm-11-or-greater")]
pub fn vector_of(&mut self, element_type: TypeRef, num_elements: usize, scalable: bool) -> TypeRef {
self.vec_types
.lookup_or_insert((element_type.clone(), num_elements, scalable), || Type::VectorType {
element_type,
num_elements,
scalable,
})
}
#[cfg(feature="llvm-10-or-lower")]
pub fn vector_of(&mut self, element_type: TypeRef, num_elements: usize) -> TypeRef {
self.vec_types
.lookup_or_insert((element_type.clone(), num_elements, false), || Type::VectorType {
element_type,
num_elements,
})
}
pub fn array_of(&mut self, element_type: TypeRef, num_elements: usize) -> TypeRef {
self.arr_types
.lookup_or_insert((element_type.clone(), num_elements), || Type::ArrayType {
element_type,
num_elements,
})
}
pub fn struct_of(&mut self, element_types: Vec<TypeRef>, is_packed: bool) -> TypeRef {
self.struct_types
.lookup_or_insert((element_types.clone(), is_packed), || Type::StructType {
element_types,
is_packed,
})
}
pub fn named_struct(&mut self, name: String) -> TypeRef {
self.named_struct_types
.lookup_or_insert(name.clone(), || Type::NamedStructType { name })
}
pub fn named_struct_def(&self, name: &str) -> &NamedStructDef {
self.named_struct_defs
.get(name)
.expect("Named struct has not been defined")
}
pub fn add_named_struct_def(&mut self, name: String, def: NamedStructDef) {
match self.named_struct_defs.entry(name) {
Entry::Occupied(_) => {
panic!("Trying to redefine named struct");
},
Entry::Vacant(ventry) => {
ventry.insert(def);
},
}
}
pub fn x86_mmx(&self) -> TypeRef {
self.x86_mmx_type.clone()
}
#[cfg(feature="llvm-12-or-greater")]
pub fn x86_amx(&self) -> TypeRef {
self.x86_amx_type.clone()
}
pub fn metadata_type(&self) -> TypeRef {
self.metadata_type.clone()
}
pub fn label_type(&self) -> TypeRef {
self.label_type.clone()
}
pub fn token_type(&self) -> TypeRef {
self.token_type.clone()
}
}
#[derive(Clone, Debug, Hash)]
pub enum NamedStructDef {
Opaque,
Defined(TypeRef),
}
#[derive(Clone)]
pub struct Types {
void_type: TypeRef,
int_types: TypeCache<u32>,
pointer_types: TypeCache<(TypeRef, AddrSpace)>,
fp_types: TypeCache<FPType>,
func_types: TypeCache<(TypeRef, Vec<TypeRef>, bool)>,
vec_types: TypeCache<(TypeRef, usize, bool)>,
arr_types: TypeCache<(TypeRef, usize)>,
struct_types: TypeCache<(Vec<TypeRef>, bool)>,
named_struct_types: TypeCache<String>,
named_struct_defs: HashMap<String, NamedStructDef>,
x86_mmx_type: TypeRef,
#[cfg(feature="llvm-12-or-greater")]
x86_amx_type: TypeRef,
metadata_type: TypeRef,
label_type: TypeRef,
token_type: TypeRef,
}
impl Types {
pub fn type_of<T: Typed + ?Sized>(&self, t: &T) -> TypeRef {
t.get_type(&self)
}
pub fn void(&self) -> TypeRef {
self.void_type.clone()
}
pub fn int(&self, bits: u32) -> TypeRef {
self.int_types
.lookup(&bits)
.unwrap_or_else(|| TypeRef::new(Type::IntegerType { bits }))
}
pub fn bool(&self) -> TypeRef {
self.int(1)
}
pub fn i8(&self) -> TypeRef {
self.int(8)
}
pub fn i16(&self) -> TypeRef {
self.int(16)
}
pub fn i32(&self) -> TypeRef {
self.int(32)
}
pub fn i64(&self) -> TypeRef {
self.int(64)
}
pub fn pointer_to(&self, pointee_type: TypeRef) -> TypeRef {
self.pointer_in_addr_space(pointee_type, 0)
}
pub fn pointer_in_addr_space(&self, pointee_type: TypeRef, addr_space: AddrSpace) -> TypeRef {
self.pointer_types
.lookup(&(pointee_type.clone(), addr_space))
.unwrap_or_else(|| {
TypeRef::new(Type::PointerType {
pointee_type,
addr_space,
})
})
}
pub fn fp(&self, fpt: FPType) -> TypeRef {
self.fp_types
.lookup(&fpt)
.unwrap_or_else(|| TypeRef::new(Type::FPType(fpt)))
}
pub fn single(&self) -> TypeRef {
self.fp(FPType::Single)
}
pub fn double(&self) -> TypeRef {
self.fp(FPType::Double)
}
pub fn func_type(
&self,
result_type: TypeRef,
param_types: Vec<TypeRef>,
is_var_arg: bool,
) -> TypeRef {
self.func_types
.lookup(&(result_type.clone(), param_types.clone(), is_var_arg))
.unwrap_or_else(|| {
TypeRef::new(Type::FuncType {
result_type,
param_types,
is_var_arg,
})
})
}
#[cfg(feature="llvm-11-or-greater")]
pub fn vector_of(&self, element_type: TypeRef, num_elements: usize, scalable: bool) -> TypeRef {
self.vec_types
.lookup(&(element_type.clone(), num_elements, scalable))
.unwrap_or_else(|| {
TypeRef::new(Type::VectorType {
element_type,
num_elements,
scalable,
})
})
}
#[cfg(feature="llvm-10-or-lower")]
pub fn vector_of(&self, element_type: TypeRef, num_elements: usize) -> TypeRef {
self.vec_types
.lookup(&(element_type.clone(), num_elements, false))
.unwrap_or_else(|| {
TypeRef::new(Type::VectorType {
element_type,
num_elements,
})
})
}
pub fn array_of(&self, element_type: TypeRef, num_elements: usize) -> TypeRef {
self.arr_types
.lookup(&(element_type.clone(), num_elements))
.unwrap_or_else(|| {
TypeRef::new(Type::ArrayType {
element_type,
num_elements,
})
})
}
pub fn struct_of(&self, element_types: Vec<TypeRef>, is_packed: bool) -> TypeRef {
self.struct_types
.lookup(&(element_types.clone(), is_packed))
.unwrap_or_else(|| {
TypeRef::new(Type::StructType {
element_types,
is_packed,
})
})
}
pub fn named_struct(&self, name: &str) -> Option<TypeRef> {
self.named_struct_types.lookup(name)
}
pub fn named_struct_def(&self, name: &str) -> Option<&NamedStructDef> {
self.named_struct_defs.get(name)
}
pub fn all_struct_names(&self) -> impl Iterator<Item = &String> {
self.named_struct_defs.keys()
}
pub fn x86_mmx(&self) -> TypeRef {
self.x86_mmx_type.clone()
}
#[cfg(feature="llvm-12-or-greater")]
pub fn x86_amx(&self) -> TypeRef {
self.x86_amx_type.clone()
}
pub fn metadata_type(&self) -> TypeRef {
self.metadata_type.clone()
}
pub fn label_type(&self) -> TypeRef {
self.label_type.clone()
}
pub fn token_type(&self) -> TypeRef {
self.token_type.clone()
}
#[rustfmt::skip]
pub fn get_for_type(&self, ty: &Type) -> TypeRef {
match ty {
Type::VoidType => self.void(),
Type::IntegerType{ bits } => self.int(*bits),
Type::PointerType { pointee_type, addr_space } => {
self.pointer_in_addr_space(pointee_type.clone(), *addr_space)
},
Type::FPType(fpt) => self.fp(*fpt),
Type::FuncType { result_type, param_types, is_var_arg } => {
self.func_type(result_type.clone(), param_types.clone(), *is_var_arg)
},
#[cfg(feature="llvm-11-or-greater")]
Type::VectorType { element_type, num_elements, scalable } => {
self.vector_of(element_type.clone(), *num_elements, *scalable)
},
#[cfg(feature="llvm-10-or-lower")]
Type::VectorType { element_type, num_elements } => {
self.vector_of(element_type.clone(), *num_elements)
},
Type::ArrayType { element_type, num_elements } => {
self.array_of(element_type.clone(), *num_elements)
},
Type::StructType { element_types, is_packed } => {
self.struct_of(element_types.clone(), *is_packed)
},
Type::NamedStructType { name } => {
self.named_struct(name).unwrap_or_else(|| TypeRef::new(Type::NamedStructType { name: name.clone() }))
},
Type::X86_MMXType => self.x86_mmx(),
#[cfg(feature="llvm-12-or-greater")]
Type::X86_AMXType => self.x86_amx(),
Type::MetadataType => self.metadata_type(),
Type::LabelType => self.label_type(),
Type::TokenType => self.token_type(),
}
}
}
impl Types {
pub fn blank_for_testing() -> Self {
TypesBuilder::new().build()
}
}
#[derive(Clone, Debug)]
struct TypeCache<K: Eq + Hash + Clone> {
map: HashMap<K, TypeRef>,
}
#[allow(dead_code)]
impl<K: Eq + Hash + Clone> TypeCache<K> {
fn new() -> Self {
Self {
map: HashMap::new(),
}
}
fn lookup<Q: ?Sized>(&self, key: &Q) -> Option<TypeRef>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.map.get(key).cloned()
}
fn lookup_or_insert(&mut self, key: K, if_missing: impl FnOnce() -> Type) -> TypeRef {
self.map
.entry(key)
.or_insert_with(|| TypeRef::new(if_missing()))
.clone()
}
fn contains_key(&self, key: &K) -> bool {
self.map.contains_key(key)
}
}
use crate::from_llvm::*;
use crate::llvm_sys::*;
use llvm_sys::LLVMTypeKind;
use std::collections::hash_map::Entry;
impl TypesBuilder {
pub(crate) fn type_from_llvm_ref(&mut self, ty: LLVMTypeRef) -> TypeRef {
if let Some(typeref) = self.llvm_type_map.get(&ty) {
return typeref.clone();
}
let typeref = self.parse_type_from_llvm_ref(ty);
self.llvm_type_map.insert(ty, typeref.clone());
typeref
}
fn parse_type_from_llvm_ref(&mut self, ty: LLVMTypeRef) -> TypeRef {
let kind = unsafe { LLVMGetTypeKind(ty) };
match kind {
LLVMTypeKind::LLVMVoidTypeKind => self.void(),
LLVMTypeKind::LLVMIntegerTypeKind => self.int(unsafe { LLVMGetIntTypeWidth(ty) }),
LLVMTypeKind::LLVMPointerTypeKind => {
let pointee_type = self.type_from_llvm_ref(unsafe { LLVMGetElementType(ty) });
self.pointer_in_addr_space(pointee_type, unsafe { LLVMGetPointerAddressSpace(ty) })
},
LLVMTypeKind::LLVMArrayTypeKind => {
let element_type = self.type_from_llvm_ref(unsafe { LLVMGetElementType(ty) });
self.array_of(element_type, unsafe { LLVMGetArrayLength(ty) as usize })
},
LLVMTypeKind::LLVMVectorTypeKind => {
let element_type = self.type_from_llvm_ref(unsafe { LLVMGetElementType(ty) });
#[cfg(feature="llvm-11-or-greater")]
let ret = self.vector_of(element_type, unsafe { LLVMGetVectorSize(ty) as usize }, false);
#[cfg(feature="llvm-10-or-lower")]
let ret = self.vector_of(element_type, unsafe { LLVMGetVectorSize(ty) as usize });
ret
},
#[cfg(feature="llvm-11-or-greater")]
LLVMTypeKind::LLVMScalableVectorTypeKind => {
let element_type = self.type_from_llvm_ref(unsafe { LLVMGetElementType(ty) });
self.vector_of(element_type, unsafe { LLVMGetVectorSize(ty) as usize }, true)
},
LLVMTypeKind::LLVMStructTypeKind => {
let name = if unsafe { LLVMIsLiteralStruct(ty) } != 0 {
None
} else {
unsafe { get_struct_name(ty) }
};
match name {
Some(s) if !s.is_empty() => {
if self.named_struct_types.contains_key(&s) {
self.named_struct(s)
} else if unsafe { LLVMIsOpaqueStruct(ty) } != 0 {
self.add_named_struct_def(s.clone(), NamedStructDef::Opaque);
self.named_struct(s)
} else {
let named_struct_typeref = self.named_struct(s.clone());
let actual_struct_type = self.struct_type_from_llvm_ref(ty);
self.add_named_struct_def(
s,
NamedStructDef::Defined(actual_struct_type),
);
named_struct_typeref
}
},
_ => self.struct_type_from_llvm_ref(ty),
}
},
LLVMTypeKind::LLVMFunctionTypeKind => {
let result_type = self.type_from_llvm_ref(unsafe { LLVMGetReturnType(ty) });
let param_types = {
let num_types = unsafe { LLVMCountParamTypes(ty) };
let mut types: Vec<LLVMTypeRef> = Vec::with_capacity(num_types as usize);
unsafe {
LLVMGetParamTypes(ty, types.as_mut_ptr());
types.set_len(num_types as usize);
};
types
.into_iter()
.map(|t| self.type_from_llvm_ref(t))
.collect()
};
self.func_type(
result_type,
param_types,
unsafe { LLVMIsFunctionVarArg(ty) } != 0,
)
},
LLVMTypeKind::LLVMHalfTypeKind => self.fp(FPType::Half),
#[cfg(feature="llvm-11-or-greater")]
LLVMTypeKind::LLVMBFloatTypeKind => self.fp(FPType::BFloat),
LLVMTypeKind::LLVMFloatTypeKind => self.fp(FPType::Single),
LLVMTypeKind::LLVMDoubleTypeKind => self.fp(FPType::Double),
LLVMTypeKind::LLVMFP128TypeKind => self.fp(FPType::FP128),
LLVMTypeKind::LLVMX86_FP80TypeKind => self.fp(FPType::X86_FP80),
LLVMTypeKind::LLVMPPC_FP128TypeKind => self.fp(FPType::PPC_FP128),
LLVMTypeKind::LLVMX86_MMXTypeKind => self.x86_mmx(),
#[cfg(feature="llvm-12-or-greater")]
LLVMTypeKind::LLVMX86_AMXTypeKind => self.x86_amx(),
LLVMTypeKind::LLVMMetadataTypeKind => self.metadata_type(),
LLVMTypeKind::LLVMLabelTypeKind => self.label_type(),
LLVMTypeKind::LLVMTokenTypeKind => self.token_type(),
}
}
fn struct_type_from_llvm_ref(&mut self, ty: LLVMTypeRef) -> TypeRef {
if unsafe { LLVMIsOpaqueStruct(ty) } != 0 {
panic!(
"struct_type_from_llvm_ref: shouldn't pass an opaque struct type to this function"
);
}
let element_types = {
let num_types = unsafe { LLVMCountStructElementTypes(ty) };
let mut types: Vec<LLVMTypeRef> = Vec::with_capacity(num_types as usize);
unsafe {
LLVMGetStructElementTypes(ty, types.as_mut_ptr());
types.set_len(num_types as usize);
};
types
.into_iter()
.map(|t| self.type_from_llvm_ref(t))
.collect()
};
self.struct_of(element_types, unsafe { LLVMIsPackedStruct(ty) } != 0)
}
}