mirror of
https://github.com/mirror/tinycc.git
synced 2025-01-27 06:10:06 +08:00
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:
parent
f2fd56a27d
commit
587e1f5598
6
tcc.h
6
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 */
|
||||
|
60
tccgen.c
60
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;
|
||||
|
181
tests/tests2/104_inline_test.c
Normal file
181
tests/tests2/104_inline_test.c
Normal 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"
|
||||
;
|
||||
|
25
tests/tests2/104_inline_test.expect
Normal file
25
tests/tests2/104_inline_test.expect
Normal 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
|
Loading…
Reference in New Issue
Block a user