Handle always_inline as GNU inline

this is needed for multi-file testcases using stdio.h, as
the __sputc function is implemented as a extern inline
function (with gnu_inline attribute, but we don't support that for now).

Without this change that leads to multiply defined symbols when using
multiple units including stdio.h.

It also has an always_inline attribute, which we can use to guide our
behaviour, as in ISO-C an always_inline can't be defined with ISO
'extern inline' semantics.  This is the minimal change and not a full
implementation of GNU inline semantics, which would require thorough
testcases.

If __clang__ would be defined the header would make use of C99 semantics,
which would work for us.  It would also do that if _GNUC_ wouldn't be
defined.  But we can't do the latter (as the whole MacOSX SDK refuses
to be compiled with anything not defining that).  I haven't tested
defining __clang__, but suspect that's going to be problematic.
This commit is contained in:
Michael Matz 2020-05-22 05:17:02 +02:00
parent 5d2f4cb403
commit f18f865159
3 changed files with 23 additions and 5 deletions

4
tcc.h
View File

@ -494,7 +494,9 @@ struct FuncAttr {
func_noreturn : 1, /* attribute((noreturn)) */
func_ctor : 1, /* attribute((constructor)) */
func_dtor : 1, /* attribute((destructor)) */
func_args : 8; /* PE __stdcall args */
func_args : 8, /* PE __stdcall args */
func_alwinl : 1, /* always_inline */
xxxx :15;
};
/* symbol management */

View File

@ -4075,14 +4075,18 @@ redo:
skip(')');
break;
}
case TOK_CONSTRUCTOR1:
case TOK_CONSTRUCTOR2:
case TOK_CONSTRUCTOR1:
case TOK_CONSTRUCTOR2:
ad->f.func_ctor = 1;
break;
case TOK_DESTRUCTOR1:
case TOK_DESTRUCTOR2:
case TOK_DESTRUCTOR1:
case TOK_DESTRUCTOR2:
ad->f.func_dtor = 1;
break;
case TOK_ALWAYS_INLINE1:
case TOK_ALWAYS_INLINE2:
ad->f.func_alwinl = 1;
break;
case TOK_SECTION1:
case TOK_SECTION2:
skip('(');
@ -8225,6 +8229,16 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym)
sym = type.ref;
if (sym->f.func_type == FUNC_OLD && l == VT_CONST)
decl0(VT_CMP, 0, sym);
if (sym->f.func_alwinl
&& ((type.t & (VT_EXTERN | VT_INLINE))
== (VT_EXTERN | VT_INLINE))) {
/* always_inline functions must be handled as if they
don't generate multiple global defs, even if extern
inline, i.e. GNU inline semantics for those. Rewrite
them into static inline. */
type.t &= ~VT_EXTERN;
type.t |= VT_STATIC;
}
/* always compile 'extern inline' */
if (type.t & VT_EXTERN)
type.t &= ~VT_INLINE;

View File

@ -132,6 +132,8 @@
DEF(TOK_CONSTRUCTOR2, "__constructor__")
DEF(TOK_DESTRUCTOR1, "destructor")
DEF(TOK_DESTRUCTOR2, "__destructor__")
DEF(TOK_ALWAYS_INLINE1, "always_inline")
DEF(TOK_ALWAYS_INLINE2, "__always_inline__")
DEF(TOK_MODE, "__mode__")
DEF(TOK_MODE_QI, "__QI__")