Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upFloat-free libcore (for embedded systems and kernel drivers, among other things) #1364
Comments
steveklabnik
referenced this issue
Nov 11, 2015
Closed
Tracking issue for libcore + no_std stabilization #27701
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Since LLVM will implement small-ish |
This comment has been minimized.
This comment has been minimized.
|
I think there's actually a bit of a matrix here which can be helpful when thinking about this, we've got the two vectors of "libcore explicitly uses floating point" and "llvm codegens using floating point". Looking at the possibilities here:
With this in mind, I think it may be better to frame this around "disabling floating point support in generated code" rather than specifically omitting it from libcore itself. For example if we look at the above matrix, if LLVM is allowed to use floating point registers, then there's no reason to omit the support from libcore anyway (modulo the As a result, this may lead itself quite nicely to a non-invasive implementation. For example on intel processors there may be something like |
This comment has been minimized.
This comment has been minimized.
|
@rkruppe I agree completely. If we don't want SSE2 instructions, we should tell LLVM not to generate them. Generating them and then trying to remove them will obviously fail. @alexcrichton Thank you for clarifying the issues! If I understand it, you're proposing two things here:
Am I understanding you correctly? If so, I agree that (1) sounds like a perfectly plausible way to address these issues. But if you intended (2) as a literal proposal (and not just an abstract sketch of an implementation), then I'm not convinced it's the right way to go. The problem with writing something like #[cfg(or(target_feature = "sse2", target_feature = "x87", target_feature = "neon",
target_feature = "vfp", target_feature="vfp4", target_feature = "soft_float"))]...just to cover the Intel and ARM architectures. And depending on how you implemented it, that conditional might have to appear multiple times in Some possible alternatives might be: #[cfg(target_feature = "float"))]…or: #[cfg(target_float))]The advantage of these approaches is that Logically, this information feels like it would be either:
I'd guess that (1) is fairly easy to implement, and it would work well with the target But in the end, I'd be happy to implement just about any of these approaches—whatever works best for you. Like I said, my goal here is to provide a long-term roadmap for safely writing things like kernel modules using |
This comment has been minimized.
This comment has been minimized.
|
@emk you're understanding is spot on, that's precisely what I was thinking. I'd be fine extending the compiler to have a higher level notion of "floating point support" and disabling that means something different on every platform, and adding a particular Some prior art here could be the recent addition of the |
This comment has been minimized.
This comment has been minimized.
|
Great, thank you for the pointer to Let me sketch out a design to see if I'm in the right ballpark. Right now, We could add a This way, we could define a kernel-safe x86 target using something like:
I think that this would actually be a fairly small, clean patch. (Alternatively, we could try something more ambitious, where Would this design work as is? If not, how could I improve it? If we can come up with a basically simple and satisfactory design, I'd be happy to try to implement it. Thank you for your feedback! |
This comment has been minimized.
This comment has been minimized.
|
@emk yeah that all sounds good to me, I'd be fine if disabling floats just implied all the necessary features to pass down to LLVM so they didn't have to be repeated as well. |
This comment has been minimized.
This comment has been minimized.
|
@alexcrichton Thank you for the feedback! For a first pass, I'll try to implement "features": "...",
"disable-floating-point-features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2",
"has-floating-point": false,
"disable-redzone": true,Above, we disable floating point, and then we need explain what that means, so that the compiler can do it for us. I'm not sure that's really an improvement over: "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2",
"has-floating-point": false,
"disable-redzone": true,Can anybody think of a better design? I'm definitely open to suggestions here, and I'm sure I don't see all the use cases. And thank you again for your help refining this design! |
This comment has been minimized.
This comment has been minimized.
|
Hm yeah that's a good point I guess, the features being passed down do probably need to be generic. It's a little unfortunate that you can still construct an invalid target specification by disabling floating point and not disabling the features, but I guess that's not necessarily the end of the world. |
This comment has been minimized.
This comment has been minimized.
|
OK, I've planned out a block of time to work on this week (hopefully by midweek-ish, if all goes well). |
Ericson2314
referenced this issue
Dec 4, 2015
Closed
Make Cargo aware of standard library dependencies #1133
This comment has been minimized.
This comment has been minimized.
|
Ran into this again today :) @emk did you get a chance to work on it at all? |
This comment has been minimized.
This comment has been minimized.
|
Not yet! I'm bounding between two different (free-time) Rust projects right Le sam. 12 déc. 2015 à 12:02, Steve Klabnik notifications@github.com a
|
This comment has been minimized.
This comment has been minimized.
|
Okay :) It's not mega urgent for me, either, so I might or might not :) |
This comment has been minimized.
This comment has been minimized.
phil-opp
commented
Dec 12, 2015
|
As a workaround, I created https://github.com/phil-opp/nightly-libcore. It includes thepowersgang's libcore patch. |
This comment has been minimized.
This comment has been minimized.
alilee
commented
Dec 14, 2015
|
Great idea guys. Though is getting rid of floating point only part of the picture? Excuse me if I don't have the full perspective, but what I need is to disable (ARM) neon/vfp instructions in bootstrap/exception handler code so that I know that it won't require the fpu to be enabled or the fpu register file to be saved. (llvm-arm seems to like vldr and vstr for multi-word moves). I would want to link with a core containing the fpu routines, but know that certain sections don't access the fpu registers. If I understand things, the features are defined at the root compilation unit making it hard to set compiler features for a sub-crate or sub-unit? |
This comment has been minimized.
This comment has been minimized.
|
How would such an option affect the |
This comment has been minimized.
This comment has been minimized.
Nope. They'll just not be included in libcore. |
This comment has been minimized.
This comment has been minimized.
|
You cannot “not” include primitive types. They are a part of the language. |
This comment has been minimized.
This comment has been minimized.
|
@nagisa Of course not. They're primitive. But you can stop providing an API for them, which is what this RFC suggests. |
This comment has been minimized.
This comment has been minimized.
|
@ticki I guess quoting the original question is the best here, since there seems to be some misunderstanding:
@Amanieu The answers are “in no way” and “no”. You would still be able to use floating point literals using the notations ( |
This comment has been minimized.
This comment has been minimized.
lilred
commented
Dec 24, 2015
|
Just chiming in to say that this is an issue I'm hitting too. |
This comment has been minimized.
This comment has been minimized.
ghost
commented
Dec 27, 2015
|
Whatever we decide as the solution, I think that it should be user-friendly enough that low-level crate developers can also selectively omit floating-point code from their crates. It should also be documented in the Rust Book so that people know about it. By that, I mean that instead of a large list of targets to omit, we should definitely have a |
rkruppe
referenced this issue
Feb 20, 2016
Merged
Enable building freestanding sysroot with cargo #31792
This comment has been minimized.
This comment has been minimized.
MagaTailor
commented
Feb 20, 2016
|
Will it be possible to do things that llvm disallows? https://llvm.org/bugs/show_bug.cgi?id=25823 |
This comment has been minimized.
This comment has been minimized.
|
@petevine Generally, no. LLVM's assertions are there for a reason. It will almost per se lead to strange bugs. |
This comment has been minimized.
This comment has been minimized.
|
@phil-opp Did you disable SSE as well ( |
This comment has been minimized.
This comment has been minimized.
phil-opp
commented
Jul 26, 2016
|
I use the features proposed by @parched above: "features": "-mmx,-fxsr,-sse,-sse2,+soft-float", |
This comment has been minimized.
This comment has been minimized.
phil-opp
commented
Jul 26, 2016
|
Ok, I can't reproduce the |
This comment has been minimized.
This comment has been minimized.
parched
commented
Jul 26, 2016
Yes, I was surprised because I'm sure when I did Note also, the |
This comment has been minimized.
This comment has been minimized.
phil-opp
commented
Jul 26, 2016
Thanks! What about |
This comment has been minimized.
This comment has been minimized.
|
From a quick grep of LLVM, it seems all that flag does is enable/disable the FXSAVE and FXRSTOR instructions in the assembler. |
This comment has been minimized.
This comment has been minimized.
parched
commented
Jul 26, 2016
|
Well, I don't think it's actually needs to be disabled, it's for storing and restoring fp registers, so it's never going to be used if mmx and sse are disabled. |
This comment has been minimized.
This comment has been minimized.
|
In any case the compiler won't be generating that instruction on its own without explicitly calling the intrinsic for it. |
This comment has been minimized.
This comment has been minimized.
parched
commented
Jul 26, 2016
|
Yes, so actually it's probably better not to disable this for kernel development as it could be useful intrinsic when switching user threads. |
This comment has been minimized.
This comment has been minimized.
|
Disclaimer: I'm not an x86 kernel/OS dev :-) Can someone confirm that using |
This comment has been minimized.
This comment has been minimized.
parched
commented
Jul 30, 2016
|
I can confirm they lower to software routines as expected and only use general purpose registers . I can't confirm this is enough for the Op, but believe it should be so. |
This comment has been minimized.
This comment has been minimized.
|
I just tested this on AArch64 and it works fine with |
This comment has been minimized.
This comment has been minimized.
|
@parched @Amanieu Thanks for checking!
Interesting! Given that we would ultimately like to have Cargo build compiler-rt intrinsics when you build core/std, perhaps we'll have to add a |
This comment has been minimized.
This comment has been minimized.
|
In practice you can probably get away with a standard compiler-rt as long as you don't use any floating point values in your code. |
This comment has been minimized.
This comment has been minimized.
comex
commented
Jul 31, 2016
•
Hmm; does that mean that if you pass -sse, you can't use SSE instructions in inline assembly blocks? Because that sounds somewhat annoying. |
This comment has been minimized.
This comment has been minimized.
parched
commented
Jul 31, 2016
No it doesn't apparently (I just tested), but why would you want to use SSE instructions if you have turned it off? |
This comment has been minimized.
This comment has been minimized.
jdub
commented
Jul 31, 2016
|
The reason to turn it off is you don't want uncontrolled use of SSE (or, context switch unsafe) instructions, or use of them anywhere near ABI boundaries. Controlled use is fine, and desirable – you're probably going to want fast crypto, vector acceleration, etc. at some point. |
This comment has been minimized.
This comment has been minimized.
parched
commented
Jul 31, 2016
|
Well in that case I would turn |
This comment has been minimized.
This comment has been minimized.
jdub
commented
Jul 31, 2016
|
That'd be pretty uncontrolled. Generally, a few inline functions to safely wrap assembly blocks will suffice. |
nrc
added
T-libs
T-dev-tools
labels
Aug 19, 2016
This comment has been minimized.
This comment has been minimized.
lilred
commented
Sep 14, 2016
|
What's the "state of the union" on this particular issue? Do we still need to use |
This comment has been minimized.
This comment has been minimized.
ketsuban
commented
Sep 14, 2016
•
|
The state appears to be that according to @japaric (link] and @parched (link) using the new |
This comment has been minimized.
This comment has been minimized.
parched
commented
Sep 15, 2016
|
Well, you shouldn't really recompile anything. 'soft-float' (or equivalent for other targets) should already be set as a feature of the target (or not) which you shouldn't change with 'rustc' codegen options otherwise stuff won't link probably. |
emk commentedNov 11, 2015
(I was talking to @huonw about embedded Rust the other day, and he suggested I write this up as an RFC issue. I hope this is in the correct place!)
I'm having a ton of fun hacking on kernels in Rust. Rust is a wonderful fit for the problem domain, and the combination of
libcoreand custom JSON--targetspecs makes the whole process very ergonomic. But there's one issue that keeps coming up on#rust-osdev:libcorerequires floating point, but many otherwise reasonable environments place restrictions on floating point use.Existing discussions of this issue can be found here:
rustc, you break the parts oflibcorethat deal with floats.libcorewithout floats, closed without merge. A version of this patch is provided by rust-barebones-kernel, and this patch is frequently recommended on#rust-osdev.libcoredepends on floating point.#[cfg(float_is_broken)]. Not sure how relevant this is.Datum 1: Some otherwise reasonable processors do not support floating point
There's always been a market for embedded processors without an FPU. For the most part, these aren't pathologically weird processors. The standard ARM toolchain supports
--fpu=none. Many of the older and/or lower-end ARM chips lack FPUs. For example, the FPU is optional on the Cortex-M4.Now, I concur (enthusiastically) that not all embedded processors are suitable for Rust. In particular, there are processors where the smallest integer types are
u32andi32, makingsizeof(char) == sizeof(uint32_t) == 1in C, and whereuint8_tliterally does not exist. There were once quite a few CPUs with 36-bit words. I agree that all these CPUs are all fundamentally unsuitable for Rust, because Rust makes the simplifying decision that the basic integer types are 8, 16, 32 and 64 bits wide, to the immense relief of everybody who programs in Rust.But CPUs without floating point are a lot more common than CPUs with weird-sized bytes. And the combination of
rustcandlibcoreis an otherwise terrific toolchain for writing low-level code for this family of architecture.Datum 2: Linux (and many other kernels) forbid floating point to speed up syscalls and interrupts
Another pattern comes up very often:
writeor another common syscall?These constraints point towards an obvious optimization: If you forbid the use of floating point registers in kernel space, you can handle syscalls and interrupts without having to save the floating point state. This allows you to avoid calling epic instructions like
FXSAVEevery time you enter kernel space. Yup,FXSAVEstores 512 bytes of data.Because of these considerations, Linux normally avoids floating point in kernel space. But ARM developers trying to speed up task switching may also do something similar. And this is a very practical issue for people who want to write Linux kernel modules in Rust.
(Note that this also means that LLVM can't use SSE2 instructions for optimizing copies, either! So it's not just a matter of avoiding
f32andf64; you also need to configure your compiler correctly. This has consequences for how we solve this problem, below.)Possible solutions
Given this background, I'd argue that "
libcorewithout floats" is a fairly well-defined and principled concept, and not just, for example, a rare pathological configuration to support one broken vendor.There are several different ways that this might be implemented:
f32andf64when buildinglibcore. This avoids tripping over places where the ABI mandates the use of SSE2 registers for floating point, as in rust-lang/rust#26449. The rust-barebones-kernellibcore_nofp.patchshows that this is trivially easy to do.f32andf64support out oflibcoreand into a higher-level crate. I don't have a good feel for the tradeoffs here—perhaps it would be good to avoid crate proliferation—but this is one possible workaround.x86_64(rust-lang/rust#26449 again), so it seems like this approach is susceptible to bit rot.libcorewith floats and then try to remove them again with LTO. This is hackish, and it requires the developer to leave SSE2 enabled at compilation time, which may allow SSE2-based optimizations to slip in even wheref32andf64are never mentioned, which will subtly corrupt memory during syscalls and interrupts.What I'd like to see is a situation where people can build things like Linux kernel modules, pure-Rust kernels and (hypothetically) Cortex-M4 (etc.) code without needing to patch
libcore. These all seem like great Rust use cases, and easily disabling floating point is (in several cases) the only missing piece.