# ABI discussion for w65

**URL:** <https://internals.rust-lang.org/t/abi-discussion-for-w65/15092>\
**Category:** language design\
**Created:** [July 27, 2021, 12:59pm UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092 "2021-07-27T12:59:43Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [July 27, 2021, 12:59pm UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/1 "2021-07-27T12:59:43Z")

</div>

I'm opening this thread for a preliminary discussion of a proposed ABI for use with the w65 architecture. It is specifically for discussing the implications for rust implementations of such (though this may necessarily extend into discussions of other mentioned languages and the respective abis). The document defining the ABI is here: [SNES-Dev/abi.md at main · SNES-Dev/SNES-Dev · GitHub](https://github.com/SNES-Dev/SNES-Dev/blob/master/docs/abi.md).

Notable effects on rust:

- `usize` (`isize`), in this abi, does _not_ correspond to `size_t` (`signed size_t`). This may affect FFI code that relies on this correspondance. `usize` (`isize`) does and continues to correspond with the `uintptr_t` (`intptr_t`) type, defined in the `<stdint.h>` header.
  - The maximum size for objects in the ABI is `65535` bytes, or just less than 1 bank. This was done to ensure that every object could be allocated within a single bank. The restriction on the size of the type ensure it can be passed in a hardware register, rather than a specially reserved memory address.
  - Pointers in this abi are 4 bytes, with the high byte 0 for an inbounds pointer. NOTE: pursuant to the discussion [on this zulip thread](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Relaxing.20Limits.20on.20Pointer.20Sizes) ([archive](https://zulip-archive.rust-lang.org/213817tlang/58169RelaxingLimitsonPointerSizes.html)), this is not a validity invariant on the pointer type itself, but in the current version all inbounds pointers have a high-byte of zero.

- There is no standard rust type corresponding with the `uint_fast8_t` and `int_fast8_t` types specifically (no other fixed-sized, least-sized, or fastest least-sized integer types do not have a standard corresponding type). Implementations could provide an (unstable) mechanism to access the underlying type, but this abi does not specify that mechanism. This is not deemed to pose a significant issue to Rust code or Rust FFI.
- ZST Parameters are ignored for the purposes of ABI
- the `AtomicU8` and `AtomicU16` types can be defined by core as they are always lock-free. No other atomic types can be defined by core (notably, `AtomicUsize`/`AtomicPtr<T>` are not defined as atomic operations of size 32 are not lock-free).
- The "Rust" ABI section contemplates the creation of an `extern"w65-interrupt"` abi, which restricts the parameters it can be called with, and also forbids calls to the function. This could (ideally) be done statically to avoid unsoundness.
  - Interrupt handlers define with this ABI have significant restrictions on behaviour (corresponding to the restrictions on _async signal handlers_ from C11). This is deemed not to pose a soundness issue, as in order to be useful, they must be defined with particular link names, and both `#[export_name]` and `#[no_mangle]` are deemed to be [fundamentally `unsafe`](https://github.com/rust-lang/rust/issues/82499).

- The NULL pointer is notably valid, and (on one layout) represents the highest byte of the stack. It is not deemed necessary to be accessible (as any code that would allocate anything there is about to overflow the stack into read only memory anyways).

For formal approval, this abi has been presented to the [llvm-mos](https://github.com/llvm-mos/llvm-mos) project, to avoid abi issues between the SNES-Dev project (my project for supporting Rust, C, and others on specifically the SNES), and the llvm-mos project (which is adding support for the m6502 and, in the future, w65 architectures to llvm), so I would want to await formal approval until discussions have occured there. However, I would be curious as to the process for that. I assume adding the `extern"w65-interrupt"` would require a T-lang (and possibly T-compiler) RFC. Would everything else require an RFC, or just a sign off by lang (and possibly compiler) teams (perhaps an MCP)?

I presume, though, that defining actual targets would be waiting on [RFC 3145](https://github.com/rust-lang/rfcs/pull/3145), though I would like to confirm that no further changes to the ABI are necessary _before_ starting on the C compiler(which I intend to do in the near future as I finish up with the assembler/linker), so if there is currently a process for the approval, that would be useful.

---

<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:** [July 28, 2021, 1:28am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/2 "2021-07-28T01:28:28Z")

</div>

Everything in this definition sounds reasonable to me from a language perspective.

`extern "w65-interrupt"` would need a lang team signoff to implement (though not necessarily a full RFC), but it sounds entirely reasonable.

NULL being valid seems fine as long as you only access that via a raw pointer and a specialized memory access function that handles NULL, not via a reference or a normal raw pointer dereference.

For compatibility, you may want to consider defining a global lock that allows 32-bit atomic operations, so that `AtomicUsize` and `AtomicPtr` work.

You could define this as a Tier 3 target (and I'd love to see that). The only reason to go for the "Candidate Target" approach would be if you don't think you can meet the Tier 3 requirements anytime soon.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [July 28, 2021, 2:13am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/3 "2021-07-28T02:13:24Z")

</div>

> [@josh](#):
>
> For compatibility, you may want to consider defining a global lock that allows 32-bit atomic operations, so that `AtomicUsize` and `AtomicPtr` work.

Doesn't rust require that atomic types be always lock-free? Otherwise, there isn't really a reason that any of the atomic types couldn't exist (though, strictly, they are less useful, as atomics are primarily useful for communication with interrupt handlers, which strictly cannot access non-lock free atomics - though my planned impl will allow access from the irq handler).

> [@josh](#):
>
> You could define this as a Tier 3 target (and I'd love to see that)

Tier 3 requires an implementation in rustc, does it not? That's really the main thing, as that's blocked on either llvm-mos or gcc (the latter of which is really waiting on "do I need to make changes to abi"). It also raises the question of whether I could upstream a target in rustc that doesn't have upstream support in llvm/gcc. Beyond that, I don't see a reason it couldn't (eventually) be a Tier 3 target.

---

<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:** [July 28, 2021, 4:15am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/4 "2021-07-28T04:15:20Z")

</div>

> [@InfernoDeity](#):
>
> Doesn't rust require that atomic types be always lock-free?

You're right, it does. I did not realize that was a requirement. Given that, it does sound like there's no reasonable way to implement them. Code targeting w65 will just have to live without such atomics and data structures built atop them.

> [@InfernoDeity](#):
>
> Tier 3 requires an implementation in rustc, does it not?

Yes, though that implementation may still be a work-in-progress.

> [@InfernoDeity](#):
>
> That's really the main thing, as that's blocked on either llvm-mos or gcc (the latter of which is really waiting on "do I need to make changes to abi"). It also raises the question of whether I could upstream a target in rustc that doesn't have upstream support in llvm/gcc.

I think it'd be acceptable for a tier 3 target to require a patched LLVM; a tier 2 or higher target couldn't, but a tier 3 target could. Nothing in the Target Tier Policy prohibits requiring out-of-tree LLVM changes. I think it'd be _desirable_ for those changes to be on a _path_ towards inclusion, but they don't have to be merged yet.

> [@InfernoDeity](#):
>
> Beyond that, I don't see a reason it couldn't (eventually) be a Tier 3 target.

I'm glad to hear that.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [July 28, 2021, 7:50pm UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/5 "2021-07-28T19:50:33Z")

</div>

> [@InfernoDeity](#):
>
> This may affect FFI code that relies on this correspondance.

This seems like a pure footgun. I believe there is no other such target supported by Rust today, and a _lot_ of FFI code relies on `usize` being `size_t`. In fact, `bindgen` even has an option to codegen `size_t` as `usize` in FFI declarations.

> [@InfernoDeity](#):
>
> The NULL pointer is notably valid, and (on one layout) represents the highest byte of the stack.

How does this interact with niche optimizations? I would think this breaks many assumptions about the layout of `Option<NonNull<T>>` and the like, since the null pointer does not have the value zero.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [July 28, 2021, 8:57pm UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/6 "2021-07-28T20:57:29Z")

</div>

> [@H2CO3](#):
>
> How does this interact with niche optimizations? I would think this breaks many assumptions about the layout of `Option<NonNull<T>>` and the like, since the null pointer does not have the value zero.

The validity is simply an artifact of the memory layout (on the SNES specifically, it mirrors the first byte of WRAM). It's not necessary that it be treated as valid at any level higher than this. I probably could have clarified that, though. The main concern with rust is that on layouts that put the stack in the higher part of this mirror, this is technically unsound as it can produce an automatic storage duration object at address zero (which can be borrowed and 💥), however, as mentioned, there is a bigger issue of it's about to wrap to address `ffff`, which on the SNES is typically read-only memory and the interrupt vectors (and of course, a rust implementation could set up a stack sentinel there).

> [@H2CO3](#):
>
> This seems like a pure footgun. I believe there is no other such target supported by Rust today, and a _lot_ of FFI code relies on `usize` being `size_t` . In fact, `bindgen` even has an option to codegen `size_t` as `usize` in FFI declarations.

This is a bit more of a concern. The issue is that the upper limit size is 65536 (of which, it's fair to lose 1 byte), due to how memory works (it may be advantageous to split linker allocation at bank bounderies. Given that, using 4 bytes for `size_t`, which only stores 2 bytes, is a waste of both memory and time, given that 4 byte values need to be copied into specially reserved memory regions 2 bytes at a time, whereas 2 byte values can just be stored in registers (It's also more efficient to index pointers by a 2 byte value, as it can be done using one of the indexing operations, vs. having to perform 4 byte adds), which are the reasons for choosing `size_t` to be 2 bytes instead of 4. `usize` is 4 bytes because rust defined it as the pointer-sized type, so it needs to be the same size as `*mut u8`, which is 4 bytes because pointers are 3 bytes but extended to 4 for alignment. I'm unsure whether this would be reasonable to solve.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [July 28, 2021, 11:57pm UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/7 "2021-07-28T23:57:56Z")

</div>

An example as to why making `size_t` 2 bytes is beneficial, here is the assembly for the memset and memmove with `size_t` vs. `usize

```rust
//
memset:
    txa
    memset._L0:
    sta (__r1,%D),%Y
    dey
    beq memset._L0
    ldx __r1,%D
    stx __r0,%D
    ldx __r1+2,%D
    stx __r0+2,%D
    sep #$20
    rtl
memcpy:
memmove:
    lda (__r2,%D),%Y
    sta (__r2,%D),%Y
    dey
    beq memmove
    ldx __r1,%D
    stx __r0,%D
    ldx __r1+2,%D
    stx __r+2,%D
    sep #$20
    rtl

```

and here is just `memset` with `usize`:

```rust
memset:
    txa
    memset._L0:
    pha
    jsl __add_int32
    lda 0,%S
    sta (__r0,%D)
    jsl __dec_r2
    beq memset._L0
    ldx __r1,%D
    stx __r0,%D
    ldx __r1+2,%D
    stx __r0+2,%D
    rtl

```

I'm not even sure the code that would be generated for memcpy/memmove in that case (but I'd have to let the compiler figure it out).

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [July 29, 2021, 7:08am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/8 "2021-07-29T07:08:16Z")

</div>

> [@InfernoDeity](#):
>
> The validity is simply an artifact of the memory layout (on the SNES specifically, it mirrors the first byte of WRAM). It's not necessary that it be treated as valid at any level higher than this.

I get that, the problem I see is specifically with the value of the null pointer being not zero, and thus changing the otherwise guaranteed layout of types which currently rely on it being zero, if I understand correctly.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [July 29, 2021, 10:08am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/9 "2021-07-29T10:08:30Z")

</div>

> [@H2CO3](#):
>
> I get that, the problem I see is specifically with the value of the null pointer being not zero, and thus changing the otherwise guaranteed layout of types which currently rely on it being zero, if I understand correctly.

Ah yes. I could perhaps have clarified that as well. The null pointer will remain address 0, for a few reasons, chief of which is rust defines it to be so. I was just mentioning that it is a valid address that could in theory have something allocated there

---

<div class="post-metadata">

**Author:** ![jethrogb](https://avatars.discourse-cdn.com/v4/letter/j/43a26b/32.png) [@jethrogb](https://internals.rust-lang.org/u/jethrogb)\
**Post date:** [August 2, 2021, 7:01am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/10 "2021-08-02T07:01:51Z")

</div>

> [@InfernoDeity](#):
>
> it is a valid address that could in theory have something allocated there

This is true for most architectures.

---

<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:** [August 2, 2021, 10:21am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/11 "2021-08-02T10:21:03Z")

</div>

Even on x86 there can be something allocated there. Most OSes just don't allow you to map the zero page. On Linux for example you need to change [`mmap_min_addr`](https://wiki.debian.org/mmap_min_addr) to 0 before it is allowed.

---

<div class="post-metadata">

**Author:** ![ShadowJonathan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/shadowjonathan/32/8458_2.png) [@ShadowJonathan](https://internals.rust-lang.org/u/ShadowJonathan)\
**Post date:** [August 3, 2021, 10:36pm UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/12 "2021-08-03T22:36:58Z")

</div>

(Off-topic, but I’m curious where the rationale and motivation comes from to develop for the SNES in Rust?)

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [August 3, 2021, 11:09pm UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/13 "2021-08-03T23:09:13Z")

</div>

I've been working for quite a while to be able to develop homebrew for the SNES using modern languages like C and C++. When I first learned rust, I found the advantages obvious - I could use a modern language that had powerful zero-overhead abstractions but didn't have overly burdensome requirements on freestanding environments (_cough_ C++ freestanding _cough_).

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [August 20, 2021, 12:31am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/14 "2021-08-20T00:31:40Z")

</div>

As I have begun working on the gcc backend, and am getting ready to finalize many of the details, I'd like to seek formal input/approval from T-lang on whether rust can formally support the proposed abi, and any target using such an abi (though I do not intend at this current time to propose any targets at any tier, though I intend to do so after the gcc backend is complete and work on porting rustc has begun). Is this something that can be done at this time, and, if so, through what process would that be (MCP, RFC, otherwise)?

Specific questions that I would like addressed within reasonable time, as they affect details of the C compiler as well as rust:

- Incompatibility between `usize` and `size_t`, wrt. to FFI and the issues presented here related to that.
- Preliminary issues related to `extern"w65-interrupt"`
- Currently, raw pointers and `usize` differ in their alignment requirement, with `usize` having alignment requirement 2, and pointers having alignment requirement 4. Is this an issue for rust?

Other questions that would eventually need to be addressed, but are not as immediate (as they solely impact rust and would not impact work on the C compiler or adjacent support libraries):

- Does rust permit atomic types that are defined as "non lock-free" but do not use a global mutex/spinlock? In particular, can certain atomic types be defined to be "non lock-free" in order to restrict their use from certain interrupt handlers, while being implemented in a way that allows being defined in rust (for example, by using a cpu flag to lock interrupt handlers)

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [August 20, 2021, 3:27am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/15 "2021-08-20T03:27:08Z")

</div>

> [@InfernoDeity](#):
>
> `usize` ( `isize` ), in this abi, does _not_ correspond to `size_t` ( `signed size_t` ). This may affect FFI code that relies on this correspondance. `usize` ( `isize` ) does and continues to correspond with the `uintptr_t` ( `intptr_t` ) type, defined in the `<stdint.h>` header.

Just for clarity on this one: `usize` is still 4 bytes to match the size of the pointers, right? (Since it says pointers are 4 bytes two bullets later.)

> [@InfernoDeity](#):
>
> There is no standard rust type corresponding with the `uint_fast8_t` and `int_fast8_t` types specifically (no other fixed-sized, least-sized, or fastest least-sized integer types do not have a standard corresponding type).

Aren't those just typedefs in C anyway?

> [@InfernoDeity](#):
>
> Currently, raw pointers and `usize` differ in their alignment requirement, with `usize` having alignment requirement 2, and pointers having alignment requirement 4. Is this an issue for rust?

I don't think there's any formal guarantee of alignment requirements for different rust types. (I wouldn't be surprised if `u64` or `u128` were different on different targets, for example.)

That said, I wouldn't be surprised at all for there to be extant rust code that allows things like `&usize` \<-\> `&*const T`, assuming it's fine.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [August 20, 2021, 3:30am UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/16 "2021-08-20T03:30:09Z")

</div>

> [@scottmcm](#):
>
> Just for clarity on this one: `usize` is still 4 bytes to match the size of the pointers, right? (Since it says pointers are 4 bytes two bullets later.)

Yes, it matches the size of pointers, and, as mentioned, corresponds with the C typedef `uintptr_t` (which is `unsigned long`, and likewise corresponds with the size of `void*`).

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [August 20, 2021, 4:12pm UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/17 "2021-08-20T16:12:04Z")

</div>

Another question I just thought of and should probably raise: The runtime library that supports various arithmetic features leaves some of them optional, and these correspond to target-feature flags. Some of these impact features required by the rust language (though by default, they are available). In particular:

- Floating-point support may be disabled in the library with the configuration option `--without-float`. This corresponds to the `float` target feature, though float operations are also available with the `hardfp` target feature. This affects the ability to perform operations on `f32` and `f64`.
- 128-bit integer support may also be disabled in the library with the option `--without-int128`. This corresponds to the `int128` target feature, and affects arithmetic on `i128`.

It is recommended that unusable features be ill-formed (otherwise, the result would be a link-time error if the library is compiled without the appropriate support). This should not affect rust code without the consent of the user, however, as the default will be that the features are enabled. Is this sufficient to avoid issues?

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [November 18, 2021, 4:12pm UTC](https://internals.rust-lang.org/t/abi-discussion-for-w65/15092/18 "2021-11-18T16:12:35Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
