# repr(C) AIX Struct Alignment

**URL:** <https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594>\
**Category:** compiler\
**Created:** [September 25, 2024, 6:22pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594 "2024-09-25T18:22:28Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![mustartt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mustartt/32/12577_2.png) [@mustartt](https://internals.rust-lang.org/u/mustartt)\
**Post date:** [September 25, 2024, 6:22pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/1 "2024-09-25T18:22:28Z")

</div>

On PowerPC AIX, there are [special layout alignment rules](https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=modes-alignment-aggregates) for structs not represented by the `repr(C)` attribute in Rust. Specifically, the layout of the following struct is misaligned:

```rust
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Floats {
    a: f64,
    b: u8, // currently has 7 bytes of padding
    c: f64,
}

```

While the same struct generated by Clang and GCC

```c
struct Floats {
    double a;
    char b;
    double c;
};

```

has the following layout

```llvm
Layout: <ASTRecordLayout
  Size:192
  DataSize:192
  Alignment:32
  PreferredAlignment:64
  FieldOffsets: [0, 64, 96]>
Layout: <CGRecordLayout
  LLVMType:%struct.Floats = type <{ double, i8, [3 x i8], double, [4 x i8] }>
  IsZeroInitializable:1
  BitFields:[
]>

```

There are already existing infrastructure in `rustc_target` to customize the target ABI calling convention for `extern "C"`. I'm wondering what would be the best course of action to implement this special layout rule for `repr(C)`?

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [September 25, 2024, 7:24pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/2 "2024-09-25T19:24:45Z")

</div>

How does that rule not cause UB when taking references to fields? A reference to an f64 has to be aligned to 8 bytes, but field c would only be aligned to 4 bytes.

In any case there are some corner cases with Windows MSVC too where `repr(C)` doesn't match C. Fixing this would break expectations of existing code. Because of this a better name for `repr(C)` would likely have been `repr(linear)`.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [September 25, 2024, 7:56pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/3 "2024-09-25T19:56:37Z")

</div>

> [@bjorn3](#):
>
> In any case there are some corner cases with Windows MSVC too where `repr(C)` doesn't match C.

I still think we should fix those, even if that takes an edition or similar.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [September 25, 2024, 7:57pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/4 "2024-09-25T19:57:51Z")

</div>

On AIX, in C, what is `alignof(double)` (or `_Alignof(double)` or ` __alignof__ (double)` or however your compiler spells it)?

---

<div class="post-metadata">

**Author:** ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)\
**Post date:** [September 25, 2024, 8:15pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/5 "2024-09-25T20:15:23Z")

</div>

[`repr(C)` serves two purposes](https://doc.rust-lang.org/reference/type-layout.html#the-c-representation):

> The `C` representation is designed for dual purposes. One purpose is for creating types that are interoperable with the C Language. The second purpose is to create types that you can soundly perform operations on that rely on data layout such as reinterpreting values as a different type.

The reference then goes on to document the exact layout algorithm used. Therefore, making `repr(C)` do something other than that algorithm would be a breaking change. The name `C` is unfortunate since oddities like the one this thread is about exist, and could perhaps be split into two across an edition, but we cannot _just_ make `repr(C)` platform-specific now.

---

<div class="post-metadata">

**Author:** ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)\
**Post date:** [September 25, 2024, 8:18pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/6 "2024-09-25T20:18:29Z")

</div>

Perhaps it would be useful to add a `repr(platform)` (or lets call it `repr(bikeshed)` for now to avoid that can of worms) that can match both MSVC and AIX on their respective platforms. Though looking at the provided link AIX has three different options... And it isn't clear what is default to me.

---

<div class="post-metadata">

**Author:** ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)\
**Post date:** [September 25, 2024, 8:22pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/7 "2024-09-25T20:22:53Z")

</div>

> [@bjorn3](#):
>
> How does that rule not cause UB when taking references to fields? A reference to an f64 has to be aligned to 8 bytes, but field c would only be aligned to 4 bytes.

[IBM Documentation](https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes) seems to have more info on this:

> In aggregates, the first member of this data type is aligned according to its natural alignment value; subsequent members of the aggregate are aligned on 4-byte boundaries.

and

> If you are working with aggregates containing double, long long, or long double data types, use the natural mode for highest performance, as each member of the aggregate is aligned according to its natural alignment value.

I don't know if the hardware is OK with unaligned but slower (like x86) or straight up errors (like ARM). Either way the AM isn't OK with this as I understand it.

---

<div class="post-metadata">

**Author:** ![mustartt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mustartt/32/12577_2.png) [@mustartt](https://internals.rust-lang.org/u/mustartt)\
**Post date:** [September 25, 2024, 8:34pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/8 "2024-09-25T20:34:15Z")

</div>

On AIX, ` __alignof__ (double)` double has a 8 byte alignment

---

<div class="post-metadata">

**Author:** ![mustartt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mustartt/32/12577_2.png) [@mustartt](https://internals.rust-lang.org/u/mustartt)\
**Post date:** [September 25, 2024, 8:50pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/9 "2024-09-25T20:50:36Z")

</div>

> How does that rule not cause UB when taking references to fields? A reference to an f64 has to be aligned to 8 bytes, but field c would only be aligned to 4 bytes.
> 
> In any case there are some corner cases with Windows MSVC too where `repr(C)` doesn't match C. Fixing this would break expectations of existing code. Because of this a better name for `repr(C)` would likely have been `repr(linear)`.

I need to get back on this to get a full explanation why this is allowed.

But I'm more worried that suppose (if) we take the platform dependent layout, then would this type be valid in Rust if the f64 is not aligned to the 8 byte boundary?

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [September 25, 2024, 9:20pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/10 "2024-09-25T21:20:02Z")

</div>

> [@mustartt](#):
>
> then would this type be valid in Rust if the f64 is not aligned to the 8 byte boundary?

We could lower the alignment of f64 to 4 bytes to make a 4 byte aligned &f64 valid, but then for example a struct containing an f64 as first field would only be 4 byte aligned, while it needs to be 8 byte aligned. This has also causes the size of the struct to be wrong and thus offets for types containing this struct to be wrong.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [September 25, 2024, 9:59pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/11 "2024-09-25T21:59:38Z")

</div>

> [@bjorn3](#):
>
> We could lower the alignment of f64 to 4 bytes to make a 4 byte aligned &f64 valid, but then for example a struct containing an f64 as first field would only be 4 byte aligned, while it needs to be 8 byte aligned. This has also causes the size of the struct to be wrong and thus offets for types containing this struct to be wrong.

It would also make bare `f64`s underaligned, e.g. on the stack.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [September 25, 2024, 10:22pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/12 "2024-09-25T22:22:59Z")

</div>

With unnamed fields -- [RFC 2102](https://rust-lang.github.io/rfcs/2102-unnamed-fields.html) and [rust#49804](https://github.com/rust-lang/rust/issues/49804) -- we could model it like:

```rust
#[repr(C)]
pub struct Floats {
    a: f64,
    #[repr(packed(4))]
    _: struct {
        b: u8,
        c: f64,
    },
}

```

In fact, there's a quite similar example in the [Representation](https://rust-lang.github.io/rfcs/2102-unnamed-fields.html#representation) section of the RFC. I suppose the inner `packed` will also prevent Rust references, to avoid that alignment problem.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [September 25, 2024, 10:24pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/13 "2024-09-25T22:24:04Z")

</div>

That is fascinating and horrifying. And yeah, we can't make Rust support that without a much larger change, because it requires under-aligned fields.

---

<div class="post-metadata">

**Author:** ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)\
**Post date:** [September 25, 2024, 10:31pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/14 "2024-09-25T22:31:22Z")

</div>

> [@josh](#):
>
> That is fascinating and horrifying.

Very much so. Also look at the mac68k column for some more horror. I don't even know why AIX would have a repr compatible with early 90s Macs (I presume this is about the Motorola 6800).

That said, a possible workaround on this platform is to use `repr(C, packed)` and handle the alignment yourself. I think that should work. It is also quite awful, but it is a tier 3 target, so ymmv.

---

<div class="post-metadata">

**Author:** ![farnz](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/farnz/32/6587_2.png) [@farnz](https://internals.rust-lang.org/u/farnz)\
**Post date:** [September 26, 2024, 9:23am UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/15 "2024-09-26T09:23:19Z")

</div>

> [@Vorpal](#):
>
> I don't even know why AIX would have a repr compatible with early 90s Macs (I presume this is about the Motorola 6800).

While we now mostly remember the AIM Alliance for turning POWER into PowerPC, and getting Apple to move from 68k to PPC, it also had a goal of gradually unifying IBM and Apple operating systems, via Taligent and the "Pink" OS.

As part of this goal, IBM did their best to enable you to use their compilers on AIX for all your software development needs, assuming that you were either targeting Apple or IBM hardware.

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [October 11, 2024, 3:21pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/16 "2024-10-11T15:21:23Z")

</div>

> [@bjorn3](#):
>
> In any case there are some corner cases with Windows MSVC too where `repr(C)` doesn't match C.

Just for reference, [here](https://github.com/rust-lang/unsafe-code-guidelines/issues/521) is an issue tracking those cases.

> [@Vorpal](#):
>
> [IBM Documentation](https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes) seems to have more info on this:
> 
> > In aggregates, the first member of this data type is aligned according to its natural alignment value; subsequent members of the aggregate are aligned on 4-byte boundaries.
> 
> and
> 
> > If you are working with aggregates containing double, long long, or long double data types, use the natural mode for highest performance, as each member of the aggregate is aligned according to its natural alignment value.
> 
> I don't know if the hardware is OK with unaligned but slower (like x86) or straight up errors (like ARM). Either way the AM isn't OK with this as I understand it.

Wow... whatever they were smoking when deciding this, it must have been some really good stuff.^^

> [@mustartt](#):
>
> On AIX, ` __alignof__ (double)` double has a 8 byte alignment

In that case, in my understanding the layout computed by C causes Undefined Behavior in C, since pointers to the second `double` field are not guaranteed to be 8-byte aligned.

This reminds me a bit of [this problem](https://github.com/rust-lang/rust/issues/112480)... the way I would describe both situations is that the actual alignment requirement of the type is 4, but when such a type appears in a struct some special rules kick in:

- for this AIX case, the first field of this type is treated as-if the type has alignment 8
- for the MSVC case, struct fields of type `u64` are treated as-if that type has alignment 8

This is not _impossible_ to do in Rust, though it does equip types with degrees of freedom that do not currently exist (so the layout algorithm needs to track more things), and it means `repr(C)` cannot use the algorithm currently described in the reference.

---

<div class="post-metadata">

**Author:** ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)\
**Post date:** [October 11, 2024, 3:36pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/17 "2024-10-11T15:36:34Z")

</div>

> [@RalfJung](#):
>
> and it means `repr(C)` cannot use the algorithm currently described in the reference.

I don't think that can be changed. It would break many uses of `repr(C)` for non-FFI (such as manual layout optimisation, think things like smallvec, zero copy shenanigans, etc).

It _would_ be possible to have a `repr(platform)` though. (Or lets call it `repr(bikeshed)` since I'm not interested in the name discussion.)

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [October 11, 2024, 4:02pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/18 "2024-10-11T16:02:44Z")

</div>

> [@Vorpal](#):
>
> It would break many uses of `repr(C)` for non-FFI (such as manual layout optimisation, think things like smallvec, zero copy shenanigans, etc).

Do you have a concrete example where these really rely on the alignment used during layout computation matching `align_of`?

We are currently _also_ breaking people by not delivering on our promise of matching the platform layout, so things are breaking either way. It's not clear to me that "keep `repr(C)` unchanged" is the option that breaks fewer things.

---

<div class="post-metadata">

**Author:** ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)\
**Post date:** [October 11, 2024, 4:46pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/19 "2024-10-11T16:46:25Z")

</div>

> [@RalfJung](#):
>
> Do you have a concrete example where these really rely on the alignment used during layout computation matching `align_of`?

In the GPU-using-application world (which is a kind of cross-architecture FFI, essentially), people regularly write `repr(C)` to mean "please use this deterministic layout algorithm", then write the struct fields such that they will get specific known layouts. This doesn’t depend on `align_of` but _does_ depend on knowing what the possible outcomes of `repr(C)` are.

And some of it is _probably_ wrong if primitives have lower alignment than size. Most instances contain no `f64`/`double`s, though, so this is less a practical problem on AIX than a theoretical one.

> [@RalfJung](#):
>
> We are currently _also_ breaking people by not delivering on our promise of matching the platform layout, so things are breaking either way. It's not clear to me that "keep `repr(C)` unchanged" is the option that breaks fewer things.

I think the correct thing to do here is to deprecate `repr(C)` and introduce separate `repr(platform)` and `repr(deterministic_v1)` layouts. There are two different intents, and letting developers express what guarantees they actually need is worth the added complexity.

---

<div class="post-metadata">

**Author:** ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)\
**Post date:** [October 11, 2024, 4:46pm UTC](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594/20 "2024-10-11T16:46:38Z")

</div>

I don't have concrete examples. But I can see how to plausible construct some cases that would break (more on that below). Do they happen in practise? No clue. Could a crater run detect them? Now that is an interesting question, which I don't know.

So here are some scenarios:

- The current alignment rules for repr C are assumed to hold and used for soundness of SIMD loads.
- The layout alogithm is assumed to be stabilised (I checked the reference, and it doesn't say that it is unstable). This is then used to do zero copy shared memory IPC between a pair of rust programs. _One_ of these is rebuilt with a newer compiler where the rules have changed, now the shared memory IPC is unsound.
- While the current two examples both _decrease_ alignment, what about a case where alignment is instead _increased_ (for some future case yet to be discovered)? [NoUninit in bytemuck - Rust](https://docs.rs/bytemuck/latest/bytemuck/derive.NoUninit.html) in older versions of that crate would possibly be unsound now (depending on how it is implemented). Manual implementations of that trait (as opposed to derives) would certainly have a risk of becoming unsound if the rules change and padding bytes are added.
- Creative use of unions may also be affected. You might be using repr C to convert between (properly aligned) byte buffers and another zero copy representation of data.

I think the main issues with changing the rules are:

- The current algorithm is stable. There is no easy way to tell what code may depend on it.
- It won't change on x86-64 Linux (which is what crater runs on). So crater won't find breakages.
- The platforms that would have breakages are quite frankly extremely niche, so most projects likely don't test on them either (at least not in CI). This makes it more likely to break without developers noticing.
- Spreading awareness about this change will be challenging. Most people don't read every release announcement.

Contrast that with adding a new repr:

- Change bindgen, bindgen, cxx etc to use this new repr. This will then by itself spread slowly through the ecosystem.
- That will of course not catch every case (bindgened files not updated/regenerated, dead projects hand written bindings, ...), but the platforms where it matters are again niche.
- The change will be more visible, even if you missed the original announcement, you might go "huh, what is this weird repr(bikeshed)" eventually?

Neither solution is great. Pick whichever is the lesser evil.

[Next page](https://internals.rust-lang.org/t/repr-c-aix-struct-alignment/21594.md?page=2)
