Skip to content

Commit 4047642

Browse files
mborlanddaveedvdv-nvidia
authored andcommitted
Allow inline and __attribute((noinline)) to be used together without warning in C++ [GH #183]
1 parent ec2f12c commit 4047642

23 files changed

Lines changed: 257 additions & 51 deletions

File tree

‎src/attribute.c‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6059,6 +6059,37 @@ that entity.
60596059

60606060
#endif /* GNU_NAKED_ATTRIBUTE_ALLOWED || MICROSOFT_EXTENSIONS_ALLOWED */
60616061

6062+
static a_boolean gnu_accepts_noinline_on_inline_routine(an_attribute_ptr ap)
6063+
/*
6064+
ap is a GNU "noinline" attribute being applied to an inline routine. Return
6065+
TRUE if GCC accepts that combination silently in the context of ap. For
6066+
example:
6067+
6068+
inline __attribute__((noinline)) int f() { return 0; } // No warning.
6069+
6070+
This idiom is used to ensure a noinline function in a header file does not
6071+
cause linker errors. GCC in C mode will warn on this while it does not in C++.
6072+
*/
6073+
{
6074+
a_boolean result = FALSE;
6075+
a_decl_parse_state *dps = (a_decl_parse_state*)ap->assoc_info;
6076+
6077+
if (!C_mode()) {
6078+
if (dps == NULL) {
6079+
/* The attribute is not being applied as part of a declaration, which
6080+
means it is being copied to a template instance. */
6081+
result = TRUE;
6082+
} else {
6083+
result = ap->on_primary_declaration &&
6084+
(dps->dso_flags &
6085+
(DSO_INLINE | DSO_CONSTEXPR | DSO_CONSTEVAL)) != 0 &&
6086+
!dps->inline_on_earlier_decl;
6087+
} /* if */
6088+
} /* if */
6089+
return result;
6090+
} /* gnu_accepts_noinline_on_inline_routine */
6091+
6092+
60626093
static char* apply_noinline_attr(an_attribute_ptr ap,
60636094
char *entity,
60646095
an_il_entry_kind entity_kind)
@@ -6069,15 +6100,16 @@ and return the entity.
60696100
{
60706101
if (entity_kind == iek_routine) {
60716102
a_routine_ptr rp = (a_routine_ptr)entity;
6103+
a_boolean has_always_inline = FALSE;
6104+
#if GNU_EXTENSIONS_ALLOWED
6105+
has_always_inline = find_attribute(ak_always_inline,
6106+
rp->source_corresp.attributes) != NULL;
6107+
#endif /* GNU_EXTENSIONS_ALLOWED */
60726108
rp->never_inline = TRUE;
60736109
if (rp->is_inline && is_gcc_attribute(ap) &&
60746110
(!rp->source_corresp.is_class_member ||
6075-
rp->defined_outside_of_parent
6076-
#if GNU_EXTENSIONS_ALLOWED
6077-
|| find_attribute(ak_always_inline, rp->source_corresp.attributes)
6078-
!= NULL
6079-
#endif /* GNU_EXTENSIONS_ALLOWED */
6080-
)) {
6111+
rp->defined_outside_of_parent || has_always_inline) &&
6112+
(has_always_inline || !gnu_accepts_noinline_on_inline_routine(ap))) {
60816113
pos_warning(ec_inline_gnu_noinline_conflict, &ap->position);
60826114
} /* if */
60836115
} else {

‎src/decls.c‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ be restored).
314314
dps->has_direct_initializer = FALSE;
315315
dps->first_decl = FALSE;
316316
dps->first_decl_of_predeclared_entity = FALSE;
317+
dps->inline_on_earlier_decl = FALSE;
317318
dps->override_okay = FALSE;
318319
dps->initializer_is_expr_list = FALSE;
319320
dps->no_special_cli_class_type_check = FALSE;
@@ -10374,6 +10375,9 @@ skip_overloading:;
1037410375
is_nothrow_type(rtp)) {
1037510376
routine_ptr->never_throws = TRUE;
1037610377
} /* if */
10378+
/* Record whether the routine was inline before this declaration (used when
10379+
applying the GNU "noinline" attribute). */
10380+
dps->inline_on_earlier_decl = routine_ptr->is_inline;
1037710381
if (func_info->is_inline) set_inline_flag(routine_ptr, TRUE);
1037810382
if (use_std_c99_inlining && !idlb.is_block_extern_decl) {
1037910383
/* In C99 mode the definition_for_inlining_only flag is set only if that is
@@ -11511,6 +11515,9 @@ definition of a member function of a class template.
1151111515
rout_ptr->is_constexpr = TRUE;
1151211516
} /* if */
1151311517
} /* if */
11518+
/* Record whether the routine was inline before this declaration (used when
11519+
applying the GNU "noinline" attribute). */
11520+
dps->inline_on_earlier_decl = rout_ptr->is_inline;
1151411521
if (!rout_ptr->is_inline) {
1151511522
if (func_info->is_inline || rout_ptr->is_declared_constexpr) {
1151611523
set_inline_flag(rout_ptr, TRUE);

‎src/decls.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,11 @@ typedef struct a_decl_parse_state {
10611061
/* TRUE if this is the first declaration of a variable
10621062
or function that was predeclared by the front
10631063
end. */
1064+
a_bit_field inline_on_earlier_decl:1;
1065+
/* TRUE if this declaration redeclares a function that
1066+
was already inline before this declaration (e.g.,
1067+
because an earlier declaration specified
1068+
"inline"). */
10641069
a_bit_field redeclares_tag:1;
10651070
/* TRUE if the specifiers include a tag name (like
10661071
"struct S" or "enum E") that must match a prior

‎src/func_def.c‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2531,6 +2531,9 @@ member declaration (allowed in some Microsoft modes only).
25312531
specification, and issue one if needed. */
25322532
issue_no_exception_support_diag_on_throw_spec(func_info);
25332533
} /* if */
2534+
/* Record whether the routine was inline before this declaration (used when
2535+
applying the GNU "noinline" attribute). */
2536+
dps->inline_on_earlier_decl = rp->is_inline;
25342537
if (func_info->is_inline) {
25352538
if (!rp->is_inline) {
25362539
set_inline_flag(rp, TRUE);

‎src/il.c‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3284,12 +3284,15 @@ set).
32843284
#endif /* MICROSOFT_EXTENSIONS_ALLOWED */
32853285
#if GNU_EXTENSIONS_ALLOWED
32863286
} else if (rp->never_inline && gnu_mode &&
3287+
(!rp->is_template_function || rp->is_specialized ||
3288+
rp->is_prototype_instantiation) &&
32873289
(!rp->source_corresp.is_class_member ||
32883290
rp->defined_outside_of_parent ||
32893291
find_attribute(ak_always_inline, rp->source_corresp.attributes)
32903292
!= NULL)) {
32913293
/* Was explicitly marked "noinline" previously and is now explicitly
3292-
marked inline. */
3294+
marked inline. (An instance generated from a template is not
3295+
checked: the declarations of the template itself were.) */
32933296
pos_warning(ec_inline_gnu_noinline_conflict, &error_position);
32943297
#endif /* GNU_EXTENSIONS_ALLOWED */
32953298
} /* if */
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//allowed_words: NOINLINE
2+
[GH #183]
3+
GNU compatibility: "inline" combined with "noinline" on a definition
4+
5+
In GNU and Clang C++ modes, the front end warned that a routine is both
6+
"inline" and "noinline" when a function definition was declared "inline" and
7+
had the GNU "noinline" attribute, but GCC accepts that combination silently
8+
(it is used, e.g., by Boost through "BOOST_NOINLINE inline"). For example:
9+
10+
inline __attribute__((noinline)) int f(int x) { // No warning now.
11+
return x;
12+
}
13+
14+
In C modes, where GCC and Clang warn about the combination in all of those
15+
cases, the behavior is unchanged.
16+
17+
Also, instantiating a function template (or a member function of a class
18+
template) that is both "inline" and "noinline" no longer repeats the warning
19+
"At end of source" for each instance, only the template declarations are
20+
checked.

‎tests/expectations/edg_x86_32_cp/regressions.log‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
gh-183-redecl.sft.cpp:fp:-DTEST_NUMBER=1 --c++17:REQUIREMENTS NOT MET:
12
gh-309.sft.cpp:fp:-DTEST_NUMBER=1 --g++:REQUIREMENTS NOT MET:
23
gh-309.sft.cpp:fp:-DTEST_NUMBER=2 --g++ --no_exceptions:REQUIREMENTS NOT MET:
34
immediate-pragma-lost.sft.cpp:cppbe:-DTEST_NUMBER=1:PASS:

‎tests/expectations/edg_x86_64_cp/regressions.log‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
gh-183-redecl.sft.cpp:fp:-DTEST_NUMBER=1 --c++17:REQUIREMENTS NOT MET:
12
gh-309.sft.cpp:fp:-DTEST_NUMBER=1 --g++:REQUIREMENTS NOT MET:
23
gh-309.sft.cpp:fp:-DTEST_NUMBER=2 --g++ --no_exceptions:REQUIREMENTS NOT MET:
34
immediate-pragma-lost.sft.cpp:cppbe:-DTEST_NUMBER=1:PASS:

‎tests/tests/imported/gnu/cpp/.Wattributes-3.rto/default.1.1.txt‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ fe_only -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_d
33
ATTR ((__always_inline__))
44
^
55

6-
"Test_name.c", line 53: warning: routine is both "inline" and "noinline"
7-
inline ATTR ((__noinline__))
8-
^
9-
106
"Test_name.c", line 58: warning: the "always_inline" attribute is ignored on non-inline functions
117
ATTR ((__always_inline__))
128
^

‎tests/tests/imported/gnu/cpp/ext/.attr-noinline-2.rto/default.1.1.txt‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,3 @@ fe_only -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_d
33
fnoinline_always_inline<Special>() // { dg-bogus "follows declaration" }
44
^
55

6-
"Test_name.c", line 58: warning: routine is both "inline" and "noinline"
7-
fnoinline_always_inline<Special>() // { dg-bogus "follows declaration" }
8-
^
9-

0 commit comments

Comments
 (0)