-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rustc_codegen_llvm: don't assume offsets are always aligned. #53998
Conversation
r? @oli-obk (rust_highfive has picked a reviewer for you, use r? to override) |
@@ -174,7 +174,12 @@ impl PlaceRef<'ll, 'tcx> { | |||
let cx = bx.cx; | |||
let field = self.layout.field(cx, ix); | |||
let offset = self.layout.fields.offset(ix); | |||
let align = self.align.min(self.layout.align).min(field.align); | |||
let mut real_field_align = self.align.min(self.layout.align).min(field.align); | |||
if offset.abi_align(real_field_align) > offset { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this code is already duplicated twice in rustc and will get a third duplication in miri, I think this warrants a method on Align
taking a field alignment and a field offset and producing the real alignment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not enough, sadly, unless you mean starting from self.align.min(self.layout.align)
and then using the method on that. I think I like that, and I can fix the FIXME
too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that's what I meant.
@oli-obk Does this new approach work for you? |
@bors r+ the math checks out, yes |
📌 Commit b9e7574 has been approved by |
@@ -174,7 +174,10 @@ impl PlaceRef<'ll, 'tcx> { | |||
let cx = bx.cx; | |||
let field = self.layout.field(cx, ix); | |||
let offset = self.layout.fields.offset(ix); | |||
let align = self.align.min(self.layout.align).min(field.align); | |||
let effective_field_align = self.align |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't field.align
set up properly here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok.
let align = self.align.min(self.layout.align).min(field.align); | ||
let effective_field_align = self.align | ||
.min(self.layout.align) | ||
.min(field.align) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might help #54028 to remove this line, and rely strictly on the alignment of the type, combined with the best-case alignment for the offset. That way, we can hint LLVM about alignments that are higher than it can figure out by itself. Do we want to do that and do we expect a performance improvement?
rustc_codegen_llvm: don't assume offsets are always aligned. Fixes rust-lang#53728 by taking into account not just overall type alignment and the field's alignment when determining whether a field is aligned or not ("packed"), but also the field's offset within the type. Previously, rustc assumed that the offset was always at least as aligned as `min(struct.align, field.align)`. However, there's no real reason to have that assumption, and it obviously can't always be true after we implement `#[repr(align(N), pack(K))]`. There's also a case today where that assumption is not true, involving niche discriminants in enums: Suppose that we have the code in rust-lang#53728: ```Rust #[repr(u16)] enum DeviceKind { Nil = 0, } #[repr(packed)] struct DeviceInfo { endianness: u8, device_kind: DeviceKind, } struct Wrapper { device_info: DeviceInfo, data: u32 } ``` Observe the layout of `Option<Wrapper>`. It has an alignment of 4 because of the `u32`. `device_info.device_kind` is a good niche field to use, which means the enum ends up with this layout: ``` size = 8 align = 4 fields = [ { offset=1, type=u16 } // discriminant, .<Some>.device_info.device_kind ] ``` And here we have an discriminant with alignment 2 (`u16`) but offset 1.
rustc_codegen_llvm: don't assume offsets are always aligned. Fixes #53728 by taking into account not just overall type alignment and the field's alignment when determining whether a field is aligned or not ("packed"), but also the field's offset within the type. Previously, rustc assumed that the offset was always at least as aligned as `min(struct.align, field.align)`. However, there's no real reason to have that assumption, and it obviously can't always be true after we implement `#[repr(align(N), pack(K))]`. There's also a case today where that assumption is not true, involving niche discriminants in enums: Suppose that we have the code in #53728: ```Rust #[repr(u16)] enum DeviceKind { Nil = 0, } #[repr(packed)] struct DeviceInfo { endianness: u8, device_kind: DeviceKind, } struct Wrapper { device_info: DeviceInfo, data: u32 } ``` Observe the layout of `Option<Wrapper>`. It has an alignment of 4 because of the `u32`. `device_info.device_kind` is a good niche field to use, which means the enum ends up with this layout: ``` size = 8 align = 4 fields = [ { offset=1, type=u16 } // discriminant, .<Some>.device_info.device_kind ] ``` And here we have an discriminant with alignment 2 (`u16`) but offset 1.
☀️ Test successful - status-appveyor, status-travis |
@eddyb so you mention on Discord that miri might also need a fix here. Do you have a testcase to check? I assume we'd currently throw an "unaligned access" error where we should not. |
miri: correctly compute expected alignment for field This is the miri version of rust-lang#53998. A test is added by rust-lang/miri#457. r? @eddyb
add some interesting tests for alignment corner cases `strange_enum_discriminant_offset` example found in rust-lang/rust#53998.
add some interesting tests for alignment corner cases `strange_enum_discriminant_offset` example found in rust-lang/rust#53998.
add some interesting tests for alignment corner cases `strange_enum_discriminant_offset` example found in rust-lang#53998.
Fixes #53728 by taking into account not just overall type alignment and the field's alignment when determining whether a field is aligned or not ("packed"), but also the field's offset within the type.
Previously, rustc assumed that the offset was always at least as aligned as
min(struct.align, field.align)
. However, there's no real reason to have that assumption, and it obviously can't always be true after we implement#[repr(align(N), pack(K))]
. There's also a case today where that assumption is not true, involving niche discriminants in enums:Suppose that we have the code in #53728:
Observe the layout of
Option<Wrapper>
. It has an alignment of 4 because of theu32
.device_info.device_kind
is a good niche field to use, which means the enum ends up with this layout:And here we have an discriminant with alignment 2 (
u16
) but offset 1.