standard conformant inline functions

- add tests for standard conformant inline functions
- implement it

The  old tinycc failed to provide a conforming implementation
of non-static inlines.  It would expose external symbols where it
shouldn't and hide them where it should expose them.

This commit provides a hopefully comprehensive test suite
for how things should be done. The .expect file can be obtained
by compiling the example c file (embedded in the test)
with a conforming compiler such as gcc, clang or icc and then
printing the exported symbols (e.g., with nm+awk+sort).

(The implementation currently reserves two new VT_ flags.
If anyone can provide an implementation without reserving
two extra flags, please replace mine.)
This commit is contained in:
Petr Skocik 2019-06-11 15:28:42 +02:00
parent f2fd56a27d
commit 587e1f5598
4 changed files with 259 additions and 13 deletions

6
tcc.h
View file

@ -886,7 +886,9 @@ 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 */
/* currently unused: 0x000[1248]0000 */ #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 */
#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)
@ -902,7 +904,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) #define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_INSTINL | VT_FAKESTC )
#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

@ -945,7 +945,7 @@ 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 & VT_INLINE)) if ((type->t & VT_STATIC) && !static_proto && !((type->t|sym->type.t) & VT_INLINE))
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));
@ -7411,7 +7411,7 @@ 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) { if (sym && (sym->c || (sym->type.t&VT_INSTINL) )){
/* the function was used: generate its code and /* the function was used: generate its code and
convert it to a normal function */ convert it to a normal function */
fn->sym = NULL; fn->sym = NULL;
@ -7419,6 +7419,9 @@ static void gen_inline_functions(TCCState *s)
pstrcpy(file->filename, sizeof file->filename, fn->filename); pstrcpy(file->filename, sizeof file->filename, fn->filename);
sym->type.t &= ~VT_INLINE; 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;
@ -7571,10 +7574,37 @@ 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;
/* XXX: cannot do better now: convert extern line to static inline */ sym = sym_find(v);
if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE)) /* 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; 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);
@ -7582,7 +7612,7 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym)
/* 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 ((type.t & (VT_INLINE | VT_STATIC)) == if ((sym->type.t & (VT_INLINE | VT_STATIC)) ==
(VT_INLINE | VT_STATIC)) { (VT_INLINE | VT_STATIC)) {
struct InlineFunc *fn; struct InlineFunc *fn;
const char *filename; const char *filename;
@ -7658,8 +7688,16 @@ 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;
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;

View file

@ -0,0 +1,181 @@
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
extern char const cfileContents[];
char c[]="/tmp/tcc-XXXXXX.c";
char o[]="/tmp/tcc-XXXXXX.o";
void rmh(int Sig)
{
remove(c);
remove(o);
signal(Sig,SIG_DFL);
raise(Sig);
}
int str2file(char const *fnm, char const *str)
{
FILE *f;
if(0==(f=fopen(fnm,"w"))) return -1;
if(0>fputs(str,f)) return -1;
if(0>fclose(f)) return -1;
return 0;
}
int main(int C, char **V)
{
int r=0;
if (system("which nm >/dev/null 2>&1")){ return 0; }
signal(SIGINT,SIG_IGN);
signal(SIGTERM,SIG_IGN);
if(0>mkstemps(c,2)) return perror("mkstemps"),1;
if(0>mkstemps(o,2)){
if(0>remove(c)) perror("remove");
return perror("mkstemps"),1;
}
signal(SIGINT,rmh);
signal(SIGTERM,rmh);
if(0>str2file(c, cfileContents)) { perror("write");r=1;goto out;}
char buf[1024];
sprintf(buf, "%s -c %s -o %s", V[1]?V[1]:"../../tcc", c, o); if(0!=system(buf)){ r=1;goto out;}
sprintf(buf, "nm -Ptx %s > %s", o, c); if(system(buf)) {r=1;goto out;}
sprintf(buf, "awk '{ if($2 == \"T\") print $1 }' %s > %s", c, o); if(system(buf)) {r=1;goto out;}
sprintf(buf, "sort %s", o); if(system(buf)) {r=1;goto out;}
out:
remove(c);
remove(o);
return r;
}
char const cfileContents[]=
"inline void inline_inline_2decl_only(void);\n"
"inline void inline_inline_2decl_only(void);\n"
"\n"
"inline void inline_inline_undeclared(void){}\n"
"\n"
"inline void inline_inline_predeclared(void);\n"
"inline void inline_inline_predeclared(void){}\n"
"\n"
"inline void inline_inline_postdeclared(void){}\n"
"inline void inline_inline_postdeclared(void);\n"
"\n"
"inline void inline_inline_prepostdeclared(void);\n"
"inline void inline_inline_prepostdeclared(void){}\n"
"inline void inline_inline_prepostdeclared(void);\n"
"\n"
"inline void inline_inline_undeclared2(void){}\n"
"\n"
"inline void inline_inline_predeclared2(void);\n"
"inline void inline_inline_predeclared2(void);\n"
"inline void inline_inline_predeclared2(void){}\n"
"\n"
"inline void inline_inline_postdeclared2(void){}\n"
"inline void inline_inline_postdeclared2(void);\n"
"inline void inline_inline_postdeclared2(void);\n"
"\n"
"inline void inline_inline_prepostdeclared2(void);\n"
"inline void inline_inline_prepostdeclared2(void);\n"
"inline void inline_inline_prepostdeclared2(void){}\n"
"inline void inline_inline_prepostdeclared2(void);\n"
"inline void inline_inline_prepostdeclared2(void);\n"
"\n"
"extern void extern_extern_undeclared(void){}\n"
"\n"
"extern void extern_extern_predeclared(void);\n"
"extern void extern_extern_predeclared(void){}\n"
"\n"
"extern void extern_extern_postdeclared(void){}\n"
"extern void extern_extern_postdeclared(void);\n"
"\n"
"extern void extern_extern_prepostdeclared(void);\n"
"extern void extern_extern_prepostdeclared(void){}\n"
"extern void extern_extern_prepostdeclared(void);\n"
"\n"
"extern void extern_extern_undeclared2(void){}\n"
"\n"
"extern void extern_extern_predeclared2(void);\n"
"extern void extern_extern_predeclared2(void);\n"
"extern void extern_extern_predeclared2(void){}\n"
"\n"
"extern void extern_extern_postdeclared2(void){}\n"
"extern void extern_extern_postdeclared2(void);\n"
"extern void extern_extern_postdeclared2(void);\n"
"\n"
"extern void extern_extern_prepostdeclared2(void);\n"
"extern void extern_extern_prepostdeclared2(void);\n"
"extern void extern_extern_prepostdeclared2(void){}\n"
"extern void extern_extern_prepostdeclared2(void);\n"
"extern void extern_extern_prepostdeclared2(void);\n"
"\n"
"void extern_undeclared(void){}\n"
"\n"
"void extern_predeclared(void);\n"
"void extern_predeclared(void){}\n"
"\n"
"void extern_postdeclared(void){}\n"
"void extern_postdeclared(void);\n"
"\n"
"void extern_prepostdeclared(void);\n"
"void extern_prepostdeclared(void){}\n"
"void extern_prepostdeclared(void);\n"
"\n"
"void extern_undeclared2(void){}\n"
"\n"
"void extern_predeclared2(void);\n"
"void extern_predeclared2(void);\n"
"void extern_predeclared2(void){}\n"
"\n"
"void extern_postdeclared2(void){}\n"
"void extern_postdeclared2(void);\n"
"void extern_postdeclared2(void);\n"
"\n"
"\n"
"extern inline void noinst_extern_inline_undeclared(void){}\n"
"\n"
"extern inline void noinst_extern_inline_postdeclared(void){}\n"
"inline void noinst_extern_inline_postdeclared(void);\n"
"\n"
"extern inline void noinst_extern_inline_postdeclared2(void){}\n"
"inline void noinst_extern_inline_postdeclared2(void);\n"
"inline void noinst_extern_inline_postdeclared2(void);\n"
"\n"
"extern inline void inst_extern_inline_postdeclared(void){}\n"
"extern inline void inst_extern_inline_postdeclared(void);\n"
"inline void inst2_extern_inline_postdeclared(void){}\n"
"void inst2_extern_inline_postdeclared(void);\n"
"\n"
"void inst_extern_inline_predeclared(void);\n"
"extern inline void inst_extern_inline_predeclared(void){}\n"
"void inst2_extern_inline_predeclared(void);\n"
"inline void inst2_extern_inline_predeclared(void){}\n"
"extern inline void inst3_extern_inline_predeclared(void);\n"
"inline void inst3_extern_inline_predeclared(void){}\n"
"\n"
"static inline void noinst_static_inline_postdeclared(void){}\n"
"static inline void noinst_static_inline_postdeclared(void);\n"
"static inline void noinst2_static_inline_postdeclared(void){}\n"
"static void noinst2_static_inline_postdeclared(void);\n"
"\n"
"static void noinst_static_inline_predeclared(void);\n"
"static inline void noinst_static_inline_predeclared(void){}\n"
"static void noinst2_static_inline_predeclared(void);\n"
"static inline void noinst2_static_inline_predeclared(void){}\n"
"\n"
"static void static_func(void);\n"
"void static_func(void) { }\n"
"\n"
"inline void noinst_extern_inline_func(void);\n"
"void noinst_extern_inline_func(void) { }\n"
"int main()\n"
"{\n"
" inline_inline_undeclared(); inline_inline_predeclared(); inline_inline_postdeclared();\n"
" inline_inline_undeclared2(); inline_inline_predeclared2(); inline_inline_postdeclared2();\n"
" noinst_extern_inline_undeclared();\n"
" noinst_extern_inline_postdeclared();\n"
" noinst_extern_inline_postdeclared2();\n"
" noinst_static_inline_predeclared();\n"
" noinst2_static_inline_predeclared();\n"
" noinst_static_inline_predeclared();\n"
" noinst2_static_inline_predeclared();\n"
"}\n"
"\n"
;

View file

@ -0,0 +1,25 @@
extern_extern_postdeclared
extern_extern_postdeclared2
extern_extern_predeclared
extern_extern_predeclared2
extern_extern_prepostdeclared
extern_extern_prepostdeclared2
extern_extern_undeclared
extern_extern_undeclared2
extern_postdeclared
extern_postdeclared2
extern_predeclared
extern_predeclared2
extern_prepostdeclared
extern_undeclared
extern_undeclared2
inst2_extern_inline_postdeclared
inst2_extern_inline_predeclared
inst3_extern_inline_predeclared
inst_extern_inline_postdeclared
inst_extern_inline_predeclared
main
noinst_extern_inline_func
noinst_extern_inline_postdeclared
noinst_extern_inline_postdeclared2
noinst_extern_inline_undeclared