-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port compiler-rt
intrinsics to Rust
#35437
Comments
I'm probably missing some information about the details of |
@TimNN We can do that too but it doesn't give us the advantage of (2) because we would still rely on a external C (cross) compiler to compile all the intrinsics that haven't yet been ported to Rust. The advantage of (2) is that we can immediately get rid of the dependency on a external C (cross) compiler. |
Ah, I see, I should probably have read that section more carefully; thanks for clearing that up. |
Note that (at least on Linux) this already happens today, will still happen until #35021 lands and probably has been the case for a long time. Evidence below: #![feature(start)]
use std::ptr;
#[start]
fn start(_: isize, _: *const *const u8) -> isize {
let x = unsafe {
// to prevent the compiler from optimizing away the whole floating point operation
ptr::read_volatile(0x0 as *const f64)
};
let y = 1.;
let z = x + y;
z as isize
}
Note:
Note:
Note: The intrinsics for floating point operations come from |
I've started with this approach in this repository. I've only ported a few intrinsics, mainly the ones that are needed for ARM Cortex-M targets to get basic stuff like |
I think we should also include some other functions that are required by Rust but not provided by compiler-rt:
These should be defined as weak symbols so that an optimized implementation from libc can be used if available. |
@Amanieu Sounds like a good idea to me. But, is there a mechanism to mark a symbol as weak from within Rust? I've done it before for executables but using a linker script; that won't work for rlibs because building those doesn't involve the linker. |
On Windows, the PE/COFF specification describes weak externals which can be used for the purpose of defining symbols such as
|
@retep998 Ok, that sounds like there might exists a LLVM thing to mark a symbol as "weak". I guess my actual question was: is there any Rust attribute to mark a function as weak? |
@japaric I'd personally prefer the incremental approach of just moving intrinsics from C to Rust over time. That way we can ensure that everyone keeps building locally, and we could even add cargo features perhaps to disable all C intrinsics so if all the Rust intrinsics suffice for your target you can get by with pure Rust anyway. |
Perhaps |
#[linkage = "weak"]
#[no_mangle]
pub extern fn memfoo() {}
@alexcrichton Sounds good to me. One thing I like about having |
Doesn't look like it works on Windows. If it worked there would be a |
@japaric I'd be down for that! I agree that moving CI off buildbot is always best :) |
Now that #35021 has landed, let's re-start the discussion here! As I mentioned before, I started working on this in the rustc-builtins repo. And Tentative next steps:
Unresolved questions:
|
It might be beneficial to prefer linking to |
@Amanieu You mean adding something like this: #[link(name = "gcc_s")]
extern {
fn __muldi3(u64, u64) -> u64;
} to rustc-builtins instead of compiling the C implementation from compiler-rt? Seems fine given that, pre #35021, we have been using gcc_s' intrinsics instead of compiler-rt's due to how we have been passing the |
Thanks for pushing this forward @japaric!
Sounds good to me! We'd probably move the repo to rust-lang-nursery, but otherwise seems plausible to me.
I'd recommend a submodule.
I think it's fine to include, but I'd prefer they were turned off for libstd builds
To me this seems like a pretty critical piece to fix. One possible solution though could be:
|
re: testing against libgcc Do you (or anyone else) know how to re-export a symbol from a specific C library as a Rust The issue I see is that there three libraries/crates that contain symbols (intrinsics) with the same For example, libgcc.a and libgcc_s.so do not contain the // gcc_builtins/src/lib.rs
#![no_std]
#[link(name = "gcc")]
extern {
pub fn __muldi3(a: u64, b: u64) -> u64;
}
which implies that Ultimately, I'd like to test the ported builtins like this: assert_eq!(::__multi3(a, b), gcc_builtins::__multi3(a, b)) |
@japaric That's not possible, the linker ensures that there exists only one instance of each symbol name. The only way to do something like this would be to dynamically load libgcc.so and get a specific symbol from it. |
When you link to a symbol, as far as I know there's no way to tell the linker which library to get the symbol from. It just gets it from the first library it finds it in, depending upon linker order. The Theoretically if we integrated LLD as a library we could teach it which library to get a specific symbol from, but that's far off in the future. |
One possible (though not ideal) solution is to create versions of the On Fri, Sep 16, 2016, 1:05 PM Amanieu [email protected] wrote:
|
On windows-msvc, |
@mattico I didn't try the "prefix the symbols" approach but I've implemented the dynamic loading solution in rust-lang/compiler-builtins#67.
This sounds like quite a bit of work. The other issue I see with this approach is that gcc_s doesn't expose the same symbols on all architectures. For example, |
We'd only need to do this on MSVC where we don't have objcopy, and we could automate this with a script using |
I'm assuming that this dynamic loading (with
MSVC doesn't have a libgcc to begin with. |
Yes, this is only for tests.
Does it have any other library/dll that may contain these intrinsics? In any case, it's more important to test different architectures than to test different OSes on the same architecture. Unless, it's an OS-specific intrinsic. Alternatively, we could test the Rust implementation against gcc_s, if available, or against a compiler-rt that we, ourselves, compile, if there's no gcc_s. |
|
@japaric yeah as @Amanieu mentioned I don't think it's possible to do that. The testing for this crate is likely to basically just be a very flavorful binary. In essence though we should avoid linking compiler-builtins and instead link libgcc instead. Then all the symbols in the compiler-builtins crate don't have their real symbol name, but rather a |
I believe everything is essentially done on this issue upstream at https://github.com/rust-lang-nursery/compiler-builtins now. The README also contains a checklist for in-progress translations, so I'm going to close this. |
Rationale
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 complicatedbuild.rs
scripts that need to handle conditional compilation logic and deal with obscuregcc
flags . On Linux, there are three C dependencies that we have to deal with:backtrace
,jemalloc
andcompiler-rt
.backtrace
andjemalloc
are optional on Linux and not available on some other platforms butcompiler-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 complicatedbuild.rs
and makestd
easier to (cross) compile! An extra advantage is that, with this change, the compiler-rt intrinsics will receive the same optimizations as thestd
crate. This is not done today because it would make thebuild.rs
even more complicated: flags like-march
and-mcpu
would have to be conditionally passed togcc
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:
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.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 (becauserustc-builtins
is essentially empty!). Its disadvantage is thatno_std
programs which don't link togcc_s
and that depend oncompiler-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
compilerrt_abort
Some intrinsics can fail (e.g.
absvdi2
).compiler-rt
handles the failures by "aborting" -- it calls acompilerrt_abort()
function. Should this function be ported to Rust? Or should the intrinsics, instead,panic!
on failure?cc @alexcrichton @brson
The text was updated successfully, but these errors were encountered: