-
Notifications
You must be signed in to change notification settings - Fork 97
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
Give the nounwind LLVM attribute to all public APIs #208
Conversation
Is there a measurable benefit to this PR? I understand what |
This removed some panic code in #198 .
I wouldn't say rarely. Calling math functions in hot loops is something a lot of code does, and this is required to replace these functions via compiler-builtins.
#198 automatically enforced this, but that enforcement is not part of this PR. |
FWIW this is a somewhat frustrating sentiment to read. There's a massive number of changes in #198 and attributing one small portion of that can't really easily be verified and it could easily be mistaken for any other change in the PR. I've theorized that what's happening here is if
This is misunderstanding how these functions are called though. No one is explicitly calling these functions but it's typically through LLVM's lowering of intrinsics, which don't at all take into account
Enforcing it isn't really addressing the issue though? It's just enforcing that it looks weird, but papers over the fact that it still looks different than normal idiomatic code. Like it's definitely better to verify it's all consistent, but I continue to not understand why this change is being made in the first place. |
I don't know how that theory fits with the fact that My theory is that, when some functions are too large, LLVM might fail to infer This is why both, inlining, and extern "C", separately, solve the issue. One inlines the code, so LLVM does not need to emit any panic handling code "just in case", and the other tells llvm that unwinding cannot happen, so it can remove such code.
I said that these functions are called via compiler-builtins. LLVM math intrinsics, when not optimized away, generate calls to the C libm symbols, and compiler-builtins provides these symbols, such that linking doesn't fail. These symbols need to be What I don't understand is why these functions are not made For example, you mentioned that making this pub extern "C" fn libm_api(...) -> ... { libm::libm_api(...) } If So I don't see the point of marking these functions as This also complicates things if we want to meaningfully test the library, by running a libm testsuite for C. We could wrap the library in a C API here as well for these tests... but then we need to "duplicate" part of the compiler-builtins code for that. |
Er ok hm so there's a lot do digest there, I'll try responding to all the pieces though:
This isn't quite true. LLVM has pretty trivial inference of the
I've been thinking about why panics are "going away", and I think it just gets down to The reason The reason I quite strongly feel
This is not true, if an
I think this isn't quite right? I merely inherited this crate and the main use case I'm aware of is that it's integrated into compiler-builtins. There's way more intrinsics in this crate than compiler-builtins uses, and there are PRs to add more. I assume that means someone is using this code for something else, such as an external crate which just calls into the Rust ABI.
As I've mentioned before, we always have to wrap APIs. No matter what we do with the ABI we can't change the symbol mangling, so an unmangled wrapper is always required. |
Note that this is not fixed yet, and it is unclear when a fix will land. Right now, if an extern "C" function panics, the panic escapes from a nounwind function, and you get UB.
I agree.
So I think I didn't properly explain what I meant. Given: fn foo(x: f32) {
// ... huge function ..
if always_false(x) { panic!() }
}
fn libm_api(x: f32) {
// ...huge function...
foo( map_such_that_foo_neven_panics(x) );
} In When So arguably, the error here is in the code that inserted the panic branch in I think we should identify using
AFAICT, compiler-builtins doesn't expose all intrinsics required, and if we were to expose all float methods in libcore, which we don't yet do, we would run into linker errors until that is fixed. People using this crate were using the We should expose the methods in libcore, and tell people to use those directly. I think this is a much better solution, since it allows LLVM to perform higher-level optimizations on floating-point math. |
You mean that a |
WTF!? |
@burrbull you would call Were you to instead call I think this is the main use case for the crate. (*) right now this is done in compiler-builtins for this libm, but I wouldn't mind being able to link whatever libm you want, e.g., there are better libms available than the system one, and other compilers like clang let you pick them. I feel its a bit weird that this particular libm is special. |
Er sorry there's still a lot to respond to, so I'm going to try to continue to go piece by piece:
This is not the case on nightly, and the patch has been perpetually backed out for "reasons" on stable/beta for like 5 releases. I think there's a PR to actually revert the nightly behavior and leave it as UB. In any case this really feels like an extreme case of lawyering that isn't super productive for this conversation. The fact of the matter is that this is a bug in the language/compiler and we shouldn't be designing around it (especially as there's no practical impact right now).
My point boils down to
I don't think this is true, the functions are still public. The functions were always a different style of using these methods and presumably someone is using this crate because APIs keep getting added.
No, we have intentionally not done this. Many routines here have more optimized versions in As a note, I've literally spent at least half an hour every day this week dealing with this PR and related issues. This is way too much time to be spending on whether or not we should tag APIs with I'm quite exhausted and don't really want to have to keep debating this. I'm very seriously considering just removing |
Which nightly? This is with today's nightly: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=3cf9bf07128c54c92daee6ffd6537b92
The only thing
I didn't suggest this ? Where are you getting this ? |
I'll just use a fork, I agree that this is costing too much time. I might need to fork compiler-builtins as well, but such is life. |
The "command dumped core" when you run that is the I interpreted "We should expose the methods in libcore" as "we should move the functions into compiler-builtins so the intrinsic calls can be moved from libstd to libcore so we can access all this functionality from libcore". That leads to the performance issues I mentioned.
To be clear I think it won't be productive to have entire forks of this. It'll just end up causing confusion for users about which to use. AFAIK this is all just internal implementation details of this crate and isn't anything fundamental, which is why I'm not super interested in litigating this further. If you see a reason for a full fork to proceed independently that's different and I would prefer to avoid that if possible since it doesn't really end up benefitting many in the long run |
That makes sense.
I did mean that we should move the intrinsics from libstd to libcore. What causes the performance issues you mention is using this libm unconditionally for all targets and binaries, but I did not suggest doing that. We should have an option to denote that a crate links Clang and gcc both allow doing this for C (and much more, like working around ABI differences across libm implementations - we could work around those in the crate that links libm). I don't know if this makes sense, but I need to be able to do floating-point math from libcore, and I want to properly do so by calling LLVM intrinsics, and then resolving these symbols at link time, like all other toolchains do. I am currently manually calling this (and other) libm functions manually in |
I actually thought the whole point was to provide math functions to libcore and, if these are not the most optimized functions then at least it should link to those there are. It's kinda weird that libcore don't have any math functions, I would argue thats an essential part of any programming language. |
We can basically design w/e system we want for linking libm, but that'll likely require new features/support from rustc itself. Today we're stuck with "if the symbols are in |
@alexcrichton This might be a dumb question, but how does
This is very similar to the float intrinsics. So how does that work? Is the |
The (to be specific they're actually always compiled in apparently checking locally, but they're only Float intrinsics are sort of the same way but much newer in some regards. On platforms like wasm the intrinsics are always compiled in, on other platforms they're omitted (but this time entirely I think). |
This PR gives all public APIs the
nounwind
LLVM attribute by making themextern "C"
. Note thatextern "C"
only changes the "call ABI" of the functions, it does not change name mangling. A different attribute, like#[no_mangle]
, would be required change name mangling to C name mangling, but that is not done here.For all types that this library uses, the call ABI of the Rust and the C calling convention is the same, so this PR does not change that either.