1//! Helpers for code generation that need struct layout
2
3use super::helpers;
4
5use crate::ir::comp::CompInfo;
6use crate::ir::context::BindgenContext;
7use crate::ir::layout::Layout;
8use crate::ir::ty::{Type, TypeKind};
9use proc_macro2::{self, Ident, Span};
10use std::cmp;
11
12const MAX_GUARANTEED_ALIGN: usize = 8;
13
14/// Trace the layout of struct.
15#[derive(Debug)]
16pub struct StructLayoutTracker<'a> {
17 name: &'a str,
18 ctx: &'a BindgenContext,
19 comp: &'a CompInfo,
20 is_packed: bool,
21 known_type_layout: Option<Layout>,
22 is_rust_union: bool,
23 can_copy_union_fields: bool,
24 latest_offset: usize,
25 padding_count: usize,
26 latest_field_layout: Option<Layout>,
27 max_field_align: usize,
28 last_field_was_bitfield: bool,
29}
30
31/// Returns a size aligned to a given value.
32pub fn align_to(size: usize, align: usize) -> usize {
33 if align == 0 {
34 return size;
35 }
36
37 let rem: usize = size % align;
38 if rem == 0 {
39 return size;
40 }
41
42 size + align - rem
43}
44
45/// Returns the lower power of two byte count that can hold at most n bits.
46pub fn bytes_from_bits_pow2(mut n: usize) -> usize {
47 if n == 0 {
48 return 0;
49 }
50
51 if n <= 8 {
52 return 1;
53 }
54
55 if !n.is_power_of_two() {
56 n = n.next_power_of_two();
57 }
58
59 n / 8
60}
61
62#[test]
63fn test_align_to() {
64 assert_eq!(align_to(1, 1), 1);
65 assert_eq!(align_to(1, 2), 2);
66 assert_eq!(align_to(1, 4), 4);
67 assert_eq!(align_to(5, 1), 5);
68 assert_eq!(align_to(17, 4), 20);
69}
70
71#[test]
72fn test_bytes_from_bits_pow2() {
73 assert_eq!(bytes_from_bits_pow2(0), 0);
74 for i: usize in 1..9 {
75 assert_eq!(bytes_from_bits_pow2(i), 1);
76 }
77 for i: usize in 9..17 {
78 assert_eq!(bytes_from_bits_pow2(i), 2);
79 }
80 for i: usize in 17..33 {
81 assert_eq!(bytes_from_bits_pow2(i), 4);
82 }
83}
84
85impl<'a> StructLayoutTracker<'a> {
86 pub fn new(
87 ctx: &'a BindgenContext,
88 comp: &'a CompInfo,
89 ty: &'a Type,
90 name: &'a str,
91 ) -> Self {
92 let known_type_layout = ty.layout(ctx);
93 let is_packed = comp.is_packed(ctx, known_type_layout.as_ref());
94 let (is_rust_union, can_copy_union_fields) =
95 comp.is_rust_union(ctx, known_type_layout.as_ref(), name);
96 StructLayoutTracker {
97 name,
98 ctx,
99 comp,
100 is_packed,
101 known_type_layout,
102 is_rust_union,
103 can_copy_union_fields,
104 latest_offset: 0,
105 padding_count: 0,
106 latest_field_layout: None,
107 max_field_align: 0,
108 last_field_was_bitfield: false,
109 }
110 }
111
112 pub fn can_copy_union_fields(&self) -> bool {
113 self.can_copy_union_fields
114 }
115
116 pub fn is_rust_union(&self) -> bool {
117 self.is_rust_union
118 }
119
120 pub fn saw_vtable(&mut self) {
121 debug!("saw vtable for {}", self.name);
122
123 let ptr_size = self.ctx.target_pointer_size();
124 self.latest_offset += ptr_size;
125 self.latest_field_layout = Some(Layout::new(ptr_size, ptr_size));
126 self.max_field_align = ptr_size;
127 }
128
129 pub fn saw_base(&mut self, base_ty: &Type) {
130 debug!("saw base for {}", self.name);
131 if let Some(layout) = base_ty.layout(self.ctx) {
132 self.align_to_latest_field(layout);
133
134 self.latest_offset += self.padding_bytes(layout) + layout.size;
135 self.latest_field_layout = Some(layout);
136 self.max_field_align = cmp::max(self.max_field_align, layout.align);
137 }
138 }
139
140 pub fn saw_bitfield_unit(&mut self, layout: Layout) {
141 debug!("saw bitfield unit for {}: {:?}", self.name, layout);
142
143 self.align_to_latest_field(layout);
144
145 self.latest_offset += layout.size;
146
147 debug!(
148 "Offset: <bitfield>: {} -> {}",
149 self.latest_offset - layout.size,
150 self.latest_offset
151 );
152
153 self.latest_field_layout = Some(layout);
154 self.last_field_was_bitfield = true;
155 // NB: We intentionally don't update the max_field_align here, since our
156 // bitfields code doesn't necessarily guarantee it, so we need to
157 // actually generate the dummy alignment.
158 }
159
160 /// Returns a padding field if necessary for a given new field _before_
161 /// adding that field.
162 pub fn saw_field(
163 &mut self,
164 field_name: &str,
165 field_ty: &Type,
166 field_offset: Option<usize>,
167 ) -> Option<proc_macro2::TokenStream> {
168 let mut field_layout = field_ty.layout(self.ctx)?;
169
170 if let TypeKind::Array(inner, len) =
171 *field_ty.canonical_type(self.ctx).kind()
172 {
173 // FIXME(emilio): As an _ultra_ hack, we correct the layout returned
174 // by arrays of structs that have a bigger alignment than what we
175 // can support.
176 //
177 // This means that the structs in the array are super-unsafe to
178 // access, since they won't be properly aligned, but there's not too
179 // much we can do about it.
180 if let Some(layout) = self.ctx.resolve_type(inner).layout(self.ctx)
181 {
182 if layout.align > MAX_GUARANTEED_ALIGN {
183 field_layout.size =
184 align_to(layout.size, layout.align) * len;
185 field_layout.align = MAX_GUARANTEED_ALIGN;
186 }
187 }
188 }
189 self.saw_field_with_layout(field_name, field_layout, field_offset)
190 }
191
192 pub fn saw_field_with_layout(
193 &mut self,
194 field_name: &str,
195 field_layout: Layout,
196 field_offset: Option<usize>,
197 ) -> Option<proc_macro2::TokenStream> {
198 let will_merge_with_bitfield = self.align_to_latest_field(field_layout);
199
200 let is_union = self.comp.is_union();
201 let padding_bytes = match field_offset {
202 Some(offset) if offset / 8 > self.latest_offset => {
203 offset / 8 - self.latest_offset
204 }
205 _ => {
206 if will_merge_with_bitfield ||
207 field_layout.align == 0 ||
208 is_union
209 {
210 0
211 } else if !self.is_packed {
212 self.padding_bytes(field_layout)
213 } else if let Some(l) = self.known_type_layout {
214 self.padding_bytes(l)
215 } else {
216 0
217 }
218 }
219 };
220
221 self.latest_offset += padding_bytes;
222
223 let padding_layout = if self.is_packed || is_union {
224 None
225 } else {
226 let force_padding = self.ctx.options().force_explicit_padding;
227
228 // Otherwise the padding is useless.
229 let need_padding = force_padding ||
230 padding_bytes >= field_layout.align ||
231 field_layout.align > MAX_GUARANTEED_ALIGN;
232
233 debug!(
234 "Offset: <padding>: {} -> {}",
235 self.latest_offset - padding_bytes,
236 self.latest_offset
237 );
238
239 debug!(
240 "align field {} to {}/{} with {} padding bytes {:?}",
241 field_name,
242 self.latest_offset,
243 field_offset.unwrap_or(0) / 8,
244 padding_bytes,
245 field_layout
246 );
247
248 let padding_align = if force_padding {
249 1
250 } else {
251 cmp::min(field_layout.align, MAX_GUARANTEED_ALIGN)
252 };
253
254 if need_padding && padding_bytes != 0 {
255 Some(Layout::new(padding_bytes, padding_align))
256 } else {
257 None
258 }
259 };
260
261 self.latest_offset += field_layout.size;
262 self.latest_field_layout = Some(field_layout);
263 self.max_field_align =
264 cmp::max(self.max_field_align, field_layout.align);
265 self.last_field_was_bitfield = false;
266
267 debug!(
268 "Offset: {}: {} -> {}",
269 field_name,
270 self.latest_offset - field_layout.size,
271 self.latest_offset
272 );
273
274 padding_layout.map(|layout| self.padding_field(layout))
275 }
276
277 pub fn add_tail_padding(
278 &mut self,
279 comp_name: &str,
280 comp_layout: Layout,
281 ) -> Option<proc_macro2::TokenStream> {
282 // Only emit an padding field at the end of a struct if the
283 // user configures explicit padding.
284 if !self.ctx.options().force_explicit_padding {
285 return None;
286 }
287
288 // Padding doesn't make sense for rust unions.
289 if self.is_rust_union {
290 return None;
291 }
292
293 if self.latest_offset == comp_layout.size {
294 // This struct does not contain tail padding.
295 return None;
296 }
297
298 trace!(
299 "need a tail padding field for {}: offset {} -> size {}",
300 comp_name,
301 self.latest_offset,
302 comp_layout.size
303 );
304 let size = comp_layout.size - self.latest_offset;
305 Some(self.padding_field(Layout::new(size, 0)))
306 }
307
308 pub fn pad_struct(
309 &mut self,
310 layout: Layout,
311 ) -> Option<proc_macro2::TokenStream> {
312 debug!(
313 "pad_struct:\n\tself = {:#?}\n\tlayout = {:#?}",
314 self, layout
315 );
316
317 if layout.size < self.latest_offset {
318 warn!(
319 "Calculated wrong layout for {}, too more {} bytes",
320 self.name,
321 self.latest_offset - layout.size
322 );
323 return None;
324 }
325
326 let padding_bytes = layout.size - self.latest_offset;
327 if padding_bytes == 0 {
328 return None;
329 }
330
331 let repr_align = self.ctx.options().rust_features().repr_align;
332
333 // We always pad to get to the correct size if the struct is one of
334 // those we can't align properly.
335 //
336 // Note that if the last field we saw was a bitfield, we may need to pad
337 // regardless, because bitfields don't respect alignment as strictly as
338 // other fields.
339 if padding_bytes >= layout.align ||
340 (self.last_field_was_bitfield &&
341 padding_bytes >= self.latest_field_layout.unwrap().align) ||
342 (!repr_align && layout.align > MAX_GUARANTEED_ALIGN)
343 {
344 let layout = if self.is_packed {
345 Layout::new(padding_bytes, 1)
346 } else if self.last_field_was_bitfield ||
347 layout.align > MAX_GUARANTEED_ALIGN
348 {
349 // We've already given up on alignment here.
350 Layout::for_size(self.ctx, padding_bytes)
351 } else {
352 Layout::new(padding_bytes, layout.align)
353 };
354
355 debug!("pad bytes to struct {}, {:?}", self.name, layout);
356
357 Some(self.padding_field(layout))
358 } else {
359 None
360 }
361 }
362
363 pub fn requires_explicit_align(&self, layout: Layout) -> bool {
364 let repr_align = self.ctx.options().rust_features().repr_align;
365
366 // Always force explicit repr(align) for stuff more than 16-byte aligned
367 // to work-around https://github.com/rust-lang/rust/issues/54341.
368 //
369 // Worst-case this just generates redundant alignment attributes.
370 if repr_align && self.max_field_align >= 16 {
371 return true;
372 }
373
374 if self.max_field_align >= layout.align {
375 return false;
376 }
377
378 // We can only generate up-to a 8-bytes of alignment unless we support
379 // repr(align).
380 repr_align || layout.align <= MAX_GUARANTEED_ALIGN
381 }
382
383 fn padding_bytes(&self, layout: Layout) -> usize {
384 align_to(self.latest_offset, layout.align) - self.latest_offset
385 }
386
387 fn padding_field(&mut self, layout: Layout) -> proc_macro2::TokenStream {
388 let ty = helpers::blob(self.ctx, layout);
389 let padding_count = self.padding_count;
390
391 self.padding_count += 1;
392
393 let padding_field_name = Ident::new(
394 &format!("__bindgen_padding_{}", padding_count),
395 Span::call_site(),
396 );
397
398 self.max_field_align = cmp::max(self.max_field_align, layout.align);
399
400 quote! {
401 pub #padding_field_name : #ty ,
402 }
403 }
404
405 /// Returns whether the new field is known to merge with a bitfield.
406 ///
407 /// This is just to avoid doing the same check also in pad_field.
408 fn align_to_latest_field(&mut self, new_field_layout: Layout) -> bool {
409 if self.is_packed {
410 // Skip to align fields when packed.
411 return false;
412 }
413
414 let layout = match self.latest_field_layout {
415 Some(l) => l,
416 None => return false,
417 };
418
419 // If it was, we may or may not need to align, depending on what the
420 // current field alignment and the bitfield size and alignment are.
421 debug!(
422 "align_to_bitfield? {}: {:?} {:?}",
423 self.last_field_was_bitfield, layout, new_field_layout
424 );
425
426 // Avoid divide-by-zero errors if align is 0.
427 let align = cmp::max(1, layout.align);
428
429 if self.last_field_was_bitfield &&
430 new_field_layout.align <= layout.size % align &&
431 new_field_layout.size <= layout.size % align
432 {
433 // The new field will be coalesced into some of the remaining bits.
434 //
435 // FIXME(emilio): I think this may not catch everything?
436 debug!("Will merge with bitfield");
437 return true;
438 }
439
440 // Else, just align the obvious way.
441 self.latest_offset += self.padding_bytes(layout);
442 false
443 }
444}
445