From 0ed16cd15b93e62fc12f79fd77e56c2176e73b24 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 9 Nov 2015 15:26:49 -0800 Subject: [PATCH 1/5] RFC: Add #[repr(align = "N")] Extend the existing `#[repr]` attribute on structs with an `align = "N"` option to specify a custom alignment for `struct` types. [Rendered][link] [link]: https://github.com/alexcrichton/rfcs/blob/repr-align/text/0000-repr-align.md --- text/0000-repr-align.md | 104 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 text/0000-repr-align.md diff --git a/text/0000-repr-align.md b/text/0000-repr-align.md new file mode 100644 index 00000000000..16d6e734957 --- /dev/null +++ b/text/0000-repr-align.md @@ -0,0 +1,104 @@ +- Feature Name: `repr_align` +- Start Date: 2015-11-09 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Extend the existing `#[repr]` attribute on structs with an `align = "N"` option +to specify a custom alignment for `struct` types. + +# Motivation +[motivation]: #motivation + +The alignment of a type is normally not worried about as the compiler will "do +the right thing" of picking an appropriate alignment for general use cases. +There are situations, however, where a nonstandard alignment may be desired when +operating with foreign systems. For example these sorts of situations tend to +necessitate or be much easier with a custom alignment: + +* Hardware can often have obscure requirements such as "this structure is + aligned to 32 bytes" when it in fact is only composed of 4-byte values. While + this can typically be manually calculated and managed, it's often also useful + to express this as a property of a type to get the compiler to do a little + extra work instead. +* C compilers like gcc and clang offer the ability to specify a custom alignment + for structures, and Rust can much more easily interoperate with these types if + Rust can also mirror the request for a custom alignment (e.g. passing a + structure to C correctly is much easier). +* Custom alignment can often be used for various tricks here and there and is + often convenient as "let's play around with an implementation" tool. For + example this can be used to statically allocate page tables in a kernel + or create an at-least cache-line-sized structure easily for concurrent + programming. + +Currently these sort of situations are possible in Rust but aren't necessarily +the most ergonomic as programmers must manually manage alignment. The purpose of +this RFC is to provide a lightweight annotation to alter the compiler-inferred +alignment of a structure to enable these situations much more easily. + +# Detailed design +[design]: #detailed-design + +The `#[repr]` attribute on `struct`s will be extended to include a form such as: + +```rust +#[repr(align = "16")] +struct MoreAligned(i32); +``` + +This structure will still have an alignment of 16 (as returned by +`mem::align_of`), and in this case the size will also be 16. + +Syntactically, the `repr` meta list will be extended to accept a meta item +name/value pair with the name "align" and the value as a string which can be +parsed as a `u64`. The restrictions on where this attribute can be placed along +with the accepted values are: + +* Custom alignment can only be specified on `struct` declarations for now. + Specifying a different alignment on perhaps `enum` or `type` definitions + should be a backwards-compatible extension. +* Alignment values must be a power of two. + +A custom alignment cannot *decrease* the alignment of a structure unless it is +also declared with `#[repr(packed)]` (to mirror what C does in this regard), but +it can increase the alignment (and hence size) of a structure (as shown +above). + +Semantically, it will be guaranteed (modulo `unsafe` code) that custom alignment +will always be respected. If a pointer to a non-aligned structure exists and is +used then it is considered unsafe behavior. Local variables, objects in arrays, +statics, etc, will all respect the custom alignment specified for a type. + +# Drawbacks +[drawbacks]: #drawbacks + +Specifying a custom alignment isn't always necessarily easy to do so via a +literal integer value. It may require usage of `#[cfg_attr]` in some situations +and may otherwise be much more convenient to name a different type instead. +Working with a raw integer, however, should provide the building block for +building up other abstractions and should be maximally flexible. It also +provides a relatively straightforward implementation and understanding of the +attribute at hand. + +This also currently does not allow for specifying the custom alignment of a +struct field (as C compilers also allow doing) without the usage of a newtype +structure. Currently `#[repr]` is not recognized here, but it would be a +backwards compatible extension to start reading it on struct fields. + +# Alternatives +[alternatives]: #alternatives + +Instead of using the `#[repr]` attribute as the "house" for the custom +alignment, there could instead be a new `#[align = "..."]` attribute. This is +perhaps more extensible to alignment in other locations such as a local variable +(with attributes on expressions), a struct field (where `#[repr]` is more of an +"outer attribute"), or enum variants perhaps. + +# Unresolved questions +[unresolved]: #unresolved-questions + +* It is likely best to simply match the semantics of C/C++ in the regard of + custom alignment, but is it ensured that this RFC is the same as the behavior + of standard C compilers? From 1b9be7363d8f91db6e60a9189a78cefdd5c41986 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 10:20:51 -0700 Subject: [PATCH 2/5] Update with a simple interaction with #[repr(packed)] --- text/0000-repr-align.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/text/0000-repr-align.md b/text/0000-repr-align.md index 16d6e734957..3dfc81f9f74 100644 --- a/text/0000-repr-align.md +++ b/text/0000-repr-align.md @@ -61,16 +61,20 @@ with the accepted values are: should be a backwards-compatible extension. * Alignment values must be a power of two. -A custom alignment cannot *decrease* the alignment of a structure unless it is -also declared with `#[repr(packed)]` (to mirror what C does in this regard), but -it can increase the alignment (and hence size) of a structure (as shown -above). +Multiple `#[repr(align = "..")]` directives are accepted on a struct +declaration, and the actual alignment of the structure will be the maximum of +all `align` directives and the natural alignment of the struct itself. Semantically, it will be guaranteed (modulo `unsafe` code) that custom alignment will always be respected. If a pointer to a non-aligned structure exists and is used then it is considered unsafe behavior. Local variables, objects in arrays, statics, etc, will all respect the custom alignment specified for a type. +The `#[repr(align)]` attribute will not interact with `#[repr(packed)]`. That +is, the `#[repr(packed)]` controls the orthogonal attribute of a structure of +how the fields are packed, and the `#[repr(align)]` attribute only controls the +alignment of the overall structure. + # Drawbacks [drawbacks]: #drawbacks From 75c895fd71875d29e30af664fd75a8e05b1e9348 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 May 2016 11:06:50 -0700 Subject: [PATCH 3/5] Clarify some interactions with #[repr(packed)] --- text/0000-repr-align.md | 71 +++++++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/text/0000-repr-align.md b/text/0000-repr-align.md index 3dfc81f9f74..2a048398f6f 100644 --- a/text/0000-repr-align.md +++ b/text/0000-repr-align.md @@ -65,15 +65,68 @@ Multiple `#[repr(align = "..")]` directives are accepted on a struct declaration, and the actual alignment of the structure will be the maximum of all `align` directives and the natural alignment of the struct itself. -Semantically, it will be guaranteed (modulo `unsafe` code) that custom alignment -will always be respected. If a pointer to a non-aligned structure exists and is -used then it is considered unsafe behavior. Local variables, objects in arrays, -statics, etc, will all respect the custom alignment specified for a type. - -The `#[repr(align)]` attribute will not interact with `#[repr(packed)]`. That -is, the `#[repr(packed)]` controls the orthogonal attribute of a structure of -how the fields are packed, and the `#[repr(align)]` attribute only controls the -alignment of the overall structure. +Semantically, it will be guaranteed (modulo `unsafe` code and `#[repr(packed)`) +that custom alignment will always be respected. If a pointer to a non-aligned +structure exists and is used then it is considered unsafe behavior. Local +variables, objects in arrays, statics, etc, will all respect the custom +alignment specified for a type. + +The `#[repr(align)]` attribute will not interact with `#[repr(packed)]` in the +sense that the `packed` attribute only affects *field alignment* whereas `align` +affects the *struct alignment*. The `packed` may indirectly lower struct +alignment by lowering the alignment of fields, and then `align` may raise the +overal struct alignment. + +Some examples of `#[repr(align)]` are: + +```rust +// Raising alignment +#[repr(align = "16")] +struct Align16(i32); + +assert_eq!(mem::align_of::(), 16); +assert_eq!(mem::size_of::(), 16); + +// Lowering has no effect +#[repr(align = "1")] +struct Align1(i32); + +assert_eq!(mem::align_of::(), 4); +assert_eq!(mem::size_of::(), 4); + +// Multiple attributes take the max +#[repr(align = "8", align = "4")] +#[repr(align = "16")] +struct AlignMany(i32); + +assert_eq!(mem::align_of::(), 16); +assert_eq!(mem::size_of::(), 16); + +// Raising alignment may not alter size. +#[repr(align = "8")] +struct Align8Many { + a: i32, + b: i32, + c: i32, + d: u8, +} + +assert_eq!(mem::align_of::(), 8); +assert_eq!(mem::size_of::(), 16); + +// Raising alignment beyond the packed value +#[repr(align = "4", packed = "2")] +struct AlignAndPacked { + a: u16, + b: i32, +} + +assert_eq!(mem::align_of::(), 4); +assert_eq!(mem::size_of::(), 8); +assert_eq!(offset_of!(AlignAndPacked, a), 0); +assert_eq!(offset_of!(AlignAndPacked, b), 2); +``` + # Drawbacks [drawbacks]: #drawbacks From ae4f3454f9c4f2366785276a4d752d62f837c381 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 9 May 2016 09:29:18 -0700 Subject: [PATCH 4/5] Disallow mixing align/packed --- text/0000-repr-align.md | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/text/0000-repr-align.md b/text/0000-repr-align.md index 2a048398f6f..efaba0b9185 100644 --- a/text/0000-repr-align.md +++ b/text/0000-repr-align.md @@ -65,17 +65,17 @@ Multiple `#[repr(align = "..")]` directives are accepted on a struct declaration, and the actual alignment of the structure will be the maximum of all `align` directives and the natural alignment of the struct itself. -Semantically, it will be guaranteed (modulo `unsafe` code and `#[repr(packed)`) -that custom alignment will always be respected. If a pointer to a non-aligned -structure exists and is used then it is considered unsafe behavior. Local -variables, objects in arrays, statics, etc, will all respect the custom -alignment specified for a type. - -The `#[repr(align)]` attribute will not interact with `#[repr(packed)]` in the -sense that the `packed` attribute only affects *field alignment* whereas `align` -affects the *struct alignment*. The `packed` may indirectly lower struct -alignment by lowering the alignment of fields, and then `align` may raise the -overal struct alignment. +Semantically, it will be guaranteed (modulo `unsafe` code) that custom alignment +will always be respected. If a pointer to a non-aligned structure exists and is +used then it is considered unsafe behavior. Local variables, objects in arrays, +statics, etc, will all respect the custom alignment specified for a type. + +For now, it will be illegal to mix `#[repr(align)]` and `#[repr(packed)]` in +structs. Specifically, both attributes cannot be applied on the same struct, and +a `#[repr(packed)]` struct cannot transitively contain another struct with +`#[repr(align)]` or vice versa. The behavior of MSVC and gcc differ in how these +properties interact, and for now we'll just yield an error while we get +experience with the two attributes. Some examples of `#[repr(align)]` are: @@ -113,21 +113,8 @@ struct Align8Many { assert_eq!(mem::align_of::(), 8); assert_eq!(mem::size_of::(), 16); - -// Raising alignment beyond the packed value -#[repr(align = "4", packed = "2")] -struct AlignAndPacked { - a: u16, - b: i32, -} - -assert_eq!(mem::align_of::(), 4); -assert_eq!(mem::size_of::(), 8); -assert_eq!(offset_of!(AlignAndPacked, a), 0); -assert_eq!(offset_of!(AlignAndPacked, b), 2); ``` - # Drawbacks [drawbacks]: #drawbacks From a43706c7610a1498db5385a87df5361a14d9b273 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 May 2016 10:43:04 -0700 Subject: [PATCH 5/5] Allow #[repr(packed)] inside #[repr(align)] --- text/0000-repr-align.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/text/0000-repr-align.md b/text/0000-repr-align.md index efaba0b9185..843d43719c7 100644 --- a/text/0000-repr-align.md +++ b/text/0000-repr-align.md @@ -70,12 +70,13 @@ will always be respected. If a pointer to a non-aligned structure exists and is used then it is considered unsafe behavior. Local variables, objects in arrays, statics, etc, will all respect the custom alignment specified for a type. -For now, it will be illegal to mix `#[repr(align)]` and `#[repr(packed)]` in -structs. Specifically, both attributes cannot be applied on the same struct, and -a `#[repr(packed)]` struct cannot transitively contain another struct with -`#[repr(align)]` or vice versa. The behavior of MSVC and gcc differ in how these -properties interact, and for now we'll just yield an error while we get -experience with the two attributes. +For now, it will be illegal for any `#[repr(packed)]` struct to transitively +contain a struct with `#[repr(align)]`. Specifically, both attributes cannot be +applied on the same struct, and a `#[repr(packed)]` struct cannot transitively +contain another struct with `#[repr(align)]`. The flip side, including a +`#[repr(packed)]` structure inside of a `#[repr(align)]` one will be allowed. +The behavior of MSVC and gcc differ in how these properties interact, and for +now we'll just yield an error while we get experience with the two attributes. Some examples of `#[repr(align)]` are: