use pliron::{
arg_err_noloc, arg_error_noloc, builtin::ops::ModuleOp, context::Context, printable::Printable,
result::Result, r#type::TypeHandle,
};
use crate::{
attributes::get_data_layout,
llvm_sys::{
core::{LLVMContext, LLVMType, llvm_type_is_sized},
target::LLVMTargetData,
},
to_llvm_ir::{TypeConversionContext, convert_type},
};
#[derive(Debug, thiserror::Error)]
pub enum DataLayoutErr {
#[error("Cannot get the data layout of the host: {0}")]
NoHostLayout(String),
#[error("Type {0} has no size, and thus no layout")]
UnsizedType(String),
}
pub struct DataLayout {
llvm_ctx: LLVMContext,
target_data: LLVMTargetData,
types: TypeConversionContext,
}
impl DataLayout {
pub fn new(layout: &str) -> Self {
Self {
llvm_ctx: LLVMContext::default(),
target_data: LLVMTargetData::new(layout),
types: TypeConversionContext::default(),
}
}
pub fn host() -> Result<Self> {
let target_data = LLVMTargetData::host()
.map_err(|err| arg_error_noloc!(DataLayoutErr::NoHostLayout(err)))?;
Ok(Self {
llvm_ctx: LLVMContext::default(),
target_data,
types: TypeConversionContext::default(),
})
}
pub fn from_module_layout(ctx: &Context, module: ModuleOp) -> Result<Self> {
match get_data_layout(ctx, module) {
Some(layout) if !layout.is_empty() => Ok(Self::new(&layout)),
_ => Self::host(),
}
}
pub fn string_representation(&self) -> String {
self.target_data.copy_string_rep_of_target_data()
}
pub fn type_size_in_bits(&mut self, ctx: &Context, ty: TypeHandle) -> Result<u64> {
let ty = self.llvm_type(ctx, ty)?;
Ok(self.target_data.size_of_type_in_bits(ty))
}
pub fn type_store_size(&mut self, ctx: &Context, ty: TypeHandle) -> Result<u64> {
let ty = self.llvm_type(ctx, ty)?;
Ok(self.target_data.store_size_of_type(ty))
}
pub fn type_alloc_size(&mut self, ctx: &Context, ty: TypeHandle) -> Result<u64> {
let ty = self.llvm_type(ctx, ty)?;
Ok(self.target_data.abi_size_of_type(ty))
}
pub fn abi_type_align(&mut self, ctx: &Context, ty: TypeHandle) -> Result<u32> {
let ty = self.llvm_type(ctx, ty)?;
Ok(self.target_data.abi_alignment_of_type(ty))
}
pub fn packs_exactly(&mut self, ctx: &Context, ty: TypeHandle) -> Result<bool> {
let ty = self.llvm_type(ctx, ty)?;
Ok(self.target_data.store_size_of_type(ty) == self.target_data.abi_size_of_type(ty))
}
fn llvm_type(&mut self, ctx: &Context, ty: TypeHandle) -> Result<LLVMType> {
let llvm_ty = convert_type(ctx, &self.llvm_ctx, &mut self.types, ty)?;
if !llvm_type_is_sized(llvm_ty) {
return arg_err_noloc!(DataLayoutErr::UnsizedType(ty.disp(ctx).to_string()));
}
Ok(llvm_ty)
}
}
#[cfg(test)]
mod tests {
use pliron::{
builtin::types::{FP16Type, FP64Type, IntegerType, Signedness},
context::Context,
ident,
result::ExpectOk,
};
use crate::{
data_layout::DataLayout,
types::{ArrayType, StructType},
};
const X86_64: &str =
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128";
#[test]
fn sizes_of_elements() {
let ctx = &mut Context::new();
let mut layout = DataLayout::new(X86_64);
let i24 = IntegerType::get(ctx, 24, Signedness::Signless).into();
assert_eq!(layout.type_size_in_bits(ctx, i24).expect_ok(ctx), 24);
assert_eq!(layout.type_store_size(ctx, i24).expect_ok(ctx), 3);
assert_eq!(layout.type_alloc_size(ctx, i24).expect_ok(ctx), 4);
assert_eq!(layout.abi_type_align(ctx, i24).expect_ok(ctx), 4);
assert!(!layout.packs_exactly(ctx, i24).expect_ok(ctx));
let array = ArrayType::get(ctx, i24, 3).into();
assert_eq!(layout.type_store_size(ctx, array).expect_ok(ctx), 12);
assert_eq!(layout.type_alloc_size(ctx, array).expect_ok(ctx), 12);
for ty in [
IntegerType::get(ctx, 8, Signedness::Signless).into(),
IntegerType::get(ctx, 32, Signedness::Signless).into(),
IntegerType::get(ctx, 128, Signedness::Signless).into(),
FP16Type::get(ctx).into(),
FP64Type::get(ctx).into(),
] {
assert!(layout.packs_exactly(ctx, ty).expect_ok(ctx));
assert_eq!(
layout.type_store_size(ctx, ty).expect_ok(ctx),
layout.type_alloc_size(ctx, ty).expect_ok(ctx)
);
}
}
#[test]
fn unsized_type_is_an_error() {
let ctx = &mut Context::new();
let mut layout = DataLayout::new(X86_64);
let opaque = StructType::get_named(ctx, ident!("opaque"), None)
.expect_ok(ctx)
.into();
assert!(layout.type_size_in_bits(ctx, opaque).is_err());
assert!(layout.type_store_size(ctx, opaque).is_err());
assert!(layout.type_alloc_size(ctx, opaque).is_err());
assert!(layout.abi_type_align(ctx, opaque).is_err());
assert!(layout.packs_exactly(ctx, opaque).is_err());
}
#[test]
fn host_layout_is_available() {
let ctx = &mut Context::new();
let mut layout = DataLayout::host().expect_ok(ctx);
assert!(!layout.string_representation().is_empty());
let ptr = crate::types::PointerType::get(ctx, 0).into();
assert_eq!(
layout.type_store_size(ctx, ptr).expect_ok(ctx) as usize,
size_of::<*const u8>()
);
}
}