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