@@ -6,21 +6,66 @@ use rustc_attr_parsing::InlineAttr;
66use rustc_attr_parsing:: InstructionSetAttr ;
77#[ cfg( feature = "master" ) ]
88use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrFlags ;
9+ use rustc_middle:: mir:: TerminatorKind ;
910use rustc_middle:: ty;
1011
1112use crate :: context:: CodegenCx ;
1213use crate :: gcc_util:: to_gcc_features;
13-
14- /// Get GCC attribute for the provided inline heuristic.
14+ /// Checks if the function `instance` is recursively inline.
15+ /// Returns `false` if a functions is guranteed to be non-recursive, and `true` if it *might* be recursive.
16+ #[ cfg( feature = "master" ) ]
17+ fn resursively_inline < ' gcc , ' tcx > (
18+ cx : & CodegenCx < ' gcc , ' tcx > ,
19+ instance : ty:: Instance < ' tcx > ,
20+ ) -> bool {
21+ // No body, so we can't check if this is recursively inline, so we assume it is.
22+ if !cx. tcx . is_mir_available ( instance. def_id ( ) ) {
23+ return true ;
24+ }
25+ // `expect_local` ought to never fail: we should be checking a function within this codegen unit.
26+ let body = cx. tcx . optimized_mir ( instance. def_id ( ) ) ;
27+ for block in body. basic_blocks . iter ( ) {
28+ let Some ( ref terminator) = block. terminator else { continue } ;
29+ // I assmume that the resursive-inline issue applies only to functions, and not to drops.
30+ // In principle, a recursive, `#[inline(always)]` drop could(?) exist, but I don't think it does.
31+ let TerminatorKind :: Call { ref func, .. } = terminator. kind else { continue } ;
32+ let Some ( ( def, _args) ) = func. const_fn_def ( ) else { continue } ;
33+ // Check if the called function is recursively inline.
34+ if matches ! (
35+ cx. tcx. codegen_fn_attrs( def) . inline,
36+ InlineAttr :: Always | InlineAttr :: Force { .. }
37+ ) {
38+ return true ;
39+ }
40+ }
41+ false
42+ }
43+ /// Get GCC attribute for the provided inline heuristic, attached to `instance`.
1544#[ cfg( feature = "master" ) ]
1645#[ inline]
1746fn inline_attr < ' gcc , ' tcx > (
1847 cx : & CodegenCx < ' gcc , ' tcx > ,
1948 inline : InlineAttr ,
49+ instance : ty:: Instance < ' tcx > ,
2050) -> Option < FnAttribute < ' gcc > > {
2151 match inline {
52+ InlineAttr :: Always => {
53+ // We can't simply always return `always_inline` unconditionally.
54+ // It is *NOT A HINT* and does not work for recurisve functions.
55+ //
56+ // So, it can only be applied *if*:
57+ // The current function does not call any functions makred `#[inline(always)]`.
58+ //
59+ // That prevents issues steming from recursive `#[inline(always)]` at a *relatively* small cost.
60+ // We *only* need to check all the terminators of a function marked with this attribute.
61+ if resursively_inline ( cx, instance) {
62+ Some ( FnAttribute :: Inline )
63+ } else {
64+ Some ( FnAttribute :: AlwaysInline )
65+ }
66+ }
2267 InlineAttr :: Hint => Some ( FnAttribute :: Inline ) ,
23- InlineAttr :: Always | InlineAttr :: Force { .. } => Some ( FnAttribute :: AlwaysInline ) ,
68+ InlineAttr :: Force { .. } => Some ( FnAttribute :: AlwaysInline ) ,
2469 InlineAttr :: Never => {
2570 if cx. sess ( ) . target . arch != "amdgpu" {
2671 Some ( FnAttribute :: NoInline )
@@ -52,7 +97,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
5297 } else {
5398 codegen_fn_attrs. inline
5499 } ;
55- if let Some ( attr) = inline_attr ( cx, inline) {
100+ if let Some ( attr) = inline_attr ( cx, inline, instance ) {
56101 if let FnAttribute :: AlwaysInline = attr {
57102 func. add_attribute ( FnAttribute :: Inline ) ;
58103 }
0 commit comments