-
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
When DST struct's last field is misaligned, memory access generated are for the wrong address #26403
Comments
cc @rust-lang/compiler |
Hmm, this seems tricky to solve properly, I see three options. 1.Force word alignment on unsized fields. This is the easiest, but won't work for types with higer alignment due to SIMD or similar. |
@Aatch the preferred solution involves right-aligning fields before the unsized one (as opposed to left-aligning them) and making pointers to the structure point to the field directly. That and your 3. are the only two correct solutions I know of. |
Worth noting that tuples have their last field ?Sized, so any decision here may affect them too. |
@gankro you sure? I'd be very surprised if that were the case, as I've never seen any compiler code supporting unsized tuples (or enums, for that matter). |
@gankro @eddyb seems to be right: https://play.rust-lang.org/?gist=38f03e1c1ed02dafffa9&version=nightly |
https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md#coercions specified this (last bullet of "coerce_inner"). https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md does not appear to overturn this decision. If it doesn't work, it's a bug. |
@gankro 401 is obsolete wrt unsizing. 982 specifies |
Works for me 👍 |
@Aatch wrote (the third of three options):
Just to be clear: am I correct that the runtime calculation is only necessary when the type I ask because that scenario does not sound so dire to me. |
@pnkfelix Yes, it's only a performance regression for The cheaper solution involves pointing, e.g. But since struct layout is not specified (and That said, even if an implementation of @Aatch's 3rd option is likely to be merged, it's still somewhat low-priority, with I expect the 16-byte SIMD alignments to mess things up, but on x64 that only can happen if the allocation itself is not aligned to 16 bytes, as the |
@eddyb yes I would normally be inclined to agree that it is low-priority; I came and looked at this based on @eefriedman 's linking #27023 here ... but I'm not really sure I agree these are fundamentally the same bug. (That is, it seems like a fix for #27023 can happen independently, and sooner, than the fix for #26403 ...) |
(on further reflection, I think I can see why @eefriedman made the connection between the two bugs -- it seems like the most robust approach to resolving bugs like #27023 may require a fix to this bug as well. Having said that, I'm pretty sure that #27023 can be isolated and fixed on its own.) |
@pnkfelix just to show that a runtime calculation would only affect things with data before the unsized field:
If offset is 0 then:
So while it's easy enough to just special-case the codegen for the 0-offset case, in theory, LLVM would be able to constant-fold and inst-simplify it away anyway. One thing idea I did come up with to try and reduce the performance hit would be to take advantage of branch prediction and do something like this: let alignment = get_alignment(val);
let offset = if likely(alignment == word_align) {
calc_offset(initial_offset, word_align)
} else {
calc_offset(initial_offset, alignment)
}; Where |
There's another option I think: the compiler could generate a separate vtable for each incorrect alignment of a trait object, whose entries point to thunks which correctly align the The downside is that there would potentially be multiple vtables per trait/type combination (up to one per trait/type/alignment combination). On the other hand, it's simple and fast, and it should be a rare enough occurence to not be a problem in terms of memory usage. |
@Diggsey I think you mean "up to one per trait/type/alignment combination". |
@eddyb Yes Actually, it's possible to improve on that idea: you only need up to two vtables per trait/type combination. One for when the alignment is correct, one for when it's incorrect. Also, you don't need separate thunks: prefix each trait method implementation with an instruction sequence which aligns the |
As a temporary measure, could we force maximal alignment on unsized fields? That is, for each platform define a constant that holds the largest alignment required for a type (SIMD or otherwise), and then force all unsized fields to have that alignment. We could then improve the efficiency of the alignment with more complicated things in future versions. In particular, I am hoping that |
I think long term, I favor either dynamic computation or right-justification (the former, dynamic computation, is what @nrc and I settled on when he was doing the initial implementation, but I guess the work never got done.) Using "maximal" alignment (really: SIMD alignment) on (potentially) DST-ified fields may be plausible, but it is a bit tricky. I guess the question of tuples is an important one. That is, we have to see how much we can identify the set of fields that we would have to align this way, because when you are creating a struct initially you do not necessarily know whether it will become DST-ified after the fact. So you have to conservatively bump up the alignment for the fields all the time. I am curious to see how hard it would be to implement the dynamic computation: in principle it seems fairly straightforward, possibly easier than "maximal" alignment even. Though introducing the notion that alignment may or may not be dynamically computed is sure to be something of a pain (brings back bad memories of the pre-monomorphization days, though obviously this is a significantly more limited change). |
There's no such thing as maximal alignment with |
However, it was pointed out that this may, indeed, be fixed -- or at least On Thu, Nov 5, 2015 at 12:47 PM, Alexis Beingessner <
|
OK, just tested --- not completely fixed. But some of the pieces are there! On Thu, Nov 5, 2015 at 1:56 PM, Nicholas Matsakis nmatsakis@mozilla.com
|
triage: I-nominated (seems like something good to at least have categorized) |
triage: P-medium |
We decided that implementing dynamic offset calculations is the best option. |
I think we should implement the dynamic solution now. It seems to be the only solution that works for all existing code. @eddyb's idea of adding a pivot field (and right-justifying the items before it) is a nifty optimization, but it requires an annotation or opt-in on the |
(Making pivot field implicit would probably not handle |
Can you clarify why it would be an opt in? I don't see what the breaking change is, unless people are blindly transmuting Rc's, which is blatant UB. |
I have implemented the dynamic solution, wasn't too hard to do. I just need to write some tests before making a PR. |
DST fields, being of an unknown type, are not automatically aligned properly, so a pointer to the field needs to be aligned using the information in the vtable. Fixes rust-lang#26403 and a number of other DST-related bugs discovered while implementing this.
Fixes #26403 This adjusts the pointer, if needed, to the correct alignment by using the alignment information in the vtable. Handling zero might not be necessary, as it shouldn't actually occur. I've left it as it's own commit so it can be removed fairly easily if people don't think it's worth doing. The way it's handled though means that there shouldn't be much impact on performance.
@gankro the |
The PR referenced above was merged, so this issue can probably be closed |
Oh, whoops, it is already closed. Carry on :-) |
[This issue](rust-lang/rust#26403) was fixed quite some time ago. The warning should no longer be necessary.
Example
in playpen
The text was updated successfully, but these errors were encountered: