Submitting the Rust libstd patches for review · Issue #14 · espressif/rust-esp32-example (original) (raw)

(This one is going to be long, please bear with me.)

As discussed on the last meeting, we should initiate this process to get early feedback from the Rust maintainers.

I think the process will run more smoothly, if we narrow down first what the names and the parameters of the Rust compiler targets for ESP-IDF would be.
This is relevant even for the C3 chip, because even though there are already two riscv32 targets in the Rust compiler riscv32imc_unknown_none_elf - without atomics - and riscv32imac_unknown_none_elf - with atomics),
these are naked - or "bare metal" if you wish - in that they have no configuration specifying the following properties:

Having the above properties properly defined is very important, because all the "#ifdefs" in Rust's libstd (and Rust's libc crate for that matter) for supporting ESP-IDF are based on those.

Additionally, there are three more properties that we might want to define/correct:

These properties are not used by libstd, but we should have a story for these too.

Proposal

Summary:
I have an opinion on os_family, env, os and vendor.
I'm not so sure about linker, linker_flavor, cpu and the target names.

os_family

Not very many options here. This one MUST be set to "unix" so that libstd is re-using its code-paths which are unix/posix compatible. The other options are "windows" (obviously not an option) and None - which is also obviously not an option.

env

env is basically the name of the C library used. Since ESP-IDF is based on "newlib" we should set it to "newlib". Arguments similar to os_family - reusing already existing code etc. etc.

os

(E.g. as in "linux", "android", "windows", "darwin" etc.)
This is a tricky one. Is ESP-IDF an OS? Yes and no... Some options:

  1. "none" (currently used)
  2. "freertos"
  3. "espidf"

At a first glance, option 1 ("none"), or option 2 ("freertos") look like the natural choice. ESP-IDF does not have a "kernel"
so in a way the OS is ether "none" indeed, or if we squint a little - perhaps "freertos".
The major problem I have with these two options (I'm currently using "none" in the libstd patches) is that these are not very useful.

While they might be reflecting the status quo, the primary purpose of the "os" property is so that you can branch the libstd Rust code on it (that is, whenever the behavior/patch is more specific and you can't just branch by os_family = "unix" and env = "newlib"; admittedly, these cases are rare, but they DO exist).

I simply cannot do it by os = "none" because lots of other targets might have os = "none", and I'm currently over-using the "vendor" property, which IMO is not such a great idea (see below).

Or to put it another way, there is a reason why the Android support is modelled with os = "android" and not with os = "linux". It is just more useful for branching.

Therefore, I suggest that we are using os = "espidf".

vendor

Currently, I'm using "espressif" for that one and I think that's Okay. Once/if we switch "os" to "espidf" the usage of the vendor property in Rust libstd should disappear completely (I hope).

cpu

This should reflect the CPU of the target platform. It is not used for branching in libstd but seems to be passed down to LLVM somehow (?).
The status quo with @MabezDev is that he is setting the cpu to match the MCU name (except for the esp32-c3, where I had to keep the cpu equal to "generic-rv32" because the Rust compiler complained otherwise).

My suggestion is to use the MCU name for cpu whenever possible, i.e.

What is important is to keep the "separate target per Espressif MCU policy".
So far, a new Espressif MCU also meant a new/extended instruction set (i.e. esp32 vs esp32s2 vs esp32s3; not sure for esp32c3 vs esp32c6 though) so that's kind of natural.

Separate Rustc target per Espressif MCU has the benefit of having "good" settings for the linker and linker_flavour properties below.

linker & linker_flavor

These should follow the name and linker nature of the Espressif toolchain for the concrete MCU. I.e.:

Target name (not that important)

Currently, the names of the Rust ESP-IDF (std) targets are as follows (note that the naming convention described here seems to be only loosely followed in general, so we have relative freedom):

I think that's mostly a good convention, but we might want to simplify the prefix of c3 (and future c6) to simply "riscv32" and possibly include "espidf" and "newlib" to reflect that
these use espidf and newlib:

I suggest we change them to:

Now, if we plan to re-use the same targets for linking against ESP-IDF as well as for bare metal, we can just keep the existing names. Reusing the same targets for "bare metal" and for ESP-IDF is possible, because "bare metal" does not care about the additional properties we define for branching in libstd.

However, the re-use of the same targets for ESP-IDF vs pure "bare metal" might be a bit problematic in regards to atomics, for targets that do not support those like ESP32S2 and ESP32C3:

For pure "bare metal", we need to define the target with:

max_atomic_width: Some(0),
atomic_cas: false,

And for ESP-IDF, we have to define:

max_atomic_width: Some(32),
atomic_cas: true, // for libcall based atomics; for emulated instructions should be `false`

... and possibly change the cpu of the esp32s2 ESP-IDF target to be esp32 and not esp32s2, so that atomics instructions are generated. Ditto for the esp32c3 ESP-IDF target, where we need to add the "+a" feature.