- 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 | 886 | #define VT_STATIC 0x00002000 /* static variable */ |
| 887 | 887 | #define VT_TYPEDEF 0x00004000 /* typedef definition */ |
| 888 | 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 | 893 | #define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (32 - 2*6) */ |
| 892 | 894 | #define VT_STRUCT_MASK (((1 << (6+6)) - 1) << VT_STRUCT_SHIFT | VT_BITFIELD) |
| @@ -902,7 +904,7 @@ struct filespec { · 9 unmodified lines | ||
| 902 | 904 | #define IS_UNION(t) ((t & (VT_STRUCT_MASK|VT_BTYPE)) == VT_UNION) |
| 903 | 905 | |
| 904 | 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 | 908 | #define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK)) |
| 907 | 909 | |
| 908 | 910 | /* symbol was created by tccasm.c first */ |
| @@ -945,7 +945,7 @@ static void patch_type(Sym *sym, CType *type) · 944 unmodified lines | ||
| 945 | 945 | } else if ((sym->type.t & VT_BTYPE) == VT_FUNC) { |
| 946 | 946 | int static_proto = sym->type.t & VT_STATIC; |
| 947 | 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 | 949 | tcc_warning("static storage ignored for redefinition of '%s'", |
| 950 | 950 | get_tok_str(sym->v, NULL)); |
| 951 | 951 | |
| @@ -7411,7 +7411,7 @@ static void gen_inline_functions(TCCState *s) · 6459 unmodified lines | ||
| 7411 | 7411 | for (i = 0; i < s->nb_inline_fns; ++i) { |
| 7412 | 7412 | fn = s->inline_fns[i]; |
| 7413 | 7413 | sym = fn->sym; |
| 7414 | − if (sym && sym->c) { | |
| 7414 | + if (sym && (sym->c || (sym->type.t&VT_INSTINL) )){ | |
| 7415 | 7415 | /* the function was used: generate its code and |
| 7416 | 7416 | convert it to a normal function */ |
| 7417 | 7417 | fn->sym = NULL; |
| @@ -7419,6 +7419,9 @@ static void gen_inline_functions(TCCState *s) · 1 unmodified line | ||
| 7419 | 7419 | pstrcpy(file->filename, sizeof file->filename, fn->filename); |
| 7420 | 7420 | sym->type.t &= ~VT_INLINE; |
| 7421 | 7421 | |
| 7422 | + if (sym->type.t&VT_INSTINL) | |
| 7423 | + sym->type.t &= ~VT_STATIC; | |
| 7424 | + | |
| 7422 | 7425 | begin_macro(fn->func_str, 1); |
| 7423 | 7426 | next(); |
| 7424 | 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 | 7566 | expect("function definition"); |
| 7564 | 7567 | |
| 7565 | 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 | 7570 | sym = type.ref; |
| 7568 | 7571 | while ((sym = sym->next) != NULL) { |
| 7569 | 7572 | if (!(sym->v & ~SYM_FIELD)) |
| 7570 | 7573 | expect("identifier"); |
| 7571 | − if (sym->type.t == VT_VOID) | |
| 7572 | − sym->type = int_type; | |
| 7573 | − } | |
| 7574 | − | |
| 7575 | − /* XXX: cannot do better now: convert extern line to static inline */ | |
| 7576 | − if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE)) | |
| 7577 | − type.t = (type.t & ~VT_EXTERN) | VT_STATIC; | |
| 7574 | + if (sym->type.t == VT_VOID) | |
| 7575 | + sym->type = int_type; | |
| 7576 | + } | |
| 7577 | + sym = type.ref; | |
| 7578 | + sym = sym_find(v); | |
| 7579 | + /* temporarily convert even extern inlines to statics */ | |
| 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 | 7609 | /* put function symbol */ |
| 7580 | 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 | 7612 | /* static inline functions are just recorded as a kind |
| 7583 | 7613 | of macro. Their code will be emitted at the end of |
| 7584 | 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 | 7616 | (VT_INLINE | VT_STATIC)) { |
| 7587 | 7617 | struct InlineFunc *fn; |
| 7588 | 7618 | const char *filename; |
| @@ -7658,8 +7688,16 @@ found: · 69 unmodified lines | ||
| 7658 | 7688 | /* NOTE: as GCC, uninitialized global static |
| 7659 | 7689 | arrays of null size are considered as |
| 7660 | 7690 | extern */ |
| 7691 | + int t = type.t; | |
| 7661 | 7692 | type.t |= VT_EXTERN; |
| 7662 | 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 | 7701 | if (ad.alias_target) { |
| 7664 | 7702 | ElfSym *esym; |
| 7665 | 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 | |