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