Skip to content

Commit c0389c3

Browse files
author
bors-servo
authored
Auto merge of #736 - emilio:bitfields-revamp, r=fitzgen
ir: Fix a bunch of bitfield correctness issues. In particular, the "flush the allocation unit" logic is only valid for ms_structs (that is, MSVC). It's slightly annoying to have this different behavior, but it'd work just fine if we'd turn that on for MSVC. This patch doesn't do that, yet at least, and adds tests for all the weird bitfield alignments around. Fixes #726 (and another set of hidden issues by the old code).
2 parents 8a09347 + 10106aa commit c0389c3

File tree

10 files changed

+405
-195
lines changed

10 files changed

+405
-195
lines changed

bindgen-integration/cpp/Test.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,24 @@ Third::assert(int first, bool second, ItemKind third)
5656
kind == third;
5757
}
5858

59+
bool
60+
Fourth::assert(MyEnum tag, unsigned long ptr)
61+
{
62+
return this->tag == tag && this->ptr == ptr;
63+
}
64+
65+
bool
66+
Date2::assert(unsigned short nWeekDay,
67+
unsigned short nMonthDay,
68+
unsigned short nMonth,
69+
unsigned short nYear,
70+
unsigned short byte)
71+
{
72+
return this->nWeekDay == nWeekDay &&
73+
this->nMonthDay == nMonthDay &&
74+
this->nMonth == nMonth &&
75+
this->nYear == nYear &&
76+
this->byte == byte;
77+
}
78+
5979
} // namespace bitfields

bindgen-integration/cpp/Test.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ struct Second {
4949
};
5050

5151
enum ItemKind {
52-
ITEM_KIND_UNO,
52+
ITEM_KIND_UNO = 0,
5353
ITEM_KIND_DOS,
5454
ITEM_KIND_TRES,
5555
};
@@ -63,6 +63,35 @@ struct Third {
6363
bool assert(int first, bool second, ItemKind third);
6464
};
6565

66+
enum MyEnum {
67+
ONE = 0,
68+
TWO,
69+
THREE,
70+
FOUR,
71+
};
72+
73+
struct Fourth {
74+
MyEnum tag: 2;
75+
unsigned long ptr: 48;
76+
77+
/// Returns true if the bitfields match the arguments, false otherwise.
78+
bool assert(MyEnum tag, unsigned long ptr);
79+
};
80+
81+
struct Date2 {
82+
unsigned short nWeekDay : 3; // 0..7 (3 bits)
83+
unsigned short nMonthDay : 6; // 0..31 (6 bits)
84+
unsigned short nMonth : 5; // 0..12 (5 bits)
85+
unsigned short nYear : 8; // 0..100 (8 bits)
86+
unsigned char byte : 8;
87+
88+
bool assert(unsigned short nWeekDay,
89+
unsigned short nMonthDay,
90+
unsigned short nMonth,
91+
unsigned short nYear,
92+
unsigned short byte);
93+
};
94+
6695
} // namespace bitfields
6796

6897
struct AutoRestoreBool {

bindgen-integration/src/lib.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,46 @@ fn test_bitfields_third() {
102102
});
103103
}
104104

