-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Mark extern functions as nounwind + other unwinding-based improvements #21186
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @eddyb (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. The way Github handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see CONTRIBUTING.md for more information. |
These seem like they might be potentially dangerous attributes to be playing around with, so could they be behind a feature gate for now? We may also want to scrutinize the naming of these attributes in the future before allowing full use of them. Also, can you be sure to add some positive and negative tests for the cases added here? Out of curiosity, do you have statistics about code size in before/after cases as well? |
|
||
let pad_ret_val = builder.landing_pad(pad_ty, llpersonality, 1us); | ||
builder.add_clause(pad_ret_val, C_null(Type::i8p(ccx))); | ||
let trap = ccx.get_intrinsic(&("llvm.trap")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be a little unfortunate to call llvm.trap
here as unwinding across an extern fn
(while not safe) may be a common newbie mistake to make. This unfortunately doesn't really aid in debugging as well as it basically just crashes the process.
We may want to explore a new lang item here which has the chance to print something, or perhaps a different strategy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, though there are several other places do that do similarly-helpful things, so a more general-purpose solution might be more appropriate.
Also, a trap will still be caught by a debugger and allow for a backtrace to be printed, so it's at least better than whatever happens when you do unwind into C.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I was thinking that this could take the same strategy as divide-by-zero or out-of-bounds only hardwired to an abort function instead of a panic function. I didn't know about the debugger though, that's pretty slick!
Thanks for this! Seems like our FFI is about to get even better! |
Hmm, looks like we were already assuming that |
Updated and added a test, should be good to go now. |
It seems mildly inconsistent to call them |
Could you add a test as well for the feature gates? (make sure they're actually gating) |
@@ -653,6 +653,8 @@ impl LintPass for UnusedAttributes { | |||
"no_debug", | |||
"omit_gdb_pretty_printer_section", | |||
"unsafe_no_drop_flag", | |||
"can_unwind", | |||
"nounwind", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/nounwind/unsafe_no_unwind
This looks pretty great to me, nice work @Aatch! I'm basically happy with the implementation, but I would like to canvas for some more opinions specifically about:
|
Just to put my answers in (biased as they may be):
|
To be clear, I totally agree with your answers to questions 1/3, just wanna make sure we're all on the same page :) For point 2, I do think it's ok to land now, I would just much rather have a nicer message. I've spent hours tracking core dumps before only to realize it was OOM and it have been super duper helpful if a message was actually printed! |
Ok, we chatted about this today in the weekly meeting and here's an update. First off, thanks again for the PR! We're all pretty excited to take advantage of the optimizations LLVM provides. For now, however, we're thinking we may want step back and make an RFC for this PR before landing. The primary change we're worried about is the difference in unwinding semantics when unwinding past an extern fn and we think that there may be some design space to explore before committing to one strategy. We had some fleeting ideas such as using a similar strategy for unwinding in destructors perhaps. Regardless, we're a little unsure that these are the semantics we'd want to commit to, so we'd just want to canvas some more feedback before moving forward in this respect. I'd be more than willing to help out in writing up, proofing, or otherwise just reviewing an RFC for this area. Feel free to ping me on IRC about this whenever! To move forward this this specific PR I think we can take a slightly altered strategy as well. In the past we've accepted implementation details which improved the efficiency of standard library primitives (e.g. NonZero) before we were ready to commit to a public API (it's entirely an implementation detail). Along these lines I think that at bare minimum the attributes this PR adds can be accepted pretty quickly (due to being behind a feature gate). I think we may also be able to take advantage of flagging FFI declarations (extern { ... } blocks) as nounwind for LLVM itself, due to unwinding being undefined behavior in that scenario today. Does that make sense for moving forward? Again, thanks for the work in this area, it's definitely much appreciated! |
Functions that use an ABI other than the Rust one are marked as nounwind. Rust functions that use non-Rust ABI are also marked nounwind. To allow for this, the unwinding is caught and we abort the process. Added a `#[can_unwind]` attribute that allows functions using a non-Rust ABI to unwind without aborting. This is required for the unwinding code in libstd to function.
Adds a nounwind attribute that allows functions to be marked as not unwinding. Most useful for cross-crate usage when the nounwind LLVM attribute cannot be inferred. Also fixes marking extern fns as nounwind when the function decl comes from another crate.
We were not allowing for the possibility that we might unwind out of a foreign abi function. Now that we allow for some of these functions to unwind, generate the proper code so clean-ups are performed. This also allowed me to write a test for the functionality, which I have now done.
What's the status of this PR? It's quite old at this point. |
@steveklabnik this is blocked on rust-lang/rfcs#638 |
I'm going to close this in favor of the associated RFC while we work that out. |
This PR does a few things:
extern fn foo() { ... }
functions and aborts. This prevents us from unwinding from Rust code into C code.#[can_unwind]
attribute for extern functions. This is for when we know a non-Rust ABI function will have to unwind. Used mainly in the unwinding implementation.#[nounwind]
attribute. It is intended for when a function that won't unwind is used cross-crate, but using#[inline]
is not appropriate. It is currently used for theoom
function inliballoc
Fixes #18510
Fixes #18512
r? @eddyb