TLC for C99 inline implementation

there's no need for two new flags in type.t .  We just can't use
VT_EXTERN as marker if functions are defined or not (like we can
for objects), and then can simply implement the rules of C99/C11
by not overwriting VT_STATIC/VT_EXTERN at all but rather only
look at them.  A function already on the inline list can be
forced by removing the VT_INLINE flag, and then linkage
follows from some combination of VT_STATIC, VT_EXTERN and VT_INLINE.
This commit is contained in:
Michael Matz 2019-06-17 03:34:03 +02:00
parent 4dfc27b101
commit cb8bbf1ab9
3 changed files with 28 additions and 61 deletions

6
tcc.h
View File

@ -886,9 +886,7 @@ struct filespec {
#define VT_STATIC 0x00002000 /* static variable */
#define VT_TYPEDEF 0x00004000 /* typedef definition */
#define VT_INLINE 0x00008000 /* inline definition */
#define VT_INSTINL 0x00010000 /* the inline should be visibly instantiated */
#define VT_FAKESTC 0x00020000 /* is marked static because it's inline */
/* currently unused: 0x000[48]0000 */
/* currently unused: 0x000[1248]0000 */
#define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (32 - 2*6) */
#define VT_STRUCT_MASK (((1 << (6+6)) - 1) << VT_STRUCT_SHIFT | VT_BITFIELD)
@ -904,7 +902,7 @@ struct filespec {
#define IS_UNION(t) ((t & (VT_STRUCT_MASK|VT_BTYPE)) == VT_UNION)
/* type mask (except storage) */
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_INSTINL | VT_FAKESTC )
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
#define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK))
/* symbol was created by tccasm.c first */

View File

@ -325,7 +325,8 @@ ST_FUNC void update_storage(Sym *sym)
esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
| sym->a.visibility;
if (sym->type.t & VT_STATIC)
if ((sym->type.t & VT_STATIC)
|| (sym->type.t & (VT_EXTERN | VT_INLINE)) == VT_INLINE)
sym_bind = STB_LOCAL;
else if (sym->a.weak)
sym_bind = STB_WEAK;
@ -407,7 +408,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num,
} else {
sym_type = STT_OBJECT;
}
if (t & VT_STATIC)
if ((t & VT_STATIC) || (t & (VT_EXTERN | VT_INLINE)) == VT_INLINE)
sym_bind = STB_LOCAL;
else
sym_bind = STB_GLOBAL;
@ -926,7 +927,8 @@ static void merge_attr(AttributeDef *ad, AttributeDef *ad1)
/* Merge some type attributes. */
static void patch_type(Sym *sym, CType *type)
{
if (!(type->t & VT_EXTERN) || IS_ENUM_VAL(sym->type.t)) {
if ((!(type->t & VT_EXTERN) || IS_ENUM_VAL(sym->type.t))
&& (type->t & VT_BTYPE) != VT_FUNC) {
if (!(sym->type.t & VT_EXTERN))
tcc_error("redefinition of '%s'", get_tok_str(sym->v, NULL));
sym->type.t &= ~VT_EXTERN;
@ -945,18 +947,22 @@ static void patch_type(Sym *sym, CType *type)
} else if ((sym->type.t & VT_BTYPE) == VT_FUNC) {
int static_proto = sym->type.t & VT_STATIC;
/* warn if static follows non-static function declaration */
if ((type->t & VT_STATIC) && !static_proto && !((type->t|sym->type.t) & VT_INLINE))
if ((type->t & VT_STATIC) && !static_proto)
tcc_warning("static storage ignored for redefinition of '%s'",
get_tok_str(sym->v, NULL));
/* Force external definition if unequal inline specifier
or an explicit extern one. */
if ((type->t & VT_INLINE) != (sym->type.t & VT_INLINE)
|| (type->t | sym->type.t) & VT_EXTERN) {
type->t &= ~VT_INLINE;
sym->type.t &= ~VT_INLINE;
}
if (0 == (type->t & VT_EXTERN)) {
/* put complete type, use static from prototype */
sym->type.t = (type->t & ~VT_STATIC) | static_proto;
if (type->t & VT_INLINE)
sym->type.t = type->t;
sym->type.ref = type->ref;
}
} else {
if ((sym->type.t & VT_ARRAY) && type->ref->c >= 0) {
/* set array size if it was omitted in extern declaration */
@ -7411,17 +7417,12 @@ static void gen_inline_functions(TCCState *s)
for (i = 0; i < s->nb_inline_fns; ++i) {
fn = s->inline_fns[i];
sym = fn->sym;
if (sym && (sym->c || (sym->type.t&VT_INSTINL) )){
/* the function was used: generate its code and
if (sym && (sym->c || !(sym->type.t & VT_INLINE) )){
/* the function was used or forced: generate its code and
convert it to a normal function */
fn->sym = NULL;
if (file)
pstrcpy(file->filename, sizeof file->filename, fn->filename);
sym->type.t &= ~VT_INLINE;
if (sym->type.t&VT_INSTINL)
sym->type.t &= ~VT_STATIC;
begin_macro(fn->func_str, 1);
next();
cur_text_section = text_section;
@ -7574,46 +7575,17 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym)
if (sym->type.t == VT_VOID)
sym->type = int_type;
}
sym = type.ref;
sym = sym_find(v);
/* temporarily convert even extern inlines to statics */
if (!sym){
if( (type.t & VT_EXTERN) && (type.t & VT_INLINE) ){
type.t |= VT_INSTINL;
type.t = (type.t & ~VT_EXTERN) | VT_STATIC | VT_FAKESTC;
}else if ( type.t & VT_INLINE ){
if(!(type.t&VT_STATIC)) type.t |= VT_FAKESTC;
type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
}
}else{
if ( !(type.t & VT_STATIC) && !(sym->type.t & VT_STATIC) ){
if(
((type.t & VT_INLINE) != (sym->type.t & VT_INLINE)) ||
( (type.t & VT_INLINE) && (type.t & VT_EXTERN) )
){
/* noninline decl + inline def OR inline decl + noinline def OR
inline explicitly extern def ALL instantiate the inline */
type.t |= sym->type.t | VT_INSTINL;
type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
}
}else{
/*(extern on nonstatic defs following static decls are turned into true static defs)*/
}
if ( sym->type.t & VT_INLINE ){
if(!(type.t&VT_STATIC)) type.t |= VT_FAKESTC;
type.t |= sym->type.t;
type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
}
}
/* put function symbol */
sym = external_sym(v, &type, 0, &ad);
if (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF
&& !(elfsym(sym)->st_other & ST_ASM_SET))
tcc_error("redefinition of '%s'", get_tok_str(sym->v, NULL));
/* static inline functions are just recorded as a kind
of macro. Their code will be emitted at the end of
the compilation unit only if they are used */
if ((sym->type.t & (VT_INLINE | VT_STATIC)) ==
(VT_INLINE | VT_STATIC)) {
if ((sym->type.t & (VT_INLINE | VT_EXTERN)) == VT_INLINE) {
struct InlineFunc *fn;
const char *filename;
@ -7688,16 +7660,9 @@ found:
/* NOTE: as GCC, uninitialized global static
arrays of null size are considered as
extern */
int t = type.t;
type.t |= VT_EXTERN;
if ((type.t & VT_BTYPE) != VT_FUNC)
type.t |= VT_EXTERN;
sym = external_sym(v, &type, r, &ad);
if ( (!(sym->type.t&VT_STATIC) || sym->type.t&VT_FAKESTC ) &&
( ((sym->type.t&VT_INLINE)!=(t&VT_INLINE)) ||
( (t&VT_INLINE) && (t&VT_EXTERN) ) )
){
sym->type.t |= VT_INSTINL;
}
if (ad.alias_target) {
ElfSym *esym;
Sym *alias_target;
@ -7717,7 +7682,8 @@ found:
r |= l;
if (has_init)
next();
else if (l == VT_CONST)
else if (l == VT_CONST
&& (type.t & VT_BTYPE) != VT_FUNC)
/* uninitialized global variables may be overridden */
type.t |= VT_EXTERN;
decl_initializer_alloc(&type, &ad, r, has_init, v, l);

View File

@ -1455,6 +1455,9 @@ int defined_function(void)
static int tab_reinit[];
static int tab_reinit[10];
static int tentative_ar[];
static int tentative_ar[] = {1,2,3};
//int cinit1; /* a global variable can be defined several times without error ! */
int cinit1;
int cinit1;