Description
Rationale
Porting
compiler-rt
to Rust is one of the remaining obstacles towards the intersection of our "on the fly compilation of std" and "rust everywhere" dreams.
For our goal of "on the fly compilation of std" (or any other set of "standard" crates), we want to minimize the number of C dependencies required to build std
as these complicate the compilation process: users need a C (cross) compiler and we (rust-lang/rust) have to support (cross) compiling each of these C dependencies, which are wrapped in crates, to every target (even custom ones!) that downstream users may use -- this last part leads to complicated build.rs
scripts that need to handle conditional compilation logic and deal with obscure gcc
flags . On Linux, there are three C dependencies that we have to deal with: backtrace
, jemalloc
and compiler-rt
. backtrace
and jemalloc
are optional on Linux and not available on some other platforms but compiler-rt
is mandatory on most (all?) the targets we support.
This issue is about porting compiler-rt
to Rust. Once ported, we can say goodbye to its complicated build.rs
and make std
easier to (cross) compile! An extra advantage is that, with this change, the compiler-rt intrinsics will receive the same optimizations as the std
crate. This is not done today because it would make the build.rs
even more complicated: flags like -march
and -mcpu
would have to be conditionally passed to gcc
according to $TARGET
.
The process
The goal is simple: We have to port each and every compiler-rt intrinsic along with their tests to the rustc-builtins
crate.
The process could go two ways:
- Using a "wholesale" approach: We can develop the new
rustc-builtins
crate out of tree: porting intrinsics and unit tests over time. Once all the intrinsics and unit tests are ported we can replace the in-treerustc-builtins
crate with the out-of-tree one and have the buildbots (and probably crater) serve as an integration test. - Using an incremental approach: We can rely on the fact that
gcc_s
provides the same intrinsics ascompiler-rt
and simply removerustc-builtins
'sbuild.rs
. This effectively means thatrustc-builtins
will no longer provide any intrinsic and thatstd
programs will instead usegcc_s
' intrinsics. Then we can start porting intrinsics + unit tests and adding them torustc-builtins
one by one. Each time a intrinsic is added, Rust programs will start using that intrinsic instead of thegcc_s
' one.
The advantage of (2) is that we get an std
that's easy to cross compile early on (because rustc-builtins
is essentially empty!). Its disadvantage is that no_std
programs which don't link to gcc_s
and that depend on compiler-rt
intrinsics will abruptly stop to compile because of linker errors (undefined reference to $intrinsic
).
Prioritization
Some Rust targets use more or different intrinsics than others. If we take the incremental approach mentioned in the previous section, it makes sense to prioritize porting the intrinsics required by tier-1 platforms. This gist contains a "hack" to yields the list of intrinsics required to link rustc
for a certain target plus lists of intrinsics generated with this "hack" for a few (right now, two) targets. The lists contained therein can be used to decide which intrinsics to prioritize.
Drawbacks
On each LLVM upgrade, we would have to carefully check if any compiler-rt intrinsics have been added (we already have to do this today) and the upgrade would be blocked on porting those new intrinsics to Rust first (this is the extra work that this change would create).
Unresolved questions
- Pick an approach to implement this change.
compilerrt_abort
Some intrinsics can fail (e.g. absvdi2
). compiler-rt
handles the failures by "aborting" -- it calls a compilerrt_abort()
function. Should this function be ported to Rust? Or should the intrinsics, instead, panic!
on failure?