@@ -2601,65 +2601,124 @@ where
2601
2601
fn adjust_for_abi ( & mut self , cx : & C , abi : SpecAbi ) ;
2602
2602
}
2603
2603
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).
2604
2650
pub fn fn_can_unwind (
2605
- panic_strategy : PanicStrategy ,
2651
+ tcx : TyCtxt < ' tcx > ,
2606
2652
codegen_fn_attr_flags : CodegenFnAttrFlags ,
2607
- call_conv : Conv ,
2608
2653
abi : SpecAbi ,
2609
2654
) -> 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 )
2662
2702
}
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 ,
2663
2722
}
2664
2723
}
2665
2724
@@ -2695,11 +2754,6 @@ pub fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv {
2695
2754
}
2696
2755
}
2697
2756
2698
- pub fn fn_ptr_codegen_fn_attr_flags ( ) -> CodegenFnAttrFlags {
2699
- // Assume that fn pointers may always unwind
2700
- CodegenFnAttrFlags :: UNWIND
2701
- }
2702
-
2703
2757
impl < ' tcx , C > FnAbiExt < ' tcx , C > for call:: FnAbi < ' tcx , Ty < ' tcx > >
2704
2758
where
2705
2759
C : LayoutOf < Ty = Ty < ' tcx > , TyAndLayout = TyAndLayout < ' tcx > >
@@ -2709,7 +2763,7 @@ where
2709
2763
+ HasParamEnv < ' tcx > ,
2710
2764
{
2711
2765
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 )
2713
2767
}
2714
2768
2715
2769
fn of_instance ( cx : & C , instance : ty:: Instance < ' tcx > , extra_args : & [ Ty < ' tcx > ] ) -> Self {
@@ -2901,12 +2955,7 @@ where
2901
2955
c_variadic : sig. c_variadic ,
2902
2956
fixed_count : inputs. len ( ) ,
2903
2957
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 ) ,
2910
2959
} ;
2911
2960
fn_abi. adjust_for_abi ( cx, sig. abi ) ;
2912
2961
debug ! ( "FnAbi::new_internal = {:?}" , fn_abi) ;
0 commit comments