From 587e1f5598f2b0c35c7772bde486d47aa9a0cbbc Mon Sep 17 00:00:00 2001 From: Petr Skocik Date: Tue, 11 Jun 2019 15:28:42 +0200 Subject: [PATCH] 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.) --- tcc.h | 6 +- tccgen.c | 60 +++++++-- tests/tests2/104_inline_test.c | 181 ++++++++++++++++++++++++++++ tests/tests2/104_inline_test.expect | 25 ++++ 4 files changed, 259 insertions(+), 13 deletions(-) create mode 100644 tests/tests2/104_inline_test.c create mode 100644 tests/tests2/104_inline_test.expect diff --git a/tcc.h b/tcc.h index b11101e..4e81edc 100644 --- a/tcc.h +++ b/tcc.h @@ -886,7 +886,9 @@ struct filespec { #define VT_STATIC 0x00002000 /* static variable */ #define VT_TYPEDEF 0x00004000 /* typedef 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_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) /* 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)) /* symbol was created by tccasm.c first */ diff --git a/tccgen.c b/tccgen.c index 0445144..674a63f 100644 --- a/tccgen.c +++ b/tccgen.c @@ -945,7 +945,7 @@ 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 & 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'", 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) { fn = s->inline_fns[i]; sym = fn->sym; - if (sym && sym->c) { + if (sym && (sym->c || (sym->type.t&VT_INSTINL) )){ /* the function was used: generate its code and convert it to a normal function */ fn->sym = NULL; @@ -7419,6 +7419,9 @@ static void gen_inline_functions(TCCState *s) 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; @@ -7563,18 +7566,45 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym) expect("function definition"); /* reject abstract declarators in function definition - make old style params without decl have int type */ + make old style params without decl have int type */ sym = type.ref; while ((sym = sym->next) != NULL) { if (!(sym->v & ~SYM_FIELD)) expect("identifier"); - if (sym->type.t == VT_VOID) - sym->type = int_type; - } - - /* XXX: cannot do better now: convert extern line to static inline */ - if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE)) - type.t = (type.t & ~VT_EXTERN) | VT_STATIC; + 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); @@ -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 of macro. Their code will be emitted at the end of 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)) { struct InlineFunc *fn; const char *filename; @@ -7658,8 +7688,16 @@ found: /* NOTE: as GCC, uninitialized global static arrays of null size are considered as extern */ + int t = type.t; 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; diff --git a/tests/tests2/104_inline_test.c b/tests/tests2/104_inline_test.c new file mode 100644 index 0000000..3276882 --- /dev/null +++ b/tests/tests2/104_inline_test.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include + +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" +; + diff --git a/tests/tests2/104_inline_test.expect b/tests/tests2/104_inline_test.expect new file mode 100644 index 0000000..9da3c69 --- /dev/null +++ b/tests/tests2/104_inline_test.expect @@ -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