charon_lib/transform/add_missing_info/
compute_layout_guarantees.rs1use itertools::Itertools;
4
5use crate::ast::*;
6use crate::options::TranslateOptions;
7use crate::transform::{TransformCtx, ctx::TransformPass};
8
9pub struct Transform;
10
11struct Guarantees {
12 size: SizeExpr,
13 align: SizeExpr,
14 offsets: IndexVec<VariantId, IndexVec<FieldId, OffsetGuarantee>>,
15}
16
17fn compute_guarantees(
18 krate: &TranslatedCrate,
19 target: &TargetTriple,
20 decl: &TypeDecl,
21) -> Option<Guarantees> {
22 let layout = decl.layout.get(target)?;
23 let repr = &layout.repr;
24 let field_tys = |fields: &IndexVec<FieldId, Field>| {
25 fields.iter().map(|field| field.ty.clone()).collect_vec()
26 };
27
28 let variants: Vec<(Option<VariantId>, Vec<Ty>)> = match &decl.kind {
30 TypeDeclKind::Struct(fields) | TypeDeclKind::Union(fields) => {
31 vec![(None, field_tys(fields))]
32 }
33 TypeDeclKind::Enum(variants) => variants
34 .iter_enumerated()
35 .map(|(id, variant)| (Some(id), field_tys(&variant.fields)))
36 .collect(),
37 TypeDeclKind::Alias(ty) => {
39 return Some(Guarantees {
40 size: SizeExpr::size_of(ty),
41 align: SizeExpr::align_of(ty),
42 offsets: IndexVec::new(),
43 });
44 }
45 TypeDeclKind::Opaque | TypeDeclKind::Error(_) => return None,
46 };
47
48 let field_align = |ty: &Ty| {
51 let align = SizeExpr::align_of(ty);
52 match repr.align_modif {
53 Some(AlignmentModifier::Pack(pack)) => {
54 SizeExprKind::Min(vec![align, SizeExpr::from_usize(pack.into())]).into_expr()
55 }
56 _ => align,
57 }
58 };
59
60 let type_align = |mut aligns: Vec<SizeExpr>| {
63 if let Some(AlignmentModifier::Align(align)) = repr.align_modif {
64 aligns.push(SizeExpr::from_usize(align.into()));
65 }
66 SizeExpr::max_align(aligns)
67 };
68
69 if repr.transparent {
73 let Ok([(_, fields)]) = <[_; 1]>::try_from(variants) else {
74 unreachable!("repr(transparent) on a type with multiple variants");
75 };
76 let size = SizeExpr::max_size(fields.iter().map(SizeExpr::size_of).collect());
77 let align = SizeExpr::max_align(fields.iter().map(SizeExpr::align_of).collect());
78 let offsets = fields
79 .iter()
80 .map(SizeExpr::align_of)
81 .map(OffsetGuarantee::GuaranteedAlignment)
82 .collect();
83 return Some(Guarantees {
84 size,
85 align,
86 offsets: [offsets].into(),
87 });
88 }
89
90 let tag_ty = (decl.kind.is_enum() && repr.guarantees_fixed_field_order()).then(|| {
94 let int_ty = repr.explicit_discr_type.unwrap_or(IntegerTy::Signed(
95 krate.target_information[target].c_enum_smallest_repr_ty,
96 ));
97 TyKind::Scalar(ScalarTy::Integer(int_ty)).into_ty()
98 });
99 let is_repr_c_like = repr.repr_algo == ReprAlgorithm::C || tag_ty.is_some();
100
101 if decl.kind.is_union() && is_repr_c_like {
104 let Ok([(_, fields)]) = <[_; 1]>::try_from(variants) else {
105 unreachable!("union with multiple variants");
106 };
107 let offsets = vec![OffsetGuarantee::AtOffset(SizeExpr::from_usize(0)); fields.len()].into();
108 let align = type_align(fields.iter().map(field_align).collect());
109 let size = SizeExpr::align_to(
110 SizeExpr::max_size(fields.iter().map(SizeExpr::size_of).collect()),
111 align.clone(),
112 );
113 return Some(Guarantees {
114 size,
115 align,
116 offsets: [offsets].into(),
117 });
118 }
119
120 if let TypeDeclKind::Enum(variants) = &decl.kind
124 && variants.iter().all(|variant| variant.fields.is_empty())
125 && let Some(tag_ty) = &tag_ty
126 {
127 return Some(Guarantees {
128 size: SizeExpr::size_of(tag_ty),
129 align: SizeExpr::align_of(tag_ty),
130 offsets: variants.map_ref(|_| IndexVec::new()),
131 });
132 }
133
134 if is_repr_c_like {
141 let variant_ends = variants.iter().filter_map(|(variant, fields)| {
143 let (last, ty) = fields.iter().enumerate().next_back()?;
144 let offset = SizeExpr::offset_of(decl.self_ref(), *variant, FieldId::from_usize(last));
145 Some(SizeExprKind::Plus(offset, SizeExpr::size_of(ty)).into_expr())
146 });
147 let field_aligns = variants
148 .iter()
149 .flat_map(|(_, fields)| fields.iter().map(field_align))
150 .collect_vec();
151 let tag_size = tag_ty.as_ref().map(SizeExpr::size_of);
152 let tag_align = tag_ty.as_ref().map(SizeExpr::align_of);
153
154 let after_tag = tag_size.clone().map(|tag_size| match repr.repr_algo {
155 ReprAlgorithm::C => OffsetGuarantee::AtOffset(SizeExpr::align_to(
158 tag_size,
159 SizeExpr::max_align(field_aligns.clone()),
160 )),
161 ReprAlgorithm::Rust => OffsetGuarantee::ReprCField(FieldPredecessor::Tag),
164 });
165
166 let align = type_align(tag_align.into_iter().chain(field_aligns).collect());
167 let size = SizeExpr::max_size(tag_size.into_iter().chain(variant_ends).collect());
168 let size = SizeExpr::align_to(size, align.clone());
169 let offsets = variants
170 .iter()
171 .map(|(_, fields)| {
172 (0..fields.len())
173 .map(|f| match (f, &after_tag) {
174 (0, None) => OffsetGuarantee::AtOffset(SizeExpr::from_usize(0)),
175 (0, Some(after_tag)) => after_tag.clone(),
176 (f, _) => OffsetGuarantee::ReprCField(FieldPredecessor::Field(
177 FieldId::from_usize(f - 1),
178 )),
179 })
180 .collect()
181 })
182 .collect();
183 return Some(Guarantees {
184 size,
185 align,
186 offsets,
187 });
188 }
189
190 let fields = variants.iter().flat_map(|(_, fields)| fields);
194 let align = type_align(fields.map(field_align).collect());
195 let field_ends = variants.iter().flat_map(|(variant, fields)| {
196 fields.iter().enumerate().map(move |(f, ty)| {
197 let offset = SizeExpr::offset_of(decl.self_ref(), *variant, FieldId::from_usize(f));
198 SizeExprKind::Plus(offset, SizeExpr::size_of(ty)).into_expr()
199 })
200 });
201 let size = SizeExpr::max_size(field_ends.collect());
202 let offsets = variants
203 .iter()
204 .map(|(_, fields)| {
205 fields
206 .iter()
207 .map(|ty| OffsetGuarantee::GuaranteedAlignment(field_align(ty)))
208 .collect()
209 })
210 .collect();
211 Some(Guarantees {
212 size: SizeExpr::at_least(SizeExpr::align_to(size, align.clone())),
213 align: SizeExpr::at_least(align),
214 offsets,
215 })
216}
217
218impl TransformPass for Transform {
219 fn should_run(&self, options: &TranslateOptions) -> bool {
220 !options.no_compute_layout_guarantees
221 }
222
223 fn transform_ctx(&self, ctx: &mut TransformCtx) {
224 let target = ctx
225 .translated
226 .target_information
227 .keys()
228 .exactly_one()
229 .expect("layout guarantees expect exactly one target")
230 .clone();
231
232 ctx.for_each_type_decl(|ctx, decl| {
233 let Some(guarantees) = compute_guarantees(&ctx.translated, &target, decl) else {
234 return;
235 };
236
237 let Some(layout) = decl.layout.get_mut(&target) else {
238 return;
239 };
240
241 layout.inhabited = layout.inhabited.clone().normalize(&ctx.translated, None);
243 for layout in layout.variant_layouts.iter_mut().flatten() {
244 layout.inhabited = layout.inhabited.clone().normalize(&ctx.translated, None);
245 }
246
247 layout.size.guarantee = Some(guarantees.size.normalize(None, None, false));
248 layout.align.guarantee = Some(guarantees.align.normalize(None, None, false));
249 for (variant, offsets) in layout.variant_layouts.iter_mut().zip(guarantees.offsets) {
250 if let Some(variant) = variant {
251 for (offset, guarantee) in variant.field_offsets.iter_mut().zip(offsets) {
252 offset.guarantee = Some(guarantee);
253 }
254 }
255 }
256 });
257 }
258}