- 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.)
Unified · Split
| @@ -886,7 +886,9 @@ struct filespec { · 885 unmodified lines | |||
| 886 | #define VT_STATIC 0x00002000 /* static variable */ | 886 | #define VT_STATIC 0x00002000 /* static variable */ |
| 887 | #define VT_TYPEDEF 0x00004000 /* typedef definition */ | 887 | #define VT_TYPEDEF 0x00004000 /* typedef definition */ |
| 888 | #define VT_INLINE 0x00008000 /* inline definition */ | 888 | #define VT_INLINE 0x00008000 /* inline definition */ |
| 889 | /* currently unused: 0x000[1248]0000 */ | 889 | #define VT_INSTINL 0x00010000 /* the inline should be visibly instantiated */ |
| 890 | #define VT_FAKESTC 0x00020000 /* is marked static because it's inline */ | ||
| 891 | /* currently unused: 0x000[48]0000 */ | ||
| 890 | 892 | ||
| 891 | #define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (32 - 2*6) */ | 893 | #define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (32 - 2*6) */ |
| 892 | #define VT_STRUCT_MASK (((1 << (6+6)) - 1) << VT_STRUCT_SHIFT | VT_BITFIELD) | 894 | #define VT_STRUCT_MASK (((1 << (6+6)) - 1) << VT_STRUCT_SHIFT | VT_BITFIELD) |
| @@ -902,7 +904,7 @@ struct filespec { · 9 unmodified lines | |||
| 902 | #define IS_UNION(t) ((t & (VT_STRUCT_MASK|VT_BTYPE)) == VT_UNION) | 904 | #define IS_UNION(t) ((t & (VT_STRUCT_MASK|VT_BTYPE)) == VT_UNION) |
| 903 | 905 | ||
| 904 | /* type mask (except storage) */ | 906 | /* type mask (except storage) */ |
| 905 | #define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE) | 907 | #define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_INSTINL | VT_FAKESTC ) |
| 906 | #define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK)) | 908 | #define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK)) |
| 907 | 909 | ||
| 908 | /* symbol was created by tccasm.c first */ | 910 | /* symbol was created by tccasm.c first */ |
| @@ -945,7 +945,7 @@ static void patch_type(Sym *sym, CType *type) · 944 unmodified lines | |||
| 945 | } else if ((sym->type.t & VT_BTYPE) == VT_FUNC) { | 945 | } else if ((sym->type.t & VT_BTYPE) == VT_FUNC) { |
| 946 | int static_proto = sym->type.t & VT_STATIC; | 946 | int static_proto = sym->type.t & VT_STATIC; |
| 947 | /* warn if static follows non-static function declaration */ | 947 | /* warn if static follows non-static function declaration */ |
| 948 | if ((type->t & VT_STATIC) && !static_proto && !(type->t & VT_INLINE)) | 948 | if ((type->t & VT_STATIC) && !static_proto && !((type->t|sym->type.t) & VT_INLINE)) |
| 949 | tcc_warning("static storage ignored for redefinition of '%s'", | 949 | tcc_warning("static storage ignored for redefinition of '%s'", |
| 950 | get_tok_str(sym->v, NULL)); | 950 | get_tok_str(sym->v, NULL)); |
| 951 | 951 | ||
| @@ -7411,7 +7411,7 @@ static void gen_inline_functions(TCCState *s) · 6459 unmodified lines | |||
| 7411 | for (i = 0; i < s->nb_inline_fns; ++i) { | 7411 | for (i = 0; i < s->nb_inline_fns; ++i) { |
| 7412 | fn = s->inline_fns[i]; | 7412 | fn = s->inline_fns[i]; |
| 7413 | sym = fn->sym; | 7413 | sym = fn->sym; |
| 7414 | if (sym && sym->c) { | 7414 | if (sym && (sym->c || (sym->type.t&VT_INSTINL) )){ |
| 7415 | /* the function was used: generate its code and | 7415 | /* the function was used: generate its code and |
| 7416 | convert it to a normal function */ | 7416 | convert it to a normal function */ |
| 7417 | fn->sym = NULL; | 7417 | fn->sym = NULL; |
| @@ -7419,6 +7419,9 @@ static void gen_inline_functions(TCCState *s) · 1 unmodified line | |||
| 7419 | pstrcpy(file->filename, sizeof file->filename, fn->filename); | 7419 | pstrcpy(file->filename, sizeof file->filename, fn->filename); |
| 7420 | sym->type.t &= ~VT_INLINE; | 7420 | sym->type.t &= ~VT_INLINE; |
| 7421 | 7421 | ||
| 7422 | if (sym->type.t&VT_INSTINL) | ||
| 7423 | sym->type.t &= ~VT_STATIC; | ||
| 7424 | |||
| 7422 | begin_macro(fn->func_str, 1); | 7425 | begin_macro(fn->func_str, 1); |
| 7423 | next(); | 7426 | next(); |
| 7424 | cur_text_section = text_section; | 7427 | cur_text_section = text_section; |
| @@ -7563,18 +7566,45 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym) · 138 unmodified lines | |||
| 7563 | expect("function definition"); | 7566 | expect("function definition"); |
| 7564 | 7567 | ||
| 7565 | /* reject abstract declarators in function definition | 7568 | /* reject abstract declarators in function definition |
| 7566 | make old style params without decl have int type */ | 7569 | make old style params without decl have int type */ |
| 7567 | sym = type.ref; | 7570 | sym = type.ref; |
| 7568 | while ((sym = sym->next) != NULL) { | 7571 | while ((sym = sym->next) != NULL) { |
| 7569 | if (!(sym->v & ~SYM_FIELD)) | 7572 | if (!(sym->v & ~SYM_FIELD)) |
| 7570 | expect("identifier"); | 7573 | expect("identifier"); |
| 7571 | if (sym->type.t == VT_VOID) | 7574 | if (sym->type.t == VT_VOID) |
| 7572 | sym->type = int_type; | 7575 | sym->type = int_type; |
| 7573 | } | 7576 | } |
| 7574 | 7577 | sym = type.ref; | |
| 7575 | /* XXX: cannot do better now: convert extern line to static inline */ | 7578 | sym = sym_find(v); |
| 7576 | if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE)) | 7579 | /* temporarily convert even extern inlines to statics */ |
| 7577 | type.t = (type.t & ~VT_EXTERN) | VT_STATIC; | 7580 | if (!sym){ |
| 7581 | if( (type.t & VT_EXTERN) && (type.t & VT_INLINE) ){ | ||
| 7582 | type.t |= VT_INSTINL; | ||
| 7583 | type.t = (type.t & ~VT_EXTERN) | VT_STATIC | VT_FAKESTC; | ||
| 7584 | }else if ( type.t & VT_INLINE ){ | ||
| 7585 | if(!(type.t&VT_STATIC)) type.t |= VT_FAKESTC; | ||
| 7586 | type.t = (type.t & ~VT_EXTERN) | VT_STATIC; | ||
| 7587 | } | ||
| 7588 | }else{ | ||
| 7589 | if ( !(type.t & VT_STATIC) && !(sym->type.t & VT_STATIC) ){ | ||
| 7590 | if( | ||
| 7591 | ((type.t & VT_INLINE) != (sym->type.t & VT_INLINE)) || | ||
| 7592 | ( (type.t & VT_INLINE) && (type.t & VT_EXTERN) ) | ||
| 7593 | ){ | ||
| 7594 | /* noninline decl + inline def OR inline decl + noinline def OR | ||
| 7595 | inline explicitly extern def ALL instantiate the inline */ | ||
| 7596 | type.t |= sym->type.t | VT_INSTINL; | ||
| 7597 | type.t = (type.t & ~VT_EXTERN) | VT_STATIC; | ||
| 7598 | } | ||
| 7599 | }else{ | ||
| 7600 | /*(extern on nonstatic defs following static decls are turned into true static defs)*/ | ||
| 7601 | } | ||
| 7602 | if ( sym->type.t & VT_INLINE ){ | ||
| 7603 | if(!(type.t&VT_STATIC)) type.t |= VT_FAKESTC; | ||
| 7604 | type.t |= sym->type.t; | ||
| 7605 | type.t = (type.t & ~VT_EXTERN) | VT_STATIC; | ||
| 7606 | } | ||
| 7607 | } | ||
| 7578 | 7608 | ||
| 7579 | /* put function symbol */ | 7609 | /* put function symbol */ |
| 7580 | sym = external_sym(v, &type, 0, &ad); | 7610 | sym = external_sym(v, &type, 0, &ad); |
| @@ -7582,7 +7612,7 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym) · 1 unmodified line | |||
| 7582 | /* static inline functions are just recorded as a kind | 7612 | /* static inline functions are just recorded as a kind |
| 7583 | of macro. Their code will be emitted at the end of | 7613 | of macro. Their code will be emitted at the end of |
| 7584 | the compilation unit only if they are used */ | 7614 | the compilation unit only if they are used */ |
| 7585 | if ((type.t & (VT_INLINE | VT_STATIC)) == | 7615 | if ((sym->type.t & (VT_INLINE | VT_STATIC)) == |
| 7586 | (VT_INLINE | VT_STATIC)) { | 7616 | (VT_INLINE | VT_STATIC)) { |
| 7587 | struct InlineFunc *fn; | 7617 | struct InlineFunc *fn; |
| 7588 | const char *filename; | 7618 | const char *filename; |
| @@ -7658,8 +7688,16 @@ found: · 69 unmodified lines | |||
| 7658 | /* NOTE: as GCC, uninitialized global static | 7688 | /* NOTE: as GCC, uninitialized global static |
| 7659 | arrays of null size are considered as | 7689 | arrays of null size are considered as |
| 7660 | extern */ | 7690 | extern */ |
| 7691 | int t = type.t; | ||
| 7661 | type.t |= VT_EXTERN; | 7692 | type.t |= VT_EXTERN; |
| 7662 | sym = external_sym(v, &type, r, &ad); | 7693 | sym = external_sym(v, &type, r, &ad); |
| 7694 | |||
| 7695 | if ( (!(sym->type.t&VT_STATIC) || sym->type.t&VT_FAKESTC ) && | ||
| 7696 | ( ((sym->type.t&VT_INLINE)!=(t&VT_INLINE)) || | ||
| 7697 | ( (t&VT_INLINE) && (t&VT_EXTERN) ) ) | ||
| 7698 | ){ | ||
| 7699 | sym->type.t |= VT_INSTINL; | ||
| 7700 | } | ||
| 7663 | if (ad.alias_target) { | 7701 | if (ad.alias_target) { |
| 7664 | ElfSym *esym; | 7702 | ElfSym *esym; |
| 7665 | Sym *alias_target; | 7703 | Sym *alias_target; |
| @@ -0,0 +1,181 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <stdlib.h> | ||
| 3 | #include <signal.h> | ||
| 4 | #include <errno.h> | ||
| 5 | |||
| 6 | extern char const cfileContents[]; | ||
| 7 | char c[]="/tmp/tcc-XXXXXX.c"; | ||
| 8 | char o[]="/tmp/tcc-XXXXXX.o"; | ||
| 9 | void rmh(int Sig) | ||
| 10 | { | ||
| 11 | remove(c); | ||
| 12 | remove(o); | ||
| 13 | signal(Sig,SIG_DFL); | ||
| 14 | raise(Sig); | ||
| 15 | } | ||
| 16 | int str2file(char const *fnm, char const *str) | ||
| 17 | { | ||
| 18 | FILE *f; | ||
| 19 | if(0==(f=fopen(fnm,"w"))) return -1; | ||
| 20 | if(0>fputs(str,f)) return -1; | ||
| 21 | if(0>fclose(f)) return -1; | ||
| 22 | return 0; | ||
| 23 | } | ||
| 24 | int main(int C, char **V) | ||
| 25 | { | ||
| 26 | int r=0; | ||
| 27 | if (system("which nm >/dev/null 2>&1")){ return 0; } | ||
| 28 | signal(SIGINT,SIG_IGN); | ||
| 29 | signal(SIGTERM,SIG_IGN); | ||
| 30 | if(0>mkstemps(c,2)) return perror("mkstemps"),1; | ||
| 31 | if(0>mkstemps(o,2)){ | ||
| 32 | if(0>remove(c)) perror("remove"); | ||
| 33 | return perror("mkstemps"),1; | ||
| 34 | } | ||
| 35 | signal(SIGINT,rmh); | ||
| 36 | signal(SIGTERM,rmh); | ||
| 37 | if(0>str2file(c, cfileContents)) { perror("write");r=1;goto out;} | ||
| 38 | char buf[1024]; | ||
| 39 | sprintf(buf, "%s -c %s -o %s", V[1]?V[1]:"../../tcc", c, o); if(0!=system(buf)){ r=1;goto out;} | ||
| 40 | sprintf(buf, "nm -Ptx %s > %s", o, c); if(system(buf)) {r=1;goto out;} | ||
| 41 | sprintf(buf, "awk '{ if($2 == \"T\") print $1 }' %s > %s", c, o); if(system(buf)) {r=1;goto out;} | ||
| 42 | sprintf(buf, "sort %s", o); if(system(buf)) {r=1;goto out;} | ||
| 43 | out: | ||
| 44 | remove(c); | ||
| 45 | remove(o); | ||
| 46 | return r; | ||
| 47 | } | ||
| 48 | char const cfileContents[]= | ||
| 49 | "inline void inline_inline_2decl_only(void);\n" | ||
| 50 | "inline void inline_inline_2decl_only(void);\n" | ||
| 51 | "\n" | ||
| 52 | "inline void inline_inline_undeclared(void){}\n" | ||
| 53 | "\n" | ||
| 54 | "inline void inline_inline_predeclared(void);\n" | ||
| 55 | "inline void inline_inline_predeclared(void){}\n" | ||
| 56 | "\n" | ||
| 57 | "inline void inline_inline_postdeclared(void){}\n" | ||
| 58 | "inline void inline_inline_postdeclared(void);\n" | ||
| 59 | "\n" | ||
| 60 | "inline void inline_inline_prepostdeclared(void);\n" | ||
| 61 | "inline void inline_inline_prepostdeclared(void){}\n" | ||
| 62 | "inline void inline_inline_prepostdeclared(void);\n" | ||
| 63 | "\n" | ||
| 64 | "inline void inline_inline_undeclared2(void){}\n" | ||
| 65 | "\n" | ||
| 66 | "inline void inline_inline_predeclared2(void);\n" | ||
| 67 | "inline void inline_inline_predeclared2(void);\n" | ||
| 68 | "inline void inline_inline_predeclared2(void){}\n" | ||
| 69 | "\n" | ||
| 70 | "inline void inline_inline_postdeclared2(void){}\n" | ||
| 71 | "inline void inline_inline_postdeclared2(void);\n" | ||
| 72 | "inline void inline_inline_postdeclared2(void);\n" | ||
| 73 | "\n" | ||
| 74 | "inline void inline_inline_prepostdeclared2(void);\n" | ||
| 75 | "inline void inline_inline_prepostdeclared2(void);\n" | ||
| 76 | "inline void inline_inline_prepostdeclared2(void){}\n" | ||
| 77 | "inline void inline_inline_prepostdeclared2(void);\n" | ||
| 78 | "inline void inline_inline_prepostdeclared2(void);\n" | ||
| 79 | "\n" | ||
| 80 | "extern void extern_extern_undeclared(void){}\n" | ||
| 81 | "\n" | ||
| 82 | "extern void extern_extern_predeclared(void);\n" | ||
| 83 | "extern void extern_extern_predeclared(void){}\n" | ||
| 84 | "\n" | ||
| 85 | "extern void extern_extern_postdeclared(void){}\n" | ||
| 86 | "extern void extern_extern_postdeclared(void);\n" | ||
| 87 | "\n" | ||
| 88 | "extern void extern_extern_prepostdeclared(void);\n" | ||
| 89 | "extern void extern_extern_prepostdeclared(void){}\n" | ||
| 90 | "extern void extern_extern_prepostdeclared(void);\n" | ||
| 91 | "\n" | ||
| 92 | "extern void extern_extern_undeclared2(void){}\n" | ||
| 93 | "\n" | ||
| 94 | "extern void extern_extern_predeclared2(void);\n" | ||
| 95 | "extern void extern_extern_predeclared2(void);\n" | ||
| 96 | "extern void extern_extern_predeclared2(void){}\n" | ||
| 97 | "\n" | ||
| 98 | "extern void extern_extern_postdeclared2(void){}\n" | ||
| 99 | "extern void extern_extern_postdeclared2(void);\n" | ||
| 100 | "extern void extern_extern_postdeclared2(void);\n" | ||
| 101 | "\n" | ||
| 102 | "extern void extern_extern_prepostdeclared2(void);\n" | ||
| 103 | "extern void extern_extern_prepostdeclared2(void);\n" | ||
| 104 | "extern void extern_extern_prepostdeclared2(void){}\n" | ||
| 105 | "extern void extern_extern_prepostdeclared2(void);\n" | ||
| 106 | "extern void extern_extern_prepostdeclared2(void);\n" | ||
| 107 | "\n" | ||
| 108 | "void extern_undeclared(void){}\n" | ||
| 109 | "\n" | ||
| 110 | "void extern_predeclared(void);\n" | ||
| 111 | "void extern_predeclared(void){}\n" | ||
| 112 | "\n" | ||
| 113 | "void extern_postdeclared(void){}\n" | ||
| 114 | "void extern_postdeclared(void);\n" | ||
| 115 | "\n" | ||
| 116 | "void extern_prepostdeclared(void);\n" | ||
| 117 | "void extern_prepostdeclared(void){}\n" | ||
| 118 | "void extern_prepostdeclared(void);\n" | ||
| 119 | "\n" | ||
| 120 | "void extern_undeclared2(void){}\n" | ||
| 121 | "\n" | ||
| 122 | "void extern_predeclared2(void);\n" | ||
| 123 | "void extern_predeclared2(void);\n" | ||
| 124 | "void extern_predeclared2(void){}\n" | ||
| 125 | "\n" | ||
| 126 | "void extern_postdeclared2(void){}\n" | ||
| 127 | "void extern_postdeclared2(void);\n" | ||
| 128 | "void extern_postdeclared2(void);\n" | ||
| 129 | "\n" | ||
| 130 | "\n" | ||
| 131 | "extern inline void noinst_extern_inline_undeclared(void){}\n" | ||
| 132 | "\n" | ||
| 133 | "extern inline void noinst_extern_inline_postdeclared(void){}\n" | ||
| 134 | "inline void noinst_extern_inline_postdeclared(void);\n" | ||
| 135 | "\n" | ||
| 136 | "extern inline void noinst_extern_inline_postdeclared2(void){}\n" | ||
| 137 | "inline void noinst_extern_inline_postdeclared2(void);\n" | ||
| 138 | "inline void noinst_extern_inline_postdeclared2(void);\n" | ||
| 139 | "\n" | ||
| 140 | "extern inline void inst_extern_inline_postdeclared(void){}\n" | ||
| 141 | "extern inline void inst_extern_inline_postdeclared(void);\n" | ||
| 142 | "inline void inst2_extern_inline_postdeclared(void){}\n" | ||
| 143 | "void inst2_extern_inline_postdeclared(void);\n" | ||
| 144 | "\n" | ||
| 145 | "void inst_extern_inline_predeclared(void);\n" | ||
| 146 | "extern inline void inst_extern_inline_predeclared(void){}\n" | ||
| 147 | "void inst2_extern_inline_predeclared(void);\n" | ||
| 148 | "inline void inst2_extern_inline_predeclared(void){}\n" | ||
| 149 | "extern inline void inst3_extern_inline_predeclared(void);\n" | ||
| 150 | "inline void inst3_extern_inline_predeclared(void){}\n" | ||
| 151 | "\n" | ||
| 152 | "static inline void noinst_static_inline_postdeclared(void){}\n" | ||
| 153 | "static inline void noinst_static_inline_postdeclared(void);\n" | ||
| 154 | "static inline void noinst2_static_inline_postdeclared(void){}\n" | ||
| 155 | "static void noinst2_static_inline_postdeclared(void);\n" | ||
| 156 | "\n" | ||
| 157 | "static void noinst_static_inline_predeclared(void);\n" | ||
| 158 | "static inline void noinst_static_inline_predeclared(void){}\n" | ||
| 159 | "static void noinst2_static_inline_predeclared(void);\n" | ||
| 160 | "static inline void noinst2_static_inline_predeclared(void){}\n" | ||
| 161 | "\n" | ||
| 162 | "static void static_func(void);\n" | ||
| 163 | "void static_func(void) { }\n" | ||
| 164 | "\n" | ||
| 165 | "inline void noinst_extern_inline_func(void);\n" | ||
| 166 | "void noinst_extern_inline_func(void) { }\n" | ||
| 167 | "int main()\n" | ||
| 168 | "{\n" | ||
| 169 | " inline_inline_undeclared(); inline_inline_predeclared(); inline_inline_postdeclared();\n" | ||
| 170 | " inline_inline_undeclared2(); inline_inline_predeclared2(); inline_inline_postdeclared2();\n" | ||
| 171 | " noinst_extern_inline_undeclared();\n" | ||
| 172 | " noinst_extern_inline_postdeclared();\n" | ||
| 173 | " noinst_extern_inline_postdeclared2();\n" | ||
| 174 | " noinst_static_inline_predeclared();\n" | ||
| 175 | " noinst2_static_inline_predeclared();\n" | ||
| 176 | " noinst_static_inline_predeclared();\n" | ||
| 177 | " noinst2_static_inline_predeclared();\n" | ||
| 178 | "}\n" | ||
| 179 | "\n" | ||
| 180 | ; | ||
| 181 | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | extern_extern_postdeclared | ||
| 2 | extern_extern_postdeclared2 | ||
| 3 | extern_extern_predeclared | ||
| 4 | extern_extern_predeclared2 | ||
| 5 | extern_extern_prepostdeclared | ||
| 6 | extern_extern_prepostdeclared2 | ||
| 7 | extern_extern_undeclared | ||
| 8 | extern_extern_undeclared2 | ||
| 9 | extern_postdeclared | ||
| 10 | extern_postdeclared2 | ||
| 11 | extern_predeclared | ||
| 12 | extern_predeclared2 | ||
| 13 | extern_prepostdeclared | ||
| 14 | extern_undeclared | ||
| 15 | extern_undeclared2 | ||
| 16 | inst2_extern_inline_postdeclared | ||
| 17 | inst2_extern_inline_predeclared | ||
| 18 | inst3_extern_inline_predeclared | ||
| 19 | inst_extern_inline_postdeclared | ||
| 20 | inst_extern_inline_predeclared | ||
| 21 | main | ||
| 22 | noinst_extern_inline_func | ||
| 23 | noinst_extern_inline_postdeclared | ||
| 24 | noinst_extern_inline_postdeclared2 | ||
| 25 | noinst_extern_inline_undeclared | ||