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::{FP16Type, FP64Type, IntegerType, Signedness},
118 context::Context,
119 result::ExpectOk,
120 };
121
122 use crate::{
123 data_layout::DataLayout,
124 types::{ArrayType, StructType},
125 };
126
127 const X86_64: &str =
129 "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128";
130
131 #[test]
132 fn sizes_of_elements() {
133 let ctx = &mut Context::new();
134 let mut layout = DataLayout::new(X86_64);
135
136 let i24 = IntegerType::get(ctx, 24, Signedness::Signless).into();
139 assert_eq!(layout.type_size_in_bits(ctx, i24).expect_ok(ctx), 24);
140 assert_eq!(layout.type_store_size(ctx, i24).expect_ok(ctx), 3);
141 assert_eq!(layout.type_alloc_size(ctx, i24).expect_ok(ctx), 4);
142 assert_eq!(layout.abi_type_align(ctx, i24).expect_ok(ctx), 4);
143 assert!(!layout.packs_exactly(ctx, i24).expect_ok(ctx));
144
145 let array = ArrayType::get(ctx, i24, 3).into();
147 assert_eq!(layout.type_store_size(ctx, array).expect_ok(ctx), 12);
148 assert_eq!(layout.type_alloc_size(ctx, array).expect_ok(ctx), 12);
149
150 for ty in [
152 IntegerType::get(ctx, 8, Signedness::Signless).into(),
153 IntegerType::get(ctx, 32, Signedness::Signless).into(),
154 IntegerType::get(ctx, 128, Signedness::Signless).into(),
155 FP16Type::get(ctx).into(),
156 FP64Type::get(ctx).into(),
157 ] {
158 assert!(layout.packs_exactly(ctx, ty).expect_ok(ctx));
159 assert_eq!(
160 layout.type_store_size(ctx, ty).expect_ok(ctx),
161 layout.type_alloc_size(ctx, ty).expect_ok(ctx)
162 );
163 }
164 }
165
166 #[test]
167 fn unsized_type_is_an_error() {
168 let ctx = &mut Context::new();
169 let mut layout = DataLayout::new(X86_64);
170
171 let opaque = StructType::get_named(ctx, "opaque".try_into().unwrap(), None)
172 .expect_ok(ctx)
173 .into();
174 assert!(layout.type_size_in_bits(ctx, opaque).is_err());
175 assert!(layout.type_store_size(ctx, opaque).is_err());
176 assert!(layout.type_alloc_size(ctx, opaque).is_err());
177 assert!(layout.abi_type_align(ctx, opaque).is_err());
178 assert!(layout.packs_exactly(ctx, opaque).is_err());
179 }
180
181 #[test]
182 fn host_layout_is_available() {
183 let ctx = &mut Context::new();
184 let mut layout = DataLayout::host().expect_ok(ctx);
185 assert!(!layout.string_representation().is_empty());
186
187 let ptr = crate::types::PointerType::get(ctx, 0).into();
189 assert_eq!(
190 layout.type_store_size(ctx, ptr).expect_ok(ctx) as usize,
191 size_of::<*const u8>()
192 );
193 }
194}