@@ -21,6 +21,7 @@ pub struct StructLayoutTracker<'a, 'ctx: 'a> {
21
21
padding_count : usize ,
22
22
latest_field_layout : Option < Layout > ,
23
23
max_field_align : usize ,
24
+ last_field_was_bitfield : bool ,
24
25
}
25
26
26
27
/// Returns a size aligned to a given value.
@@ -37,8 +38,17 @@ pub fn align_to(size: usize, align: usize) -> usize {
37
38
size + align - rem
38
39
}
39
40
41
+ /// Returns the amount of bytes from a given amount of bytes, rounding up.
42
+ pub fn bytes_from_bits ( n : usize ) -> usize {
43
+ if n % 8 == 0 {
44
+ return n / 8 ;
45
+ }
46
+
47
+ n / 8 + 1
48
+ }
49
+
40
50
/// Returns the lower power of two byte count that can hold at most n bits.
41
- pub fn bytes_from_bits ( mut n : usize ) -> usize {
51
+ pub fn bytes_from_bits_pow2 ( mut n : usize ) -> usize {
42
52
if n == 0 {
43
53
return 0 ;
44
54
}
@@ -63,6 +73,20 @@ fn test_align_to() {
63
73
assert_eq ! ( align_to( 17 , 4 ) , 20 ) ;
64
74
}
65
75
76
+ #[ test]
77
+ fn test_bytes_from_bits_pow2 ( ) {
78
+ assert_eq ! ( bytes_from_bits_pow2( 0 ) , 0 ) ;
79
+ for i in 1 ..9 {
80
+ assert_eq ! ( bytes_from_bits_pow2( i) , 1 ) ;
81
+ }
82
+ for i in 9 ..17 {
83
+ assert_eq ! ( bytes_from_bits_pow2( i) , 2 ) ;
84
+ }
85
+ for i in 17 ..33 {
86
+ assert_eq ! ( bytes_from_bits_pow2( i) , 4 ) ;
87
+ }
88
+ }
89
+
66
90
#[ test]
67
91
fn test_bytes_from_bits ( ) {
68
92
assert_eq ! ( bytes_from_bits( 0 ) , 0 ) ;
@@ -72,8 +96,8 @@ fn test_bytes_from_bits() {
72
96
for i in 9 ..17 {
73
97
assert_eq ! ( bytes_from_bits( i) , 2 ) ;
74
98
}
75
- for i in 17 ..33 {
76
- assert_eq ! ( bytes_from_bits( i) , 4 ) ;
99
+ for i in 17 ..25 {
100
+ assert_eq ! ( bytes_from_bits( i) , 3 ) ;
77
101
}
78
102
}
79
103
@@ -86,6 +110,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
86
110
padding_count : 0 ,
87
111
latest_field_layout : None ,
88
112
max_field_align : 0 ,
113
+ last_field_was_bitfield : false ,
89
114
}
90
115
}
91
116
@@ -97,26 +122,33 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
97
122
}
98
123
99
124
pub fn saw_base ( & mut self , base_ty : & Type ) {
100
- self . align_to_latest_field ( ) ;
101
-
102
125
if let Some ( layout) = base_ty. layout ( self . ctx ) {
126
+ self . align_to_latest_field ( layout) ;
127
+
103
128
self . latest_offset += self . padding_bytes ( layout) + layout. size ;
104
129
self . latest_field_layout = Some ( layout) ;
105
130
self . max_field_align = cmp:: max ( self . max_field_align , layout. align ) ;
106
131
}
107
132
}
133
+
108
134
pub fn saw_bitfield_batch ( & mut self , layout : Layout ) {
109
- self . align_to_latest_field ( ) ;
135
+ self . align_to_latest_field ( layout) ;
136
+
137
+ self . latest_offset += layout. size ;
138
+
139
+ debug ! ( "Offset: <bitfield>: {} -> {}" ,
140
+ self . latest_offset - layout. size,
141
+ self . latest_offset) ;
110
142
111
- self . latest_offset += self . padding_bytes ( layout) + layout. size ;
112
143
self . latest_field_layout = Some ( layout) ;
144
+ self . last_field_was_bitfield = true ;
113
145
// NB: We intentionally don't update the max_field_align here, since our
114
146
// bitfields code doesn't necessarily guarantee it, so we need to
115
147
// actually generate the dummy alignment.
116
148
}
117
149
118
150
pub fn saw_union ( & mut self , layout : Layout ) {
119
- self . align_to_latest_field ( ) ;
151
+ self . align_to_latest_field ( layout ) ;
120
152
121
153
self . latest_offset += self . padding_bytes ( layout) + layout. size ;
122
154
self . latest_field_layout = Some ( layout) ;
@@ -151,7 +183,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
151
183
}
152
184
}
153
185
154
- self . align_to_latest_field ( ) ;
186
+ let will_merge_with_bitfield = self . align_to_latest_field ( field_layout ) ;
155
187
156
188
let padding_layout = if self . comp . packed ( ) {
157
189
None
@@ -160,17 +192,19 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
160
192
Some ( offset) if offset / 8 > self . latest_offset => {
161
193
offset / 8 - self . latest_offset
162
194
}
163
- _ if field_layout. align == 0 => 0 ,
164
- _ => {
165
- self . padding_bytes ( field_layout)
166
- }
195
+ _ if will_merge_with_bitfield || field_layout. align == 0 => 0 ,
196
+ _ => self . padding_bytes ( field_layout) ,
167
197
} ;
168
198
169
199
// Otherwise the padding is useless.
170
200
let need_padding = padding_bytes >= field_layout. align ;
171
201
172
202
self . latest_offset += padding_bytes;
173
203
204
+ debug ! ( "Offset: <padding>: {} -> {}" ,
205
+ self . latest_offset - padding_bytes,
206
+ self . latest_offset) ;
207
+
174
208
debug ! ( "align field {} to {}/{} with {} padding bytes {:?}" ,
175
209
field_name,
176
210
self . latest_offset,
@@ -188,14 +222,22 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
188
222
self . latest_offset += field_layout. size ;
189
223
self . latest_field_layout = Some ( field_layout) ;
190
224
self . max_field_align = cmp:: max ( self . max_field_align , field_layout. align ) ;
225
+ self . last_field_was_bitfield = false ;
226
+
227
+ debug ! ( "Offset: {}: {} -> {}" ,
228
+ field_name,
229
+ self . latest_offset - field_layout. size,
230
+ self . latest_offset) ;
191
231
192
232
padding_layout. map ( |layout| self . padding_field ( layout) )
193
233
}
194
234
195
235
pub fn pad_struct ( & mut self , layout : Layout ) -> Option < ast:: StructField > {
196
236
if layout. size < self . latest_offset {
197
- warn ! ( "calculate struct layout incorrect, too more {} bytes" ,
198
- self . latest_offset - layout. size) ;
237
+ if cfg ! ( debug_assertions) {
238
+ panic ! ( "calculate struct layout incorrect, too more {} bytes" ,
239
+ self . latest_offset - layout. size) ;
240
+ }
199
241
200
242
return None
201
243
}
@@ -204,12 +246,19 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
204
246
205
247
// We always pad to get to the correct size if the struct is one of
206
248
// those we can't align properly.
249
+ //
250
+ // Note that if the last field we saw was a bitfield, we may need to pad
251
+ // regardless, because bitfields don't respect alignment as strictly as
252
+ // other fields.
207
253
if padding_bytes > 0 &&
208
254
( padding_bytes >= layout. align ||
255
+ ( self . last_field_was_bitfield &&
256
+ padding_bytes >= self . latest_field_layout . unwrap ( ) . align ) ||
209
257
layout. align > mem:: size_of :: < * mut ( ) > ( ) ) {
210
258
let layout = if self . comp . packed ( ) {
211
259
Layout :: new ( padding_bytes, 1 )
212
- } else if layout. align > mem:: size_of :: < * mut ( ) > ( ) {
260
+ } else if self . last_field_was_bitfield ||
261
+ layout. align > mem:: size_of :: < * mut ( ) > ( ) {
213
262
// We've already given up on alignment here.
214
263
Layout :: for_size ( padding_bytes)
215
264
} else {
@@ -252,11 +301,37 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
252
301
StructFieldBuilder :: named ( padding_field_name) . pub_ ( ) . build_ty ( ty)
253
302
}
254
303
255
- fn align_to_latest_field ( & mut self ) {
304
+ /// Returns whether the new field is known to merge with a bitfield.
305
+ ///
306
+ /// This is just to avoid doing the same check also in pad_field.
307
+ fn align_to_latest_field ( & mut self , new_field_layout : Layout ) -> bool {
256
308
if self . comp . packed ( ) {
257
- // skip to align field when packed
258
- } else if let Some ( layout) = self . latest_field_layout . take ( ) {
259
- self . latest_offset += self . padding_bytes ( layout) ;
309
+ // Skip to align fields when packed.
310
+ return false ;
260
311
}
312
+
313
+ let layout = match self . latest_field_layout {
314
+ Some ( l) => l,
315
+ None => return false ,
316
+ } ;
317
+
318
+ // If it was, we may or may not need to align, depending on what the
319
+ // current field alignment and the bitfield size and alignment are.
320
+ debug ! ( "align_to_bitfield? {}: {:?} {:?}" , self . last_field_was_bitfield,
321
+ layout, new_field_layout) ;
322
+
323
+ if self . last_field_was_bitfield &&
324
+ new_field_layout. align <= layout. size % layout. align &&
325
+ new_field_layout. size <= layout. size % layout. align {
326
+ // The new field will be coalesced into some of the remaining bits.
327
+ //
328
+ // FIXME(emilio): I think this may not catch everything?
329
+ debug ! ( "Will merge with bitfield" ) ;
330
+ return true ;
331
+ }
332
+
333
+ // Else, just align the obvious way.
334
+ self . latest_offset += self . padding_bytes ( layout) ;
335
+ return false ;
261
336
}
262
337
}
0 commit comments