use crate::module::AddrSpace;
use either::Either;
use std::sync::{Arc, RwLock, Weak};
#[derive(Clone, Debug)]
#[allow(non_camel_case_types)]
pub enum Type {
VoidType,
IntegerType { bits: u32 },
PointerType {
pointee_type: Box<Type>,
addr_space: AddrSpace,
},
FPType(FPType),
FuncType {
result_type: Box<Type>,
param_types: Vec<Type>,
is_var_arg: bool,
},
VectorType {
element_type: Box<Type>,
num_elements: usize,
},
ArrayType {
element_type: Box<Type>,
num_elements: usize,
},
StructType {
element_types: Vec<Type>,
is_packed: bool,
},
NamedStructType {
name: String,
ty: Option<Weak<RwLock<Type>>>,
},
X86_MMXType,
MetadataType,
LabelType,
TokenType,
}
impl PartialEq for Type {
fn eq(&self, other: &Type) -> bool {
match (self, other) {
(Type::VoidType, Type::VoidType) => true,
(Type::IntegerType { bits: bits_a },
Type::IntegerType { bits: bits_b })
=> bits_a == bits_b,
(Type::PointerType { pointee_type: pt_a, addr_space: as_a },
Type::PointerType { pointee_type: pt_b, addr_space: as_b })
=> pt_a == pt_b && as_a == as_b,
(Type::FPType(fp_a),
Type::FPType(fp_b))
=> fp_a == fp_b,
(Type::FuncType { result_type: rt_a, param_types: pt_a, is_var_arg: iva_a },
Type::FuncType { result_type: rt_b, param_types: pt_b, is_var_arg: iva_b })
=> rt_a == rt_b && pt_a == pt_b && iva_a == iva_b,
(Type::VectorType { element_type: et_a, num_elements: num_a },
Type::VectorType { element_type: et_b, num_elements: num_b })
=> et_a == et_b && num_a == num_b,
(Type::ArrayType { element_type: et_a, num_elements: num_a },
Type::ArrayType { element_type: et_b, num_elements: num_b })
=> et_a == et_b && num_a == num_b,
(Type::StructType { element_types: et_a, is_packed: ip_a },
Type::StructType { element_types: et_b, is_packed: ip_b })
=> et_a == et_b && ip_a == ip_b,
(Type::NamedStructType { name: name_a, .. },
Type::NamedStructType { name: name_b, .. })
=> name_a == name_b,
(Type::X86_MMXType, Type::X86_MMXType) => true,
(Type::MetadataType, Type::MetadataType) => true,
(Type::LabelType, Type::LabelType) => true,
(Type::TokenType, Type::TokenType) => true,
_ => false,
}
}
}
impl Eq for Type {}
impl Type {
pub fn bool() -> Type {
Type::IntegerType { bits: 1 }
}
pub fn i8() -> Type {
Type::IntegerType { bits: 8 }
}
pub fn i16() -> Type {
Type::IntegerType { bits: 16 }
}
pub fn i32() -> Type {
Type::IntegerType { bits: 32 }
}
pub fn i64() -> Type {
Type::IntegerType { bits: 64 }
}
pub fn half() -> Type {
Type::FPType(FPType::Half)
}
pub fn single() -> Type {
Type::FPType(FPType::Single)
}
pub fn double() -> Type {
Type::FPType(FPType::Double)
}
pub fn pointer_to(ty: Type) -> Type {
Type::PointerType {
pointee_type: Box::new(ty),
addr_space: 0,
}
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
#[allow(non_camel_case_types)]
pub enum FPType {
Half,
Single,
Double,
FP128,
X86_FP80,
PPC_FP128,
}
impl From<FPType> for Type {
fn from(fpt: FPType) -> Type {
Type::FPType(fpt)
}
}
pub trait Typed {
fn get_type(&self) -> Type;
}
impl Typed for Type {
fn get_type(&self) -> Type {
self.clone()
}
}
impl Typed for FPType {
fn get_type(&self) -> Type {
self.clone().into()
}
}
impl<A, B> Typed for Either<A, B>
where
A: Typed,
B: Typed,
{
fn get_type(&self) -> Type {
match self {
Either::Left(x) => x.get_type(),
Either::Right(y) => y.get_type(),
}
}
}
use crate::from_llvm::*;
use llvm_sys::LLVMTypeKind;
use std::collections::{HashMap, HashSet};
pub(crate) type TyNameMap = HashMap<String, Option<Arc<RwLock<Type>>>>;
impl Type {
pub(crate) fn from_llvm_ref(ty: LLVMTypeRef, tynamemap: &mut TyNameMap) -> Self {
let kind = unsafe { LLVMGetTypeKind(ty) };
match kind {
LLVMTypeKind::LLVMVoidTypeKind => Type::VoidType,
LLVMTypeKind::LLVMIntegerTypeKind => Type::IntegerType {
bits: unsafe { LLVMGetIntTypeWidth(ty) },
},
LLVMTypeKind::LLVMPointerTypeKind => Type::PointerType {
pointee_type: Box::new(Type::from_llvm_ref(
unsafe { LLVMGetElementType(ty) },
tynamemap,
)),
addr_space: unsafe { LLVMGetPointerAddressSpace(ty) },
},
LLVMTypeKind::LLVMArrayTypeKind => Type::ArrayType {
element_type: Box::new(Type::from_llvm_ref(
unsafe { LLVMGetElementType(ty) },
tynamemap,
)),
num_elements: unsafe { LLVMGetArrayLength(ty) as usize },
},
LLVMTypeKind::LLVMVectorTypeKind => Type::VectorType {
element_type: Box::new(Type::from_llvm_ref(
unsafe { LLVMGetElementType(ty) },
tynamemap,
)),
num_elements: unsafe { LLVMGetVectorSize(ty) as usize },
},
LLVMTypeKind::LLVMStructTypeKind => {
let name = if unsafe { LLVMIsLiteralStruct(ty) } != 0 {
None
} else {
unsafe { get_struct_name(ty) }
};
match name {
Some(ref s) if !s.is_empty() => {
let actual_type: Option<Arc<RwLock<Type>>> = if tynamemap.contains_key(s) {
tynamemap.get(s).unwrap().clone()
} else if unsafe { LLVMIsOpaqueStruct(ty) } != 0 {
tynamemap.insert(s.clone(), None);
None
} else {
tynamemap.insert(s.clone(), None);
let type_with_opaqued_self_refs =
Type::struct_type_from_llvm_ref(ty, tynamemap);
let arc = Arc::new(RwLock::new(type_with_opaqued_self_refs.clone()));
let actual_type =
Type::replace_in_type(type_with_opaqued_self_refs, s, &arc);
*arc.write().unwrap() = actual_type;
tynamemap.insert(s.clone(), Some(arc.clone()));
Some(arc)
};
Type::NamedStructType {
name: s.clone(),
ty: actual_type.map(|arc| Arc::downgrade(&arc)),
}
}
_ => Type::struct_type_from_llvm_ref(ty, tynamemap),
}
}
LLVMTypeKind::LLVMFunctionTypeKind => Type::FuncType {
result_type: Box::new(Type::from_llvm_ref(
unsafe { LLVMGetReturnType(ty) },
tynamemap,
)),
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| Type::from_llvm_ref(t, tynamemap))
.collect()
},
is_var_arg: unsafe { LLVMIsFunctionVarArg(ty) } != 0,
},
LLVMTypeKind::LLVMHalfTypeKind => Type::FPType(FPType::Half),
LLVMTypeKind::LLVMFloatTypeKind => Type::FPType(FPType::Single),
LLVMTypeKind::LLVMDoubleTypeKind => Type::FPType(FPType::Double),
LLVMTypeKind::LLVMFP128TypeKind => Type::FPType(FPType::FP128),
LLVMTypeKind::LLVMX86_FP80TypeKind => Type::FPType(FPType::X86_FP80),
LLVMTypeKind::LLVMPPC_FP128TypeKind => Type::FPType(FPType::PPC_FP128),
LLVMTypeKind::LLVMX86_MMXTypeKind => Type::X86_MMXType,
LLVMTypeKind::LLVMMetadataTypeKind => Type::MetadataType,
LLVMTypeKind::LLVMLabelTypeKind => Type::LabelType,
LLVMTypeKind::LLVMTokenTypeKind => Type::TokenType,
}
}
fn replace_in_type(ty: Type, target_name: &str, replacement: &Arc<RwLock<Type>>) -> Type {
Type::_replace_in_type(ty, target_name, replacement, &mut HashSet::new())
}
fn _replace_in_type(
ty: Type,
target_name: &str,
replacement: &Arc<RwLock<Type>>,
seen_names: &mut HashSet<String>,
) -> Type {
match ty {
Type::NamedStructType { ref name, ty: None } if name == target_name => {
Type::NamedStructType {
name: name.clone(),
ty: Some(Arc::downgrade(replacement)),
}
}
Type::NamedStructType { ref name, ref ty } if ty.is_some() && !seen_names.contains(name) => {
seen_names.insert(name.clone());
let weak = ty
.as_ref()
.expect("we checked that ty.is_some() in the pattern guard");
let arc: Arc<RwLock<Type>> = weak.upgrade().expect("Failed to upgrade weak reference");
let inner_ty = arc.read().unwrap().clone();
*arc.write().unwrap() = Type::_replace_in_type(inner_ty, target_name, replacement, seen_names);
Type::NamedStructType {
name: name.clone(),
ty: Some(Arc::downgrade(&arc)),
}
}
Type::PointerType { pointee_type, addr_space } => Type::PointerType {
pointee_type: Box::new(Type::_replace_in_type(*pointee_type, target_name, replacement, seen_names)),
addr_space,
},
Type::FuncType { result_type, param_types, is_var_arg } => Type::FuncType {
result_type: Box::new(Type::_replace_in_type(
*result_type,
target_name,
replacement,
seen_names,
)),
param_types: param_types
.into_iter()
.map(|t| {
Type::_replace_in_type(t, target_name, replacement, seen_names)
})
.collect(),
is_var_arg,
},
Type::VectorType { element_type, num_elements } => Type::VectorType {
element_type: Box::new(Type::_replace_in_type(
*element_type,
target_name,
replacement,
seen_names,
)),
num_elements,
},
Type::ArrayType { element_type, num_elements } => Type::ArrayType {
element_type: Box::new(Type::_replace_in_type(
*element_type,
target_name,
replacement,
seen_names,
)),
num_elements,
},
Type::StructType { element_types, is_packed } => Type::StructType {
element_types: element_types
.into_iter()
.map(|t| {
Type::_replace_in_type(t, target_name, replacement, seen_names)
})
.collect(),
is_packed,
},
_ => ty,
}
}
fn struct_type_from_llvm_ref(ty: LLVMTypeRef, tynamemap: &mut TyNameMap) -> Self {
if unsafe { LLVMIsOpaqueStruct(ty) } != 0 {
panic!("struct_type_from_llvm_ref: shouldn't pass an opaque struct type to this function");
}
Type::StructType {
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| Type::from_llvm_ref(t, tynamemap))
.collect()
},
is_packed: unsafe { LLVMIsPackedStruct(ty) } != 0,
}
}
}