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.)
mob
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_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 */

View File

@ -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;

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