105+
#[test]
106+
fn test_bitfields_fourth() {
107+
let mut fourth: bindings::bitfields::Fourth = unsafe {
108+
mem::zeroed()
109+
};
110+
assert!(unsafe {
111+
fourth.assert(bindings::bitfields::MyEnum::ONE, 0)
112+
});
113+
114+
fourth.set_tag(bindings::bitfields::MyEnum::THREE);
115+
fourth.set_ptr(0xdeadbeef);
116+
assert!(unsafe {
117+
fourth.assert(bindings::bitfields::MyEnum::THREE, 0xdeadbeef)
118+
});
119+
}
120+
121+
#[test]
122+
fn test_bitfields_date2() {
123+
let mut date: bindings::bitfields::Date2 = unsafe {
124+
mem::zeroed()
125+
};
126+
assert!(unsafe {
127+
date.assert(0, 0, 0, 0, 0)
128+
});
129+
130+
date.set_nWeekDay(6); // saturdays are the best
131+
date.set_nMonthDay(20);
132+
date.set_nMonth(11);
133+
date.set_nYear(95);
134+
date.set_byte(255);
135+
assert!(unsafe {
136+
date.assert(6, 20, 11, 95, 255)
137+
});
138+
}
139+
105140
#[test]
106141
fn test_bitfield_constructors() {
142+
use std::mem;
107143
let mut first = bindings::bitfields::First {
108-
_bitfield_1: bindings::bitfields::First::new_bitfield_1(1),
109-
_bitfield_2: bindings::bitfields::First::new_bitfield_2(2, 3),
144+
_bitfield_1: unsafe { mem::transmute(bindings::bitfields::First::new_bitfield_1(1, 2, 3)) },
110145
__bindgen_align: [],
111146
};
112147
assert!(unsafe {

src/ir/comp.rs

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -490,49 +490,67 @@ fn bitfields_to_allocation_units<E, I>(ctx: &BindgenContext,
490490
let mut unit_align = 0;
491491
let mut bitfields_in_unit = vec![];
492492

493+
// TODO(emilio): Determine this from attributes or pragma ms_struct
494+
// directives. Also, perhaps we should check if the target is MSVC?
495+
const is_ms_struct: bool = false;
496+
493497
for bitfield in raw_bitfields {
494498
let bitfield_width = bitfield.bitfield().unwrap() as usize;
495-
let bitfield_align = ctx.resolve_type(bitfield.ty())
496-
.layout(ctx)
497-
.expect("Bitfield without layout? Gah!")
498-
.align;
499-
500-
if unit_size_in_bits != 0 &&
501-
(bitfield_width == 0 ||
502-
bitfield_width > unfilled_bits_in_unit) {
503-
// We've reached the end of this allocation unit, so flush it
504-
// and its bitfields.
505-
unit_size_in_bits = align_to(unit_size_in_bits,
506-
bitfield_align);
507-
flush_allocation_unit(fields,
508-
bitfield_unit_count,
509-
unit_size_in_bits,
510-
unit_align,
511-
mem::replace(&mut bitfields_in_unit, vec![]));
512-
513-
// Now we're working on a fresh bitfield allocation unit, so reset
514-
// the current unit size and alignment.
515-
unit_size_in_bits = 0;
516-
unit_align = 0;
499+
let bitfield_layout =
500+
ctx.resolve_type(bitfield.ty())
501+
.layout(ctx)
502+
.expect("Bitfield without layout? Gah!");
503+
let bitfield_size = bitfield_layout.size;
504+
let bitfield_align = bitfield_layout.align;
505+
506+
let mut offset = unit_size_in_bits;
507+
if is_ms_struct {
508+
if unit_size_in_bits != 0 &&
509+
(bitfield_width == 0 ||
510+
bitfield_width > unfilled_bits_in_unit) {
511+
// We've reached the end of this allocation unit, so flush it
512+
// and its bitfields.
513+
unit_size_in_bits = align_to(unit_size_in_bits, unit_align * 8);
514+
flush_allocation_unit(fields,
515+
bitfield_unit_count,
516+
unit_size_in_bits,
517+
unit_align,
518+
mem::replace(&mut bitfields_in_unit, vec![]));
519+
520+
// Now we're working on a fresh bitfield allocation unit, so reset
521+
// the current unit size and alignment.
522+
#[allow(unused_assignments)]
523+
{
524+
unit_size_in_bits = 0;
525+
offset = 0;
526+
unit_align = 0;
527+
}
528+
}
529+
} else {
530+
if offset != 0 &&
531+
(bitfield_width == 0 ||
532+
(offset & (bitfield_align * 8 - 1)) + bitfield_width > bitfield_size * 8) {
533+
offset = align_to(offset, bitfield_align * 8);
534+
}
517535
}
518536

519537
// Only keep named bitfields around. Unnamed bitfields (with > 0
520538
// bitsize) are used for padding. Because the `Bitfield` struct stores
521539
// the bit-offset into its allocation unit where its bits begin, we
522540
// don't need any padding bits hereafter.
523541
if bitfield.name().is_some() {
524-
bitfields_in_unit.push(Bitfield::new(unit_size_in_bits, bitfield));
542+
bitfields_in_unit.push(Bitfield::new(offset, bitfield));
525543
}
526544

527-
unit_size_in_bits += bitfield_width;
528-
529545
max_align = cmp::max(max_align, bitfield_align);
530546

531547
// NB: The `bitfield_width` here is completely, absolutely intentional.
532548
// Alignment of the allocation unit is based on the maximum bitfield
533549
// width, not (directly) on the bitfields' types' alignment.
534550
unit_align = cmp::max(unit_align, bitfield_width);
535551

552+
unit_size_in_bits = offset + bitfield_width;
553+
536554
// Compute what the physical unit's final size would be given what we
537555
// have seen so far, and use that to compute how many bits are still
538556
// available in the unit.

0 commit comments

Comments
 (0)