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_STATIC 0x00002000 /* static variable */
#define VT_TYPEDEF 0x00004000 /* typedef definition */ #define VT_TYPEDEF 0x00004000 /* typedef definition */
#define VT_INLINE 0x00008000 /* inline definition */ #define VT_INLINE 0x00008000 /* inline definition */
#define VT_INSTINL 0x00010000 /* the inline should be visibly instantiated */ /* currently unused: 0x000[1248]0000 */
#define VT_FAKESTC 0x00020000 /* is marked static because it's inline */
/* currently unused: 0x000[48]0000 */
#define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (32 - 2*6) */ #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) #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) #define IS_UNION(t) ((t & (VT_STRUCT_MASK|VT_BTYPE)) == VT_UNION)
/* type mask (except storage) */ /* 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)) #define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK))
/* symbol was created by tccasm.c first */ /* 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)) esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
| sym->a.visibility; | 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; sym_bind = STB_LOCAL;
else if (sym->a.weak) else if (sym->a.weak)
sym_bind = STB_WEAK; sym_bind = STB_WEAK;
@ -407,7 +408,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num,
} else { } else {
sym_type = STT_OBJECT; sym_type = STT_OBJECT;
} }
if (t & VT_STATIC) if ((t & VT_STATIC) || (t & (VT_EXTERN | VT_INLINE)) == VT_INLINE)
sym_bind = STB_LOCAL; sym_bind = STB_LOCAL;
else else
sym_bind = STB_GLOBAL; sym_bind = STB_GLOBAL;
@ -926,7 +927,8 @@ static void merge_attr(AttributeDef *ad, AttributeDef *ad1)
/* Merge some type attributes. */ /* Merge some type attributes. */
static void patch_type(Sym *sym, CType *type) 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)) if (!(sym->type.t & VT_EXTERN))
tcc_error("redefinition of '%s'", get_tok_str(sym->v, NULL)); tcc_error("redefinition of '%s'", get_tok_str(sym->v, NULL));
sym->type.t &= ~VT_EXTERN; 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) { } else if ((sym->type.t & VT_BTYPE) == VT_FUNC) {
int static_proto = sym->type.t & VT_STATIC; int static_proto = sym->type.t & VT_STATIC;
/* warn if static follows non-static function declaration */ /* 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'", tcc_warning("static storage ignored for redefinition of '%s'",
get_tok_str(sym->v, NULL)); 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)) { if (0 == (type->t & VT_EXTERN)) {
/* put complete type, use static from prototype */ /* put complete type, use static from prototype */
sym->type.t = (type->t & ~VT_STATIC) | static_proto; sym->type.t = (type->t & ~VT_STATIC) | static_proto;
if (type->t & VT_INLINE)
sym->type.t = type->t;
sym->type.ref = type->ref; sym->type.ref = type->ref;
} }
} else { } else {
if ((sym->type.t & VT_ARRAY) && type->ref->c >= 0) { if ((sym->type.t & VT_ARRAY) && type->ref->c >= 0) {
/* set array size if it was omitted in extern declaration */ /* 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) { for (i = 0; i < s->nb_inline_fns; ++i) {
fn = s->inline_fns[i]; fn = s->inline_fns[i];
sym = fn->sym; sym = fn->sym;
if (sym && (sym->c || (sym->type.t&VT_INSTINL) )){ if (sym && (sym->c || !(sym->type.t & VT_INLINE) )){
/* the function was used: generate its code and /* the function was used or forced: generate its code and
convert it to a normal function */ convert it to a normal function */
fn->sym = NULL; fn->sym = NULL;
if (file) if (file)
pstrcpy(file->filename, sizeof file->filename, fn->filename); 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); begin_macro(fn->func_str, 1);
next(); next();
cur_text_section = text_section; 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) if (sym->type.t == VT_VOID)
sym->type = int_type; 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 */ /* put function symbol */
sym = external_sym(v, &type, 0, &ad); 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 /* static inline functions are just recorded as a kind
of macro. Their code will be emitted at the end of of macro. Their code will be emitted at the end of
the compilation unit only if they are used */ the compilation unit only if they are used */
if ((sym->type.t & (VT_INLINE | VT_STATIC)) == if ((sym->type.t & (VT_INLINE | VT_EXTERN)) == VT_INLINE) {
(VT_INLINE | VT_STATIC)) {
struct InlineFunc *fn; struct InlineFunc *fn;
const char *filename; const char *filename;
@ -7688,16 +7660,9 @@ found:
/* NOTE: as GCC, uninitialized global static /* NOTE: as GCC, uninitialized global static
arrays of null size are considered as arrays of null size are considered as
extern */ extern */
int t = type.t; if ((type.t & VT_BTYPE) != VT_FUNC)
type.t |= VT_EXTERN; type.t |= VT_EXTERN;
sym = external_sym(v, &type, r, &ad); 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) { if (ad.alias_target) {
ElfSym *esym; ElfSym *esym;
Sym *alias_target; Sym *alias_target;
@ -7717,7 +7682,8 @@ found:
r |= l; r |= l;
if (has_init) if (has_init)
next(); next();
else if (l == VT_CONST) else if (l == VT_CONST
&& (type.t & VT_BTYPE) != VT_FUNC)
/* uninitialized global variables may be overridden */ /* uninitialized global variables may be overridden */
type.t |= VT_EXTERN; type.t |= VT_EXTERN;
decl_initializer_alloc(&type, &ad, r, has_init, v, l); 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[];
static int tab_reinit[10]; 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; /* a global variable can be defined several times without error ! */
int cinit1; int cinit1;
int cinit1; int cinit1;