1use pliron::{
7 arg_err_noloc, arg_error_noloc, builtin::ops::ModuleOp, context::Context, printable::Printable,
8 result::Result, r#type::TypeHandle,
9};
10
11use crate::{
12 attributes::get_data_layout,
13 llvm_sys::{
14 core::{LLVMContext, LLVMType, llvm_type_is_sized},
15 target::LLVMTargetData,
16 },
17 to_llvm_ir::{TypeConversionContext, convert_type},
18};
19
20#[derive(Debug, thiserror::Error)]
21pub enum DataLayoutErr {
22 #[error("Cannot get the data layout of the host: {0}")]
23 NoHostLayout(String),
24 #[error("Type {0} has no size, and thus no layout")]
25 UnsizedType(String),
26}
27
28pub struct DataLayout {
30 llvm_ctx: LLVMContext,
31 target_data: LLVMTargetData,
32 types: TypeConversionContext,
33}
34
35impl DataLayout {
36 pub fn new(layout: &str) -> Self {
40 Self {
41 llvm_ctx: LLVMContext::default(),
42 target_data: LLVMTargetData::new(layout),
43 types: TypeConversionContext::default(),
44 }
45 }
46
47 pub fn host() -> Result<Self> {
49 let target_data = LLVMTargetData::host()
50 .map_err(|err| arg_error_noloc!(DataLayoutErr::NoHostLayout(err)))?;
51 Ok(Self {
52 llvm_ctx: LLVMContext::default(),
53 target_data,
54 types: TypeConversionContext::default(),
55 })
56 }
57
58 pub fn from_module_layout(ctx: &Context, module: ModuleOp) -> Result<Self> {
61 match get_data_layout(ctx, module) {
62 Some(layout) if !layout.is_empty() => Ok(Self::new(&layout)),
63 _ => Self::host(),
64 }
65 }
66
67 pub fn string_representation(&self) -> String {
69 self.target_data.copy_string_rep_of_target_data()
70 }
71
72 pub fn type_size_in_bits(&mut self, ctx: &Context, ty: TypeHandle) -> Result<u64> {
74 let ty = self.llvm_type(ctx, ty)?;
75 Ok(self.target_data.size_of_type_in_bits(ty))
76 }
77
78 pub fn type_store_size(&mut self, ctx: &Context, ty: TypeHandle) -> Result<u64> {
80 let ty = self.llvm_type(ctx, ty)?;
81 Ok(self.target_data.store_size_of_type(ty))
82 }
83
84 pub fn type_alloc_size(&mut self, ctx: &Context, ty: TypeHandle) -> Result<u64> {
86 let ty = self.llvm_type(ctx, ty)?;
87 Ok(self.target_data.abi_size_of_type(ty))
88 }
89
90 pub fn abi_type_align(&mut self, ctx: &Context, ty: TypeHandle) -> Result<u32> {
92 let ty = self.llvm_type(ctx, ty)?;
93 Ok(self.target_data.abi_alignment_of_type(ty))
94 }
95
96 pub fn packs_exactly(&mut self, ctx: &Context, ty: TypeHandle) -> Result<bool> {
100 let ty = self.llvm_type(ctx, ty)?;
101 Ok(self.target_data.store_size_of_type(ty) == self.target_data.abi_size_of_type(ty))
102 }
103
104 fn llvm_type(&mut self, ctx: &Context, ty: TypeHandle) -> Result<LLVMType> {
106 let llvm_ty = convert_type(ctx, &self.llvm_ctx, &mut self.types, ty)?;
107 if !llvm_type_is_sized(llvm_ty) {
108 return arg_err_noloc!(DataLayoutErr::UnsizedType(ty.disp(ctx).to_string()));
109 }
110 Ok(llvm_ty)
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use pliron::{
117 builtin::types::{BF16Type, FP16Type, FP64Type, IntegerType, Signedness},
118 context::Context,
119 ident,
120 result::ExpectOk,
121 };
122
123 use crate::{
124 data_layout::DataLayout,
125 types::{ArrayType, StructType},
126 };
127
128 const X86_64: &str =
130 "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128";
131
132 #[test]
133 fn sizes_of_elements() {
134 let ctx = &mut Context::new();
135 let mut layout = DataLayout::new(X86_64);
136
137 let i24 = IntegerType::get(ctx, 24, Signedness::Signless).into();
140 assert_eq!(layout.type_size_in_bits(ctx, i24).expect_ok(ctx), 24);
141 assert_eq!(layout.type_store_size(ctx, i24).expect_ok(ctx), 3);
142 assert_eq!(layout.type_alloc_size(ctx, i24).expect_ok(ctx), 4);
143 assert_eq!(layout.abi_type_align(ctx, i24).expect_ok(ctx), 4);
144 assert!(!layout.packs_exactly(ctx, i24).expect_ok(ctx));
145
146 let array = ArrayType::get(ctx, i24, 3).into();
148 assert_eq!(layout.type_store_size(ctx, array).expect_ok(ctx), 12);
149 assert_eq!(layout.type_alloc_size(ctx, array).expect_ok(ctx), 12);
150
151 for ty in [
153 IntegerType::get(ctx, 8, Signedness::Signless).into(),
154 IntegerType::get(ctx, 32, Signedness::Signless).into(),
155 IntegerType::get(ctx, 128, Signedness::Signless).into(),
156 FP16Type::get(ctx).into(),
157 BF16Type::get(ctx).into(),
158 FP64Type::get(ctx).into(),
159 ] {
160 assert!(layout.packs_exactly(ctx, ty).expect_ok(ctx));
161 assert_eq!(
162 layout.type_store_size(ctx, ty).expect_ok(ctx),
163 layout.type_alloc_size(ctx, ty).expect_ok(ctx)
164 );
165 }
166 }
167
168 #[test]
169 fn unsized_type_is_an_error() {
170 let ctx = &mut Context::new();
171 let mut layout = DataLayout::new(X86_64);
172
173 let opaque = StructType::get_named(ctx, ident!("opaque"), None)
174 .expect_ok(ctx)
175 .into();
176 assert!(layout.type_size_in_bits(ctx, opaque).is_err());
177 assert!(layout.type_store_size(ctx, opaque).is_err());
178 assert!(layout.type_alloc_size(ctx, opaque).is_err());
179 assert!(layout.abi_type_align(ctx, opaque).is_err());
180 assert!(layout.packs_exactly(ctx, opaque).is_err());
181 }
182
183 #[test]
184 fn host_layout_is_available() {
185 let ctx = &mut Context::new();
186 let mut layout = DataLayout::host().expect_ok(ctx);
187 assert!(!layout.string_representation().is_empty());
188
189 let ptr = crate::types::PointerType::get(ctx, 0).into();
191 assert_eq!(
192 layout.type_store_size(ctx, ptr).expect_ok(ctx) as usize,
193 size_of::<*const u8>()
194 );
195 }
196}