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 proc_macro2::{self, Ident, Span}; |
10 | use std::cmp; |
11 | |
12 | const MAX_GUARANTEED_ALIGN: usize = 8; |
13 | |
14 | /// Trace the layout of struct. |
15 | #[derive (Debug)] |
16 | pub 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. |
32 | pub 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. |
46 | pub 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 ] |
63 | fn 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 ] |
72 | fn 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 | |
85 | impl<'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 | |