@@ -2601,65 +2601,124 @@ where
26012601 fn adjust_for_abi ( & mut self , cx : & C , abi : SpecAbi ) ;
26022602}
26032603
2604+ /// Calculates whether a function's ABI can unwind or not.
2605+ ///
2606+ /// This takes two primary parameters:
2607+ ///
2608+ /// * `codegen_fn_attr_flags` - these are flags calculated as part of the
2609+ /// codegen attrs for a defined function. For function pointers this set of
2610+ /// flags is the empty set. This is only applicable for Rust-defined
2611+ /// functions, and generally isn't needed except for small optimizations where
2612+ /// we try to say a function which otherwise might look like it could unwind
2613+ /// doesn't actually unwind (such as for intrinsics and such).
2614+ ///
2615+ /// * `abi` - this is the ABI that the function is defined with. This is the
2616+ /// primary factor for determining whether a function can unwind or not.
2617+ ///
2618+ /// Note that in this case unwinding is not necessarily panicking in Rust. Rust
2619+ /// panics are implemented with unwinds on most platform (when
2620+ /// `-Cpanic=unwind`), but this also accounts for `-Cpanic=abort` build modes.
2621+ /// Notably unwinding is disallowed for more non-Rust ABIs unless it's
2622+ /// specifically in the name (e.g. `"C-unwind"`). Unwinding within each ABI is
2623+ /// defined for each ABI individually, but it always corresponds to some form of
2624+ /// stack-based unwinding (the exact mechanism of which varies
2625+ /// platform-by-platform).
2626+ ///
2627+ /// Rust functions are classfied whether or not they can unwind based on the
2628+ /// active "panic strategy". In other words Rust functions are considered to
2629+ /// unwind in `-Cpanic=unwind` mode and cannot unwind in `-Cpanic=abort` mode.
2630+ /// Note that Rust supports intermingling panic=abort and panic=unwind code, but
2631+ /// only if the final panic mode is panic=abort. In this scenario any code
2632+ /// previously compiled assuming that a function can unwind is still correct, it
2633+ /// just never happens to actually unwind at runtime.
2634+ ///
2635+ /// This function's answer to whether or not a function can unwind is quite
2636+ /// impactful throughout the compiler. This affects things like:
2637+ ///
2638+ /// * Calling a function which can't unwind means codegen simply ignores any
2639+ /// associated unwinding cleanup.
2640+ /// * Calling a function which can unwind from a function which can't unwind
2641+ /// causes the `abort_unwinding_calls` MIR pass to insert a landing pad that
2642+ /// aborts the process.
2643+ /// * This affects whether functions have the LLVM `nounwind` attribute, which
2644+ /// affects various optimizations and codegen.
2645+ ///
2646+ /// FIXME: this is actually buggy with respect to Rust functions. Rust functions
2647+ /// compiled with `-Cpanic=unwind` and referenced from another crate compiled
2648+ /// with `-Cpanic=abort` will look like they can't unwind when in fact they
2649+ /// might (from a foreign exception or similar).
26042650pub fn fn_can_unwind (
2605- panic_strategy : PanicStrategy ,
2651+ tcx : TyCtxt < ' tcx > ,
26062652 codegen_fn_attr_flags : CodegenFnAttrFlags ,
2607- call_conv : Conv ,
26082653 abi : SpecAbi ,
26092654) -> bool {
2610- if panic_strategy != PanicStrategy :: Unwind {
2611- // In panic=abort mode we assume nothing can unwind anywhere, so
2612- // optimize based on this!
2613- false
2614- } else if codegen_fn_attr_flags. contains ( CodegenFnAttrFlags :: UNWIND ) {
2615- // If a specific #[unwind] attribute is present, use that.
2616- true
2617- } else if codegen_fn_attr_flags. contains ( CodegenFnAttrFlags :: RUSTC_ALLOCATOR_NOUNWIND ) {
2618- // Special attribute for allocator functions, which can't unwind.
2619- false
2620- } else {
2621- if call_conv == Conv :: Rust {
2622- // Any Rust method (or `extern "Rust" fn` or `extern
2623- // "rust-call" fn`) is explicitly allowed to unwind
2624- // (unless it has no-unwind attribute, handled above).
2625- true
2626- } else {
2627- // Anything else is either:
2628- //
2629- // 1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or
2630- //
2631- // 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
2632- //
2633- // In both of these cases, we should refer to the ABI to determine whether or not we
2634- // should unwind. See Rust RFC 2945 for more information on this behavior, here:
2635- // https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
2636- use SpecAbi :: * ;
2637- match abi {
2638- C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
2639- unwind
2640- }
2641- Cdecl
2642- | Fastcall
2643- | Vectorcall
2644- | Aapcs
2645- | Win64
2646- | SysV64
2647- | PtxKernel
2648- | Msp430Interrupt
2649- | X86Interrupt
2650- | AmdGpuKernel
2651- | EfiApi
2652- | AvrInterrupt
2653- | AvrNonBlockingInterrupt
2654- | CCmseNonSecureCall
2655- | Wasm
2656- | RustIntrinsic
2657- | PlatformIntrinsic
2658- | Unadjusted => false ,
2659- // In the `if` above, we checked for functions with the Rust calling convention.
2660- Rust | RustCall => unreachable ! ( ) ,
2661- }
2655+ // Special attribute for functions which can't unwind.
2656+ if codegen_fn_attr_flags. contains ( CodegenFnAttrFlags :: NEVER_UNWIND ) {
2657+ return false ;
2658+ }
2659+
2660+ // Otherwise if this isn't special then unwinding is generally determined by
2661+ // the ABI of the itself. ABIs like `C` have variants which also
2662+ // specifically allow unwinding (`C-unwind`), but not all platform-specific
2663+ // ABIs have such an option. Otherwise the only other thing here is Rust
2664+ // itself, and those ABIs are determined by the panic strategy configured
2665+ // for this compilation.
2666+ //
2667+ // Unfortunately at this time there's also another caveat. Rust [RFC
2668+ // 2945][rfc] has been accepted and is in the process of being implemented
2669+ // and stabilized. In this interim state we need to deal with historical
2670+ // rustc behavior as well as plan for future rustc behavior.
2671+ //
2672+ // Historically functions declared with `extern "C"` were marked at the
2673+ // codegen layer as `nounwind`. This happened regardless of `panic=unwind`
2674+ // or not. This is UB for functions in `panic=unwind` mode that then
2675+ // actually panic and unwind. Note that this behavior is true for both
2676+ // externally declared functions as well as Rust-defined function.
2677+ //
2678+ // To fix this UB rustc would like to change in the future to catch unwinds
2679+ // from function calls that may unwind within a Rust-defined `extern "C"`
2680+ // function and forcibly abort the process, thereby respecting the
2681+ // `nounwind` attribut emitted for `extern "C"`. This behavior change isn't
2682+ // ready to roll out, so determining whether or not the `C` family of ABIs
2683+ // unwinds is conditional not only on their definition but also whether the
2684+ // `#![feature(c_unwind)]` feature gate is active.
2685+ //
2686+ // Note that this means that unlike historical compilers rustc now, by
2687+ // default, unconditionally thinks that the `C` ABI may unwind. This will
2688+ // prevent some optimization opportunities, however, so we try to scope this
2689+ // change and only assume that `C` unwinds with `panic=unwind` (as opposed
2690+ // to `panic=abort`).
2691+ //
2692+ // Eventually the check against `c_unwind` here will ideally get removed and
2693+ // this'll be a little cleaner as it'll be a straightforward check of the
2694+ // ABI.
2695+ //
2696+ // [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
2697+ use SpecAbi :: * ;
2698+ match abi {
2699+ C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
2700+ unwind
2701+ || ( !tcx. features ( ) . c_unwind && tcx. sess . panic_strategy ( ) == PanicStrategy :: Unwind )
26622702 }
2703+ Cdecl
2704+ | Fastcall
2705+ | Vectorcall
2706+ | Aapcs
2707+ | Win64
2708+ | SysV64
2709+ | PtxKernel
2710+ | Msp430Interrupt
2711+ | X86Interrupt
2712+ | AmdGpuKernel
2713+ | EfiApi
2714+ | AvrInterrupt
2715+ | AvrNonBlockingInterrupt
2716+ | CCmseNonSecureCall
2717+ | Wasm
2718+ | RustIntrinsic
2719+ | PlatformIntrinsic
2720+ | Unadjusted => false ,
2721+ Rust | RustCall => tcx. sess . panic_strategy ( ) == PanicStrategy :: Unwind ,
26632722 }
26642723}
26652724
@@ -2695,11 +2754,6 @@ pub fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv {
26952754 }
26962755}
26972756
2698- pub fn fn_ptr_codegen_fn_attr_flags ( ) -> CodegenFnAttrFlags {
2699- // Assume that fn pointers may always unwind
2700- CodegenFnAttrFlags :: UNWIND
2701- }
2702-
27032757impl < ' tcx , C > FnAbiExt < ' tcx , C > for call:: FnAbi < ' tcx , Ty < ' tcx > >
27042758where
27052759 C : LayoutOf < Ty = Ty < ' tcx > , TyAndLayout = TyAndLayout < ' tcx > >
@@ -2709,7 +2763,7 @@ where
27092763 + HasParamEnv < ' tcx > ,
27102764{
27112765 fn of_fn_ptr ( cx : & C , sig : ty:: PolyFnSig < ' tcx > , extra_args : & [ Ty < ' tcx > ] ) -> Self {
2712- call:: FnAbi :: new_internal ( cx, sig, extra_args, None , fn_ptr_codegen_fn_attr_flags ( ) , false )
2766+ call:: FnAbi :: new_internal ( cx, sig, extra_args, None , CodegenFnAttrFlags :: empty ( ) , false )
27132767 }
27142768
27152769 fn of_instance ( cx : & C , instance : ty:: Instance < ' tcx > , extra_args : & [ Ty < ' tcx > ] ) -> Self {
@@ -2901,12 +2955,7 @@ where
29012955 c_variadic : sig. c_variadic ,
29022956 fixed_count : inputs. len ( ) ,
29032957 conv,
2904- can_unwind : fn_can_unwind (
2905- cx. tcx ( ) . sess . panic_strategy ( ) ,
2906- codegen_fn_attr_flags,
2907- conv,
2908- sig. abi ,
2909- ) ,
2958+ can_unwind : fn_can_unwind ( cx. tcx ( ) , codegen_fn_attr_flags, sig. abi ) ,
29102959 } ;
29112960 fn_abi. adjust_for_abi ( cx, sig. abi ) ;
29122961 debug ! ( "FnAbi::new_internal = {:?}" , fn_abi) ;
0 commit comments