-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Missed-optimization: extern "C" fn type calls are not nounwind #64090
Comments
I don't think this is a bug. At least if we accept rust-lang/rfcs#2753, until we can track the unwinding ABI in fn ptr types, we cannot and should not assume that functions can't unwind based only on their call ABI. Before changing our codegen for fn ptrs, IMO we should accept an RFC that basically says the opposite of rust-lang/rfcs#2753. |
FWIW LLVM has no way of tracking "nounwind" for function pointers and since |
@nagisa we can apply attributes to calls through function pointers, e.g., see how clang does it: https://rust.godbolt.org/z/YMv6NJ @RalfJung That discussion belongs to the RFC PR. |
The code generated by |
@nagisa specifically which ones? |
@nagisa See https://godbolt.org/z/7nnS_v, we currently optimize extern "C" { fn foo(); }
static FOO: unsafe extern "C" fn() = foo;
pub unsafe fn bar() { FOO() } to essentially this extern "C" { fn foo(); }
pub unsafe fn bar() { foo() } // bar is nounwind because foo is nounwind This optimization is sound if the Declaring a function with the wrong ABI cannot probably be made UB since it is something that safe Rust code can do, calling an incorrect function declaration is definitely UB. Here, we never call the declaration, only the function pointer. If it were ok for the function pointer to unwind, then AFAICT at least two of the optimizations above (removing the landing pads and making bar nounwind) are unsound and we'd need to stop doing them. So AFAICT we are already optimizing Rust code under the assumption that the |
Even if we had a separate As such, either:
Either way, it seems like an orthogonal issue to whether or not |
We are optimizing based on the assumption that calling a function through a function pointer behaves the same as if calling the function directly. |
Well, right now we are (in our handling of fn ptrs) future compatible with
rust-lang/rfcs#2753, I imagine. |
I have no idea what you mean by this. Calling an Unwinding from an |
If you do this: extern "C" { fn foo(); } // Declared to never unwind
static BAR: extern "C+unwind" unsafe fn() = foo as _; // this cast is sound
unsafe fn bar() { BAR() } // BOOM: bar is optimized as nounwind We will optimize The declaration itself cannot be UB. The cast itself is IMO sound: functions that never unwind can definitely be called from function pointers that support unwinding. So either this optimization is unsound or it is UB to call a function declared as not-unwinding via a function pointer that supports unwinding if that function actually unwinds. IMO, this is just another flavor of: mod bad { extern "C" { fn foo(); } } // never unwinds
extern "C+unwind" { fn foo(); } // BOOM: becomes nounwind because of bad::foo |
That certainly is UB, as I said above. It is UB for a function to unwind if at least one of the caller and callee signature says this may not unwind. In your case, the callee signature declared here extern "C" { fn foo(); } // Declared to never unwind says "cannot unwind", and hence this may not unwind, no matter the signature used by the caller. |
But that's not the callee signature; it's only a signature we're using on the caller's end to declare the symbol. I'm assuming that the callee's actual implementation (which might be in another crate, or written in a different language altogether) is properly declared as unwinding. |
I would say it is the callee signature; if we have both declarations and a definition of the same symbol floating around and they are not the same then we are looking at rust-lang/unsafe-code-guidelines#198 which is an orthogonal issue. |
See https://rust.godbolt.org/z/9UvEyu
When the function
extern "C" { fn bar(); }
is called, thenounwind
function is compiled to:However, when the function type
static bar: extern "C" fn();
is called, this sub-optimal machine code is emitted:This means that we can't call the large majority of C FFI functions, which cannot unwind, and all of the C++
noexcept
functions, which cannot unwind either, from Rust function pointers efficiently.The text was updated successfully, but these errors were encountered: