-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Separate size and stride for types #1397
Comments
There's some discussion about this in rust-lang/rust#28951 |
I like how Swift has implemented various features we've only discussed. |
Neither of the following must become UB due to changes made to object layout. fn dummy(p: &T) {
unsafe {
// getelementptr inbounds
p.offset(1);
}
}
unsafe fn get_second(p: *const T) -> T {
ptr::read(p.offset(1))
} |
@mahkoh |
I think this is easily solved by requiring memory allocation to use the aligned size of an object instead of the packed size. That way |
@eddyb: Then the object cannot be smaller than the aligned_size_of because otherwise the first function is UB. @Amanieu: The first function can take a reference to |
@eddyb: Ah, I see what you meant. I'll update the code above. |
And to bring fn dummy2(p: &T) {
unsafe {
// getelementptr inbounds
(p as *const _ as *const u8).offset(mem::size_of::<T>());
}
} must not be UB either. |
@mahkoh: This isn't UB because, from my understanding, |
Still, this optimization seems to be impossible due to the issue mentioned in the OP:
The user is actually guaranteed this property for
While |
Ah, I had overlooked this issue, but this was one of the things I was thinking about while writing the Allocator RFC (#1398). I believe the (Update: in fact I had already commented (rust-lang/rust#28951 (comment)) about having thoughts related to this topic, though that comment is quite wishy-washy about what direction we should go in...) |
(nominating because I think the lang team should be aware of the question here.) |
It looks like even arrays Making arrays to follow the same rule has benefits of making |
The first step towards supporting this would be to add |
Funnily enough, it seems this would actually break the code I have written for atomic-rs. This function might overwrite an adjacent value if it is embedded in the padding of the atomic object (which is just an The fix would be to use |
As pointed out in #1582, this would allow us to change the layout of tuples so that a tuple can be destructured into a single element and the rest of the tuple while still retaining a fairly space-efficient layout. That would, in turn, make it easier to support variadic generics. |
My feeling is that inhibiting writing a 3-byte struct like However, I don't have a great feeling for the number of actual odd-size structs, nor the number of nested ones, nor how often they hit memory. I guess one possibility for collecting data would be instrumenting the compiler to report/record this sort of info during compilation and run it on some programs (e.g. the bootstrap, servo, some things from the rest of the ecosystem) to get an idea (getting an idea for number of writes would require runtime instrumentation, I suppose). Theoretically it might good to also know about this in the precise of layout optimisations that reorder fields (e.g. |
I just noticed a slight flaw with this scheme regarding DSTs. If the last element in a struct is even potentially unsized then it must remain the last element in the struct. For example consider This means that making a type support unsized type parameters with |
@Amanieu can't the layout vary based on the choice of types that the struct is instantiated with, and thus the constraints imposed on instantiations with an unsized type won't affect instantiations with all sized types? |
@pnkfelix No. Look at the example I gave in my comment:
Because |
@Amanieu oh, sorry for not reading carefully enough |
This is basically rust-lang/rust#17027 |
This would also help C++ interop. In C++20, C++ added the (In practice, this is not actually every type. Some types have no tail padding – for instance, If we separated out Because this seems likely to be important for C++ interop, I'd be interested in taking this on / pushing this forward, but I don't really know what the next steps are. There is this issue in the RFCs repo, but there is not an RFC markdown file. Is the next step to write up a concrete proposal + rationale as a markdown file, following the RFC process? |
Note that this would have to be done in a way that is backwards-compatible, and it should not be a pessimization. Both of make this very difficult. Concretely we do need to keep that In terms of your question, yes, the next steps would be either:
|
The Swift ABI defines an interesting struct layout which defines the size of a type separately from its stride:
While rust doesn't define a stable ABI yet, I think using such a layout would be a breaking change due to assumptions that unsafe code make about
mem::size_of
.In particular, unsafe code might assume that, when given a pointer
let x: *mut T
, all bytes fromx
tox + mem::size_of::<T>()
can safely be overwritten. This would be incorrect when the padding bytes at the end ofT
contain fields of an outer struct.Is such an optimization worth considering? From a quick look at the rust source, it seems that all uses of
mem::size_of
will continue to work fine if we define it as returning the stride (aligned size). I haven't looked at any external crates yet but I think they would continue to work fine as well. In any case this would be a breaking change and should be considered carefully.The text was updated successfully, but these errors were encountered: