From 30df3189b177c65d65094bed3a066a2722fe2dc0 Mon Sep 17 00:00:00 2001 From: grischka Date: Sat, 9 May 2015 14:29:39 +0200 Subject: [PATCH] tccpp: fix issues, add tests * fix some macro expansion issues * add some pp tests in tests/pp * improved tcc -E output for better diff'ability * remove -dD feature (quirky code, exotic feature, didn't work well) Based partially on ideas / researches from PipCet Some issues remain with VA_ARGS macros (if used in a rather tricky way). Also, to keep it simple, the pp doesn't automtically add any extra spaces to separate tokens which otherwise would form wrong tokens if re-read from tcc -E output (such as '+' '=') GCC does that, other compilers don't. * cleanups - #line 01 "file" / # 01 "file" processing - #pragma comment(lib,"foo") - tcc -E: forward some pragmas to output (pack, comment(lib)) - fix macro parameter list parsing mess from a3fc54345949535524d01319e1ca6378b7c2c201 a715d7143d9d17da17e67fec6af1c01409a71a31 (some coffee might help, next time ;) - introduce TOK_PPSTR - to have character constants as written in the file (similar to TOK_PPNUM) - allow '\' appear in macros - new functions begin/end_macro to: - fix switching macro levels during expansion - allow unget_tok to unget more than one tok - slight speedup by using bitflags in isidnum_table Also: - x86_64.c : fix decl after statements - i386-gen,c : fix a vstack leak with VLA on windows - configure/Makefile : build on windows (MSYS) was broken - tcc_warning: fflush stderr to keep output order (win32) --- configure | 6 - i386-gen.c | 1 - libtcc.c | 83 +- tcc.c | 27 +- tcc.h | 45 +- tccasm.c | 2 +- tccelf.c | 2 + tccgen.c | 56 +- tccpe.c | 9 +- tccpp.c | 1391 ++++++++++++------------- tests/Makefile | 25 +- tests/abitest.c | 39 +- tests/libtcc_test.c | 16 +- tests/pp/01.c | 6 + tests/pp/01.expect | 1 + tests/pp/02.c | 28 + tests/pp/02.expect | 5 + tests/pp/03.c | 15 + tests/pp/03.expect | 5 + tests/pp/04.c | 4 + tests/pp/04.expect | 1 + tests/pp/05.c | 7 + tests/pp/05.expect | 3 + tests/pp/06.c | 5 + tests/pp/06.expect | 1 + tests/pp/07.c | 4 + tests/pp/07.expect | 2 + tests/pp/08.c | 4 + tests/pp/08.expect | 1 + tests/pp/09.c | 4 + tests/pp/09.expect | 1 + tests/pp/10.c | 10 + tests/pp/10.expect | 5 + tests/pp/11.c | 31 + tests/pp/11.expect | 15 + tests/pp/Makefile | 35 + tests/tcctest.c | 2 + tests/tests2/65_macro_concat_start.c | 2 - tests/tests2/65_macro_concat_start.expect | 1 - tests/tests2/66_macro_concat_end.c | 2 - tests/tests2/66_macro_concat_end.expect | 1 - tests/tests2/68_macro_param_list_err_1.c | 9 - tests/tests2/68_macro_param_list_err_1.expect | 1 - tests/tests2/69_macro_param_list_err_2.c | 9 - tests/tests2/69_macro_param_list_err_2.expect | 1 - tests/tests2/72_long_long_constant.c | 2 + tests/tests2/72_long_long_constant.expect | 1 + tests/tests2/Makefile | 219 ++-- x86_64-gen.c | 9 +- 49 files changed, 1113 insertions(+), 1041 deletions(-) create mode 100644 tests/pp/01.c create mode 100644 tests/pp/01.expect create mode 100644 tests/pp/02.c create mode 100644 tests/pp/02.expect create mode 100644 tests/pp/03.c create mode 100644 tests/pp/03.expect create mode 100644 tests/pp/04.c create mode 100644 tests/pp/04.expect create mode 100644 tests/pp/05.c create mode 100644 tests/pp/05.expect create mode 100644 tests/pp/06.c create mode 100644 tests/pp/06.expect create mode 100644 tests/pp/07.c create mode 100644 tests/pp/07.expect create mode 100644 tests/pp/08.c create mode 100644 tests/pp/08.expect create mode 100644 tests/pp/09.c create mode 100644 tests/pp/09.expect create mode 100644 tests/pp/10.c create mode 100644 tests/pp/10.expect create mode 100644 tests/pp/11.c create mode 100644 tests/pp/11.expect create mode 100644 tests/pp/Makefile delete mode 100644 tests/tests2/65_macro_concat_start.c delete mode 100644 tests/tests2/65_macro_concat_start.expect delete mode 100644 tests/tests2/66_macro_concat_end.c delete mode 100644 tests/tests2/66_macro_concat_end.expect delete mode 100644 tests/tests2/68_macro_param_list_err_1.c delete mode 100644 tests/tests2/68_macro_param_list_err_1.expect delete mode 100644 tests/tests2/69_macro_param_list_err_2.c delete mode 100644 tests/tests2/69_macro_param_list_err_2.expect create mode 100644 tests/tests2/72_long_long_constant.expect rewrite tests/tests2/Makefile (63%) diff --git a/configure b/configure index 9bef59fd..01bcdb11 100755 --- a/configure +++ b/configure @@ -449,12 +449,6 @@ LIBSUF=$LIBSUF EXESUF=$EXESUF HOST_OS=$host_os EOF -if test "$mingw32" = "yes"; then -cat >>config.mak <error_func) { /* default case: stderr */ + if (s1->ppfp) /* print a newline during tcc -E */ + fprintf(s1->ppfp, "\n"), fflush(s1->ppfp); fprintf(stderr, "%s\n", buf); + fflush(stderr); /* print error/warning now (win32) */ } else { s1->error_func(s1->error_opaque, buf); } @@ -672,7 +675,7 @@ ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen) BufferedFile *bf; int buflen = initlen ? initlen : IO_BUF_SIZE; - bf = tcc_malloc(sizeof(BufferedFile) + buflen); + bf = tcc_mallocz(sizeof(BufferedFile) + buflen); bf->buf_ptr = bf->buffer; bf->buf_end = bf->buffer + initlen; bf->buf_end[0] = CH_EOB; /* put eob symbol */ @@ -681,7 +684,6 @@ ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen) normalize_slashes(bf->filename); #endif bf->line_num = 1; - bf->ifndef_macro = 0; bf->ifdef_stack_ptr = s1->ifdef_stack_ptr; bf->fd = -1; bf->prev = file; @@ -703,7 +705,7 @@ ST_FUNC int tcc_open(TCCState *s1, const char *filename) { int fd; if (strcmp(filename, "-") == 0) - fd = 0, filename = "stdin"; + fd = 0, filename = ""; else fd = open(filename, O_RDONLY | O_BINARY); if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3) @@ -721,7 +723,6 @@ ST_FUNC int tcc_open(TCCState *s1, const char *filename) static int tcc_compile(TCCState *s1) { Sym *define_start; - SValue *pvtop; char buf[512]; volatile int section_sym; @@ -797,14 +798,12 @@ static int tcc_compile(TCCState *s1) ch = file->buf_ptr[0]; tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; - parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM; - pvtop = vtop; + parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | PARSE_FLAG_TOK_STR; next(); decl(VT_CONST); if (tok != TOK_EOF) expect("declaration"); - if (pvtop != vtop) - tcc_warning("internal compiler error: vstack leak? (%d)", vtop - pvtop); + check_vstack(); /* end of translation unit info */ if (s1->do_debug) { @@ -829,31 +828,13 @@ static int tcc_compile(TCCState *s1) LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str) { - int i; int len, ret; - len = strlen(str); + len = strlen(str); tcc_open_bf(s, "", len); memcpy(file->buffer, str, len); - - len = s->nb_files; ret = tcc_compile(s); tcc_close(); - - /* habdle #pragma comment(lib,) */ - for(i = len; i < s->nb_files; i++) { - /* int filetype = *(unsigned char *)s->files[i]; */ - const char *filename = s->files[i] + 1; - if (filename[0] == '-' && filename[1] == 'l') { - if (tcc_add_library(s, filename + 2) < 0) { - tcc_warning("cannot find library 'lib%s'", filename+2); - ret++; - } - } - tcc_free(s->files[i]); - } - s->nb_files = len; - return ret; } @@ -896,20 +877,11 @@ LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym) /* cleanup all static data used during compilation */ static void tcc_cleanup(void) { - int i, n; if (NULL == tcc_state) return; tcc_state = NULL; - /* free -D defines */ - free_defines(NULL); - - /* free tokens */ - n = tok_ident - TOK_IDENT; - for(i = 0; i < n; i++) - tcc_free(table_ident[i]); - tcc_free(table_ident); - table_ident = NULL; + preprocess_delete(); /* free sym_pools */ dynarray_reset(&sym_pools, &nb_sym_pools); @@ -917,8 +889,6 @@ static void tcc_cleanup(void) cstr_free(&tokcstr); /* reset symbol stack */ sym_free_first = NULL; - /* cleanup from error/setjmp */ - macro_ptr = NULL; } LIBTCCAPI TCCState *tcc_new(void) @@ -1128,6 +1098,7 @@ LIBTCCAPI void tcc_delete(TCCState *s1) tcc_free(s1->deps_outfile); dynarray_reset(&s1->files, &s1->nb_files); dynarray_reset(&s1->target_deps, &s1->nb_target_deps); + dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs); #ifdef TCC_IS_NATIVE # ifdef HAVE_SELINUX @@ -1138,8 +1109,7 @@ LIBTCCAPI void tcc_delete(TCCState *s1) # endif #endif - if(s1->sym_attrs) tcc_free(s1->sym_attrs); - + tcc_free(s1->sym_attrs); tcc_free(s1); } @@ -1338,6 +1308,22 @@ LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname) return -1; } +PUB_FUNC int tcc_add_library_err(TCCState *s, const char *libname) +{ + int ret = tcc_add_library(s, libname); + if (ret < 0) + tcc_error_noabort("cannot find library 'lib%s'", libname); + return ret; +} + +/* habdle #pragma comment(lib,) */ +ST_FUNC void tcc_add_pragma_libs(TCCState *s1) +{ + int i; + for (i = 0; i < s1->nb_pragma_libs; i++) + tcc_add_library_err(s1, s1->pragma_libs[i]); +} + LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val) { #ifdef TCC_TARGET_PE @@ -1355,8 +1341,6 @@ LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val) LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type) { s->output_type = output_type; - if (output_type == TCC_OUTPUT_PREPROCESS) - print_defines(); if (!s->nostdinc) { /* default include paths */ @@ -1694,7 +1678,6 @@ enum { TCC_OPTION_g, TCC_OPTION_c, TCC_OPTION_dumpversion, - TCC_OPTION_d, TCC_OPTION_float_abi, TCC_OPTION_static, TCC_OPTION_std, @@ -1751,7 +1734,6 @@ static const TCCOption tcc_options[] = { { "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, { "c", TCC_OPTION_c, 0 }, { "dumpversion", TCC_OPTION_dumpversion, 0}, - { "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, #ifdef TCC_TARGET_ARM { "mfloat-abi", TCC_OPTION_float_abi, TCC_OPTION_HAS_ARG }, #endif @@ -1878,8 +1860,7 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv) case TCC_OPTION_HELP: return 0; case TCC_OPTION_I: - if (tcc_add_include_path(s, optarg) < 0) - tcc_error("too many include paths"); + tcc_add_include_path(s, optarg); break; case TCC_OPTION_D: parse_option_D(s, optarg); @@ -1924,14 +1905,6 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv) tcc_warning("-c: some compiler action already specified (%d)", s->output_type); s->output_type = TCC_OUTPUT_OBJ; break; - case TCC_OPTION_d: - if (*optarg != 'D') { - if (s->warn_unsupported) - goto unsupported_option; - tcc_error("invalid option -- '%s'", r); - } - s->dflag = 1; - break; #ifdef TCC_TARGET_ARM case TCC_OPTION_float_abi: /* tcc doesn't support soft float yet */ diff --git a/tcc.c b/tcc.c index a0b1c700..29508eb7 100644 --- a/tcc.c +++ b/tcc.c @@ -55,31 +55,28 @@ static void display_info(TCCState *s, int what) # endif #endif #ifdef TCC_TARGET_PE - ", mingw" + " Windows" #else - #ifdef __linux - ", Linux" - #else - ", Unknown" - #endif + " Linux" #endif ")\n", TCC_VERSION); break; case 1: printf("install: %s\n", s->tcc_lib_path); /* print_paths("programs", NULL, 0); */ - print_paths("crt", s->crt_paths, s->nb_crt_paths); - print_paths("libraries", s->library_paths, s->nb_library_paths); print_paths("include", s->sysinclude_paths, s->nb_sysinclude_paths); + print_paths("libraries", s->library_paths, s->nb_library_paths); +#ifndef TCC_TARGET_PE + print_paths("crt", s->crt_paths, s->nb_crt_paths); printf("elfinterp:\n %s\n", DEFAULT_ELFINTERP(s)); +#endif break; } } -static void help(TCCState *s) +static void help(void) { - display_info(s, 0); - printf("Tiny C Compiler - Copyright (C) 2001-2006 Fabrice Bellard\n" + printf("Tiny C Compiler "TCC_VERSION" - Copyright (C) 2001-2006 Fabrice Bellard\n" "Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n" " tcc [options...] -run infile [arguments...]\n" "General options:\n" @@ -94,13 +91,11 @@ static void help(TCCState *s) " -dumpversion\n" " -bench show compilation statistics\n" "Preprocessor options:\n" - " -E preprocess only\n" " -Idir add include path 'dir'\n" " -Dsym[=val] define 'sym' with value 'val'\n" " -Usym undefine 'sym'\n" - " -P do not output a #line directive\n" - " -P1 use a #line directive in output instead of the gcc style\n" - " -dD put a define directive in the output (inside a comment)\n" + " -E preprocess only\n" + " -P[1] no/alternative output of #line directives with -E\n" "Linker options:\n" " -Ldir add library path 'dir'\n" " -llib link with dynamic or static library 'lib'\n" @@ -260,7 +255,7 @@ int main(int argc, char **argv) tcc_set_environment(s); if (optind == 0) { - help(s); + help(); return 1; } diff --git a/tcc.h b/tcc.h index 331d6e4f..314045b3 100644 --- a/tcc.h +++ b/tcc.h @@ -498,11 +498,12 @@ typedef struct BufferedFile { int fd; struct BufferedFile *prev; int line_num; /* current line number - here to simplify code */ - int line_ref; /* moved from tcc_preprocess(), needed for a right ouput in other places */ + int line_ref; /* tcc -E: last printed line */ int ifndef_macro; /* #ifndef macro / #endif search */ int ifndef_macro_saved; /* saved ifndef_macro */ int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */ char filename[1024]; /* filename */ + unsigned char unget[4]; unsigned char buffer[1]; /* extra size for CH_EOB char */ } BufferedFile; @@ -524,11 +525,15 @@ typedef struct TokenString { int len; int allocated_len; int last_line_num; + /* used to chain token-strings with begin/end_macro() */ + struct TokenString *prev; + const int *prev_ptr; + char alloc; } TokenString; /* inline functions */ typedef struct InlineFunc { - int *token_str; + TokenString func_str; Sym *sym; char filename[1]; } InlineFunc; @@ -663,8 +668,7 @@ struct TCCState { LINE_MACRO_OUTPUT_FORMAT_GCC, LINE_MACRO_OUTPUT_FORMAT_NONE, LINE_MACRO_OUTPUT_FORMAT_STD, - } Pflag; - int dflag; /* for keeping a -dD value */ + } Pflag; /* -P switch */ /* for -MD/-MF: collected dependencies for this compilation */ char **target_deps; @@ -685,6 +689,8 @@ struct TCCState { /* #pragma pack stack */ int pack_stack[PACK_STACK_SIZE]; int *pack_stack_ptr; + char **pragma_libs; + int nb_pragma_libs; /* inline functions are stored as token lists and compiled last only if referenced */ @@ -860,21 +866,21 @@ struct TCCState { #define TOK_CDOUBLE 0xbc /* double constant */ #define TOK_CLDOUBLE 0xbd /* long double constant */ #define TOK_PPNUM 0xbe /* preprocessor number */ -#define TOK_LINENUM 0xbf /* line number info */ +#define TOK_PPSTR 0xbf /* preprocessor string */ +#define TOK_LINENUM 0xc0 /* line number info */ /* <-- */ -#define TOK_TWOSHARPS 0xc0 /* ## preprocessing token */ -#define TOK_PLCHLDR 0xc1 /* placeholder token as defined in C99 */ #define TOK_UMULL 0xc2 /* unsigned 32x32 -> 64 mul */ #define TOK_ADDC1 0xc3 /* add with carry generation */ #define TOK_ADDC2 0xc4 /* add with carry use */ #define TOK_SUBC1 0xc5 /* add with carry generation */ #define TOK_SUBC2 0xc6 /* add with carry use */ -#define TOK_ARROW 0xcb -#define TOK_DOTS 0xcc /* three dots */ -#define TOK_SHR 0xcd /* unsigned shift right */ -#define TOK_NOSUBST 0xcf /* means following token has already been pp'd */ -#define TOK_GNUCOMMA 0xd0 /* ,## preprocessing token */ +#define TOK_ARROW 0xc7 +#define TOK_DOTS 0xc8 /* three dots */ +#define TOK_SHR 0xc9 /* unsigned shift right */ +#define TOK_TWOSHARPS 0xca /* ## preprocessing token */ +#define TOK_PLCHLDR 0xcb /* placeholder token as defined in C99 */ +#define TOK_NOSUBST 0xcc /* means following token has already been pp'd */ #define TOK_SHL 0x01 /* shift left */ #define TOK_SAR 0x02 /* signed shift right */ @@ -1113,10 +1119,11 @@ ST_FUNC void tcc_close(void); ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags, int filetype); ST_FUNC int tcc_add_crt(TCCState *s, const char *filename); ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags); +ST_FUNC void tcc_add_pragma_libs(TCCState *s1); +PUB_FUNC int tcc_add_library_err(TCCState *s, const char *f); PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time); PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv); - PUB_FUNC void tcc_set_environment(TCCState *s); /* ------------ tccpp.c ------------ */ @@ -1148,9 +1155,12 @@ ST_DATA TokenSym **table_ident; #define PARSE_FLAG_ASM_FILE 0x0008 /* we processing an asm file: '#' can be used for line comment, etc. */ #define PARSE_FLAG_SPACES 0x0010 /* next() returns space tokens (for -E) */ #define PARSE_FLAG_ACCEPT_STRAYS 0x0020 /* next() returns '\\' token */ +#define PARSE_FLAG_TOK_STR 0x0040 /* return parsed strings instead of TOK_PPSTR */ ST_FUNC TokenSym *tok_alloc(const char *str, int len); -ST_FUNC char *get_tok_str(int v, CValue *cv); +ST_FUNC const char *get_tok_str(int v, CValue *cv); +ST_FUNC void begin_macro(TokenString *str, int alloc); +ST_FUNC void end_macro(void); ST_FUNC void save_parse_state(ParseState *s); ST_FUNC void restore_parse_state(ParseState *s); ST_INLN void tok_str_new(TokenString *s); @@ -1161,7 +1171,6 @@ ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg); ST_FUNC void define_undef(Sym *s); ST_INLN Sym *define_find(int v); ST_FUNC void free_defines(Sym *b); -ST_FUNC void print_defines(void); ST_FUNC Sym *label_find(int v); ST_FUNC Sym *label_push(Sym **ptop, int v, int flags); ST_FUNC void label_pop(Sym **ptop, Sym *slast); @@ -1172,6 +1181,7 @@ ST_FUNC void next(void); ST_INLN void unget_tok(int last_tok); ST_FUNC void preprocess_init(TCCState *s1); ST_FUNC void preprocess_new(void); +ST_FUNC void preprocess_delete(void); ST_FUNC int tcc_preprocess(TCCState *s1); ST_FUNC void skip(int c); ST_FUNC NORETURN void expect(const char *msg); @@ -1204,7 +1214,7 @@ ST_DATA Sym *local_label_stack; ST_DATA Sym *global_label_stack; ST_DATA Sym *define_stack; ST_DATA CType char_pointer_type, func_old_type, int_type, size_type; -ST_DATA SValue __vstack[1+/*to make bcheck happy*/ VSTACK_SIZE], *vtop; +ST_DATA SValue __vstack[1+/*to make bcheck happy*/ VSTACK_SIZE], *vtop, *pvtop; #define vstack (__vstack + 1) ST_DATA int rsym, anon_sym, ind, loc; @@ -1215,8 +1225,9 @@ ST_DATA CType func_vt; /* current function return type (used by return instructi ST_DATA int func_var; /* true if current function is variadic */ ST_DATA int func_vc; ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */ -ST_DATA char *funcname; +ST_DATA const char *funcname; +ST_FUNC void check_vstack(void); ST_INLN int is_float(int t); ST_FUNC int ieee_finite(double d); ST_FUNC void test_lvalue(void); diff --git a/tccasm.c b/tccasm.c index b46ce3d9..3cbae198 100644 --- a/tccasm.c +++ b/tccasm.c @@ -747,7 +747,7 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess) ch = file->buf_ptr[0]; tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; - parse_flags = PARSE_FLAG_ASM_FILE; + parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR; if (do_preprocess) parse_flags |= PARSE_FLAG_PREPROCESS; next(); diff --git a/tccelf.c b/tccelf.c index 581ebefa..1f879465 100644 --- a/tccelf.c +++ b/tccelf.c @@ -1605,6 +1605,8 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1) /* add tcc runtime libraries */ ST_FUNC void tcc_add_runtime(TCCState *s1) { + tcc_add_pragma_libs(s1); + /* add libc */ if (!s1->nostdlib) { tcc_add_library(s1, "c"); diff --git a/tccgen.c b/tccgen.c index 5c89b2d3..fcec8f10 100644 --- a/tccgen.c +++ b/tccgen.c @@ -59,7 +59,7 @@ ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */ ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */ ST_DATA int vla_sp_loc; /* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */ -ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop; +ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop, *pvtop; ST_DATA int const_wanted; /* true if constant wanted */ ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */ @@ -68,7 +68,7 @@ ST_DATA CType func_vt; /* current function return type (used by return instructi ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */ ST_DATA int func_vc; ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */ -ST_DATA char *funcname; +ST_DATA const char *funcname; ST_DATA CType char_pointer_type, func_old_type, int_type, size_type; @@ -118,6 +118,12 @@ ST_FUNC void test_lvalue(void) expect("lvalue"); } +ST_FUNC void check_vstack(void) +{ + if (pvtop != vtop) + tcc_error("internal compiler error: vstack leak (%d)", vtop - pvtop); +} + /* ------------------------------------------------------------------------- */ /* symbol allocator */ static Sym *__sym_malloc(void) @@ -4005,9 +4011,9 @@ ST_FUNC void unary(void) break; } case TOK___va_arg: { + CType type; if (nocode_wanted) tcc_error("statement in global scope"); - CType type; next(); skip('('); expr_eq(); @@ -5777,7 +5783,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, /* compute size */ save_parse_state(&saved_parse_state); - macro_ptr = init_str.str; + begin_macro(&init_str, 0); next(); decl_initializer(type, NULL, 0, 1, 1); /* prepare second initializer parsing */ @@ -5937,7 +5943,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, decl_initializer(type, sec, addr, 1, 0); /* restore parse state if needed */ if (init_str.str) { - tok_str_free(init_str.str); + end_macro(); restore_parse_state(&saved_parse_state); } /* patch flexible array member size back to -1, */ @@ -6018,6 +6024,7 @@ static void func_decl_list(Sym *func_sym) static void gen_function(Sym *sym) { int saved_nocode_wanted = nocode_wanted; + nocode_wanted = 0; ind = cur_text_section->data_offset; /* NOTE: we patch the symbol size later */ @@ -6034,11 +6041,9 @@ static void gen_function(Sym *sym) sym_push2(&local_stack, SYM_FIELD, 0, 0); gfunc_prolog(&sym->type); #ifdef CONFIG_TCC_BCHECK - if (tcc_state->do_bounds_check - && !strcmp(get_tok_str(sym->v, NULL), "main")) { + if (tcc_state->do_bounds_check && !strcmp(funcname, "main")) { int i; - - sym = local_stack; + Sym *sym; for (i = 0, sym = local_stack; i < 2; i++, sym = sym->prev) { if (sym->v & SYM_FIELD || sym->prev->v & SYM_FIELD) break; @@ -6075,14 +6080,16 @@ static void gen_function(Sym *sym) func_var = 0; /* for safety */ ind = 0; /* for safety */ nocode_wanted = saved_nocode_wanted; + check_vstack(); } ST_FUNC void gen_inline_functions(void) { Sym *sym; - int *str, inline_generated, i; + int inline_generated, i, ln; struct InlineFunc *fn; + ln = file->line_num; /* iterate while inline function are referenced */ for(;;) { inline_generated = 0; @@ -6092,18 +6099,17 @@ ST_FUNC void gen_inline_functions(void) if (sym && sym->c) { /* the function was used: generate its code and convert it to a normal function */ - str = fn->token_str; fn->sym = NULL; if (file) pstrcpy(file->filename, sizeof file->filename, fn->filename); sym->r = VT_SYM | VT_CONST; sym->type.t &= ~VT_INLINE; - macro_ptr = str; + begin_macro(&fn->func_str, 0); next(); cur_text_section = text_section; gen_function(sym); - macro_ptr = NULL; /* fail safe */ + end_macro(); inline_generated = 1; } @@ -6111,10 +6117,12 @@ ST_FUNC void gen_inline_functions(void) if (!inline_generated) break; } + file->line_num = ln; + /* free tokens of unused inline functions */ for (i = 0; i < tcc_state->nb_inline_fns; ++i) { fn = tcc_state->inline_fns[i]; - str = fn->token_str; - tok_str_free(str); + if (fn->sym) + tok_str_free(fn->func_str.str); } dynarray_reset(&tcc_state->inline_fns, &tcc_state->nb_inline_fns); } @@ -6267,19 +6275,22 @@ static int decl0(int l, int is_for_loop_init) the compilation unit only if they are used */ if ((type.t & (VT_INLINE | VT_STATIC)) == (VT_INLINE | VT_STATIC)) { - TokenString func_str; int block_level; struct InlineFunc *fn; const char *filename; - tok_str_new(&func_str); + filename = file ? file->filename : ""; + fn = tcc_malloc(sizeof *fn + strlen(filename)); + strcpy(fn->filename, filename); + fn->sym = sym; + tok_str_new(&fn->func_str); block_level = 0; for(;;) { int t; if (tok == TOK_EOF) tcc_error("unexpected end of file"); - tok_str_add_tok(&func_str); + tok_str_add_tok(&fn->func_str); t = tok; next(); if (t == '{') { @@ -6290,13 +6301,8 @@ static int decl0(int l, int is_for_loop_init) break; } } - tok_str_add(&func_str, -1); - tok_str_add(&func_str, 0); - filename = file ? file->filename : ""; - fn = tcc_malloc(sizeof *fn + strlen(filename)); - strcpy(fn->filename, filename); - fn->sym = sym; - fn->token_str = func_str.str; + tok_str_add(&fn->func_str, -1); + tok_str_add(&fn->func_str, 0); dynarray_add((void ***)&tcc_state->inline_fns, &tcc_state->nb_inline_fns, fn); } else { diff --git a/tccpe.c b/tccpe.c index e392f407..0fd8c362 100644 --- a/tccpe.c +++ b/tccpe.c @@ -1767,17 +1767,18 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe) ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, SHN_UNDEF, start_symbol); + tcc_add_pragma_libs(s1); + if (0 == s1->nostdlib) { static const char *libs[] = { - "libtcc1.a", "msvcrt", "kernel32", "", "user32", "gdi32", NULL + "tcc1", "msvcrt", "kernel32", "", "user32", "gdi32", NULL }; const char **pp, *p; for (pp = libs; 0 != (p = *pp); ++pp) { if (0 == *p) { if (PE_DLL != pe_type && PE_GUI != pe_type) break; - } else if (pp == libs ? tcc_add_dll(s1, p, 0) : tcc_add_library(s1, p)) { - tcc_error_noabort("cannot find library: %s", p); + } else if (tcc_add_library_err(s1, p) < 0) { break; } } @@ -1805,8 +1806,8 @@ ST_FUNC int pe_output_file(TCCState * s1, const char *filename) pe.filename = filename; pe.s1 = s1; - tcc_add_bcheck(s1); pe_add_runtime(s1, &pe); + tcc_add_bcheck(s1); relocate_common_syms(); /* assign bss adresses */ tcc_add_linker_symbols(s1); diff --git a/tccpp.c b/tccpp.c index 95c3f233..1c03febf 100644 --- a/tccpp.c +++ b/tccpp.c @@ -40,14 +40,15 @@ ST_DATA TokenSym **table_ident; /* ------------------------------------------------------------------------- */ -static int *macro_ptr_allocated; -static const int *unget_saved_macro_ptr; -static int unget_saved_buffer[TOK_MAX_SIZE + 1]; -static int unget_buffer_enabled; static TokenSym *hash_ident[TOK_HASH_SIZE]; static char token_buf[STRING_MAX_SIZE + 1]; -/* true if isid(c) || isnum(c) */ -static unsigned char isidnum_table[256-CH_EOF]; +static unsigned char isidnum_table[256 - CH_EOF]; +/* isidnum_table flags: */ +#define IS_SPC 1 +#define IS_ID 2 +#define IS_NUM 4 + +static TokenString *macro_stack; static const char tcc_keywords[] = #define DEF(id, str) str "\0" @@ -85,18 +86,7 @@ static const unsigned char tok_two_chars[] = 0 }; -struct macro_level { - struct macro_level *prev; - const int *p; -}; - static void next_nomacro_spc(void); -static void macro_subst( - TokenString *tok_str, - Sym **nested_list, - const int *macro_str, - struct macro_level **can_read_stream - ); ST_FUNC void skip(int c) { @@ -110,6 +100,29 @@ ST_FUNC void expect(const char *msg) tcc_error("%s expected", msg); } +ST_FUNC void begin_macro(TokenString *str, int alloc) +{ + str->alloc = alloc; + str->prev = macro_stack; + str->prev_ptr = macro_ptr; + macro_ptr = str->str; + macro_stack = str; +} + +ST_FUNC void end_macro(void) +{ + TokenString *str = macro_stack; + macro_stack = str->prev; + macro_ptr = str->prev_ptr; + if (str->alloc == 2) { + str->alloc = 3; /* just mark as finished */ + } else { + tok_str_free(str->str); + if (str->alloc == 1) + tcc_free(str); + } +} + /* ------------------------------------------------------------------------- */ /* CString handling */ static void cstr_realloc(CString *cstr, int new_size) @@ -262,7 +275,7 @@ ST_FUNC TokenSym *tok_alloc(const char *str, int len) /* XXX: buffer overflow */ /* XXX: float tokens */ -ST_FUNC char *get_tok_str(int v, CValue *cv) +ST_FUNC const char *get_tok_str(int v, CValue *cv) { static char buf[STRING_MAX_SIZE + 1]; static CString cstr_buf; @@ -276,10 +289,6 @@ ST_FUNC char *get_tok_str(int v, CValue *cv) cstr_buf.size_allocated = sizeof(buf); p = buf; -/* just an explanation, should never happen: - if (v <= TOK_LINENUM && v >= TOK_CINT && cv == NULL) - tcc_error("internal error: get_tok_str"); */ - switch(v) { case TOK_CINT: case TOK_CUINT: @@ -304,12 +313,8 @@ ST_FUNC char *get_tok_str(int v, CValue *cv) cstr_ccat(&cstr_buf, '\0'); break; case TOK_PPNUM: - cstr = cv->cstr; - len = cstr->size - 1; - for(i=0;idata)[i]); - cstr_ccat(&cstr_buf, '\0'); - break; + case TOK_PPSTR: + return (char*)cv->cstr->data; case TOK_LSTR: cstr_ccat(&cstr_buf, 'L'); case TOK_STR: @@ -361,6 +366,10 @@ ST_FUNC char *get_tok_str(int v, CValue *cv) } q += 3; } + if (v >= 127) { + sprintf(buf, "<%02x>", v); + return buf; + } addv: *p++ = v; *p = '\0'; @@ -378,10 +387,13 @@ ST_FUNC char *get_tok_str(int v, CValue *cv) return cstr_buf.data; } -/* fill input buffer and peek next char */ -static int tcc_peekc_slow(BufferedFile *bf) +/* return the current character, handling end of block if necessary + (but not stray) */ +ST_FUNC int handle_eob(void) { + BufferedFile *bf = file; int len; + /* only tries to read if really end of buffer */ if (bf->buf_ptr >= bf->buf_end) { if (bf->fd != -1) { @@ -409,13 +421,6 @@ static int tcc_peekc_slow(BufferedFile *bf) } } -/* return the current character, handling end of block if necessary - (but not stray) */ -ST_FUNC int handle_eob(void) -{ - return tcc_peekc_slow(file); -} - /* read next char from current input file and handle end of input buffer */ ST_INLN void inp(void) { @@ -459,20 +464,21 @@ static int handle_stray1(uint8_t *p) { int c; + file->buf_ptr = p; if (p >= file->buf_end) { - file->buf_ptr = p; c = handle_eob(); + if (c != '\\') + return c; p = file->buf_ptr; - if (c == '\\') - goto parse_stray; - } else { - parse_stray: - file->buf_ptr = p; - ch = *p; - handle_stray(); - p = file->buf_ptr; - c = *p; } + ch = *p; + if (handle_stray_noerror()) { + if (!(parse_flags & PARSE_FLAG_ACCEPT_STRAYS)) + tcc_error("stray '\\' in program"); + *--file->buf_ptr = '\\'; + } + p = file->buf_ptr; + c = *p; return c; } @@ -625,13 +631,13 @@ ST_FUNC uint8_t *parse_comment(uint8_t *p) static inline void skip_spaces(void) { - while (is_space(ch)) + while (isidnum_table[ch - CH_EOF] & IS_SPC) cinp(); } static inline int check_space(int t, int *spc) { - if (is_space(t)) { + if (t < 256 && (isidnum_table[t - CH_EOF] & IS_SPC)) { if (*spc) return 1; *spc = 1; @@ -779,10 +785,7 @@ redo_start: in_warn_or_error = 1; else if (tok == TOK_LINEFEED) goto redo_start; - else if (parse_flags & PARSE_FLAG_ASM_FILE) - p = parse_line_comment(p); - } - else if (parse_flags & PARSE_FLAG_ASM_FILE) + } else if (parse_flags & PARSE_FLAG_ASM_FILE) p = parse_line_comment(p); break; _default: @@ -822,9 +825,9 @@ ST_FUNC void restore_parse_state(ParseState *s) /* return the number of additional 'ints' necessary to store the token */ -static inline int tok_ext_size(int t) +static inline int tok_size(const int *p) { - switch(t) { + switch(*p) { /* 4 bytes */ case TOK_CINT: case TOK_CUINT: @@ -832,20 +835,20 @@ static inline int tok_ext_size(int t) case TOK_LCHAR: case TOK_CFLOAT: case TOK_LINENUM: - return 1; + return 1 + 1; case TOK_STR: case TOK_LSTR: case TOK_PPNUM: - tcc_error("unsupported token"); - return 1; + case TOK_PPSTR: + return 1 + ((sizeof(CString) + ((CString *)(p+1))->size + 3) >> 2); case TOK_CDOUBLE: case TOK_CLLONG: case TOK_CULLONG: - return 2; + return 1 + 2; case TOK_CLDOUBLE: - return LDOUBLE_SIZE / 4; + return 1 + LDOUBLE_SIZE / 4; default: - return 0; + return 1 + 0; } } @@ -912,6 +915,7 @@ static void tok_str_add2(TokenString *s, int t, CValue *cv) str[len++] = cv->tab[0]; break; case TOK_PPNUM: + case TOK_PPSTR: case TOK_STR: case TOK_LSTR: { @@ -995,6 +999,7 @@ static inline void TOK_GET(int *t, const int **pp, CValue *cv) case TOK_STR: case TOK_LSTR: case TOK_PPNUM: + case TOK_PPSTR: cv->cstr = (CString *)p; cv->cstr->data = (char *)p + sizeof(CString); p += (sizeof(CString) + cv->cstr->size + 3) >> 2; @@ -1040,54 +1045,6 @@ static int macro_is_equal(const int *a, const int *b) return !(*a || *b); } -static void define_print(Sym *s, int is_undef) -{ - int c, t; - CValue cval; - const int *str; - Sym *arg; - - if (tcc_state->dflag == 0 || !s || !tcc_state->ppfp) - return; - - if (file) { - c = file->line_num - file->line_ref - 1; - if (c > 0) { - while (c--) - fputs("\n", tcc_state->ppfp); - file->line_ref = file->line_num; - } - } - - if (is_undef) { - fprintf(tcc_state->ppfp, "// #undef %s", get_tok_str(s->v, NULL)); - return; - } - - fprintf(tcc_state->ppfp, "// #define %s", get_tok_str(s->v, NULL)); - arg = s->next; - if (arg) { - char *sep = "("; - while (arg) { - fprintf(tcc_state->ppfp, "%s%s", sep, get_tok_str(arg->v & ~SYM_FIELD, NULL)); - sep = ","; - arg = arg->next; - } - fprintf(tcc_state->ppfp, ")"); - } - - str = s->d; - if (str) - fprintf(tcc_state->ppfp, " "); - - while (str) { - TOK_GET(&t, &str, &cval); - if (!t) - break; - fprintf(tcc_state->ppfp, "%s", get_tok_str(t, &cval)); - } -} - /* defines handling */ ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg) { @@ -1101,7 +1058,6 @@ ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg) s->d = str; s->next = first_arg; table_ident[v - TOK_IDENT]->sym_define = s; - define_print(s, 0); } /* undefined a define symbol. Its name is just set to zero */ @@ -1109,10 +1065,8 @@ ST_FUNC void define_undef(Sym *s) { int v; v = s->v; - if (v >= TOK_IDENT && v < tok_ident) { - define_print(s, 1); + if (v >= TOK_IDENT && v < tok_ident) table_ident[v - TOK_IDENT]->sym_define = NULL; - } } ST_INLN Sym *define_find(int v) @@ -1144,22 +1098,6 @@ ST_FUNC void free_defines(Sym *b) define_stack = b; } -ST_FUNC void print_defines(void) -{ - Sym *top, *s; - int v; - - top = define_stack; - while (top) { - v = top->v; - if (v >= TOK_IDENT && v < tok_ident) { - s = table_ident[v - TOK_IDENT]->sym_define; - define_print(s, 0); - } - top = top->prev; - } -} - /* label lookup */ ST_FUNC Sym *label_find(int v) { @@ -1241,20 +1179,59 @@ static int expr_preprocess(void) tok_str_add(&str, -1); /* simulate end of file */ tok_str_add(&str, 0); /* now evaluate C constant expression */ - macro_ptr = str.str; + begin_macro(&str, 0); next(); c = expr_const(); - macro_ptr = NULL; - tok_str_free(str.str); + end_macro(); return c != 0; } +//#define PP_DEBUG + +#if defined(PARSE_DEBUG) || defined(PP_DEBUG) +static void tok_print(const char *msg, const int *str) +{ + int t; + CValue cval; + + printf("%s ", msg); + while (1) { + TOK_GET(&t, &str, &cval); + if (!t) + break; + printf("%s", get_tok_str(t, &cval)); + } + printf("\n"); +} + +static void define_print(int v) +{ + Sym *s, *a; + + s = define_find(v); + printf("#define %s", get_tok_str(v, NULL)); + if (s->type.t == MACRO_FUNC) { + a = s->next; + printf("("); + if (a) + for (;;) { + printf("%s", get_tok_str(a->v & ~SYM_FIELD, NULL)); + if (!(a = a->next)) + break; + printf(","); + } + printf(")"); + } + tok_print("", s->d); +} +#endif + /* parse after #define */ ST_FUNC void parse_define(void) { Sym *s, *first, **ps; - int v, t, varg, is_vaargs, spc, ptok, macro_list_start; - int saved_parse_flags; + int v, t, varg, is_vaargs, spc; + int saved_parse_flags = parse_flags; TokenString str; v = tok; @@ -1264,11 +1241,12 @@ ST_FUNC void parse_define(void) first = NULL; t = MACRO_OBJ; /* '(' must be just after macro definition for MACRO_FUNC */ + parse_flags |= PARSE_FLAG_SPACES; next_nomacro_spc(); if (tok == '(') { next_nomacro(); ps = &first; - while (tok != ')') { + if (tok != ')') for (;;) { varg = tok; next_nomacro(); is_vaargs = 0; @@ -1280,12 +1258,15 @@ ST_FUNC void parse_define(void) next_nomacro(); } if (varg < TOK_IDENT) - tcc_error( "\'%s\' may not appear in parameter list", get_tok_str(varg, NULL)); + bad_list: + tcc_error("bad macro parameter list"); s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0); *ps = s; ps = &s->next; - if (tok != ',') - continue; + if (tok == ')') + break; + if (tok != ',' || is_vaargs) + goto bad_list; next_nomacro(); } next_nomacro_spc(); @@ -1293,37 +1274,36 @@ ST_FUNC void parse_define(void) } tok_str_new(&str); spc = 2; - /* EOF testing necessary for '-D' handling */ - ptok = 0; - macro_list_start = 1; - saved_parse_flags = parse_flags; parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED; while (tok != TOK_LINEFEED && tok != TOK_EOF) { - if (macro_list_start && spc == 2 && tok == TOK_TWOSHARPS) - tcc_error("'##' invalid at start of macro"); - ptok = tok; /* remove spaces around ## and after '#' */ if (TOK_TWOSHARPS == tok) { + if (2 == spc) + goto bad_twosharp; if (1 == spc) --str.len; - spc = 2; + spc = 3; } else if ('#' == tok) { - spc = 2; + spc = 4; } else if (check_space(tok, &spc)) { goto skip; } tok_str_add2(&str, tok, &tokc); - macro_list_start = 0; skip: next_nomacro_spc(); } + parse_flags = saved_parse_flags; - if (ptok == TOK_TWOSHARPS) - tcc_error("'##' invalid at end of macro"); if (spc == 1) --str.len; /* remove trailing space */ tok_str_add(&str, 0); + if (3 == spc) +bad_twosharp: + tcc_error("'##' cannot appear at either end of macro"); define_push(v, t, str.str, first); +#ifdef PP_DEBUG + define_print(v); +#endif } static inline int hash_cached_include(const char *filename) @@ -1381,14 +1361,49 @@ static inline void add_cached_include(TCCState *s1, const char *filename, int if static void pragma_parse(TCCState *s1) { next_nomacro(); - if (tok == TOK_pack) { - /* - This may be: - #pragma pack(1) // set - #pragma pack() // reset to default - #pragma pack(push,1) // push & set - #pragma pack(pop) // restore previous - */ + if (tok == TOK_push_macro || tok == TOK_pop_macro) { + int t = tok, v; + Sym *s; + + if (next(), tok != '(') + goto pragma_err; + if (next(), tok != TOK_STR) + goto pragma_err; + v = tok_alloc(tokc.cstr->data, tokc.cstr->size - 1)->tok; + if (next(), tok != ')') + goto pragma_err; + if (t == TOK_push_macro) { + while (NULL == (s = define_find(v))) + define_push(v, 0, NULL, NULL); + s->type.ref = s; /* set push boundary */ + } else { + for (s = define_stack; s; s = s->prev) + if (s->v == v && s->type.ref == s) { + s->type.ref = NULL; + break; + } + } + if (s) + table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL; + else + tcc_warning("unbalanced #pragma pop_macro"); + + } else if (tok == TOK_once) { + add_cached_include(s1, file->filename, TOK_once); + + } else if (s1->ppfp) { + /* tcc -E: keep pragmas below unchanged */ + unget_tok(' '); + unget_tok(TOK_PRAGMA); + unget_tok('#'); + unget_tok(TOK_LINEFEED); + + } else if (tok == TOK_pack) { + /* This may be: + #pragma pack(1) // set + #pragma pack() // reset to default + #pragma pack(push,1) // push & set + #pragma pack(pop) // restore previous */ next(); skip('('); if (tok == TOK_ASM_pop) { @@ -1419,81 +1434,32 @@ static void pragma_parse(TCCState *s1) } if (tok != ')') goto pragma_err; - } else if (tok == TOK_comment) { - if (s1->ms_extensions) { - next(); - skip('('); - if (tok == TOK_lib) { - next(); - skip(','); - if (tok != TOK_STR) { - tcc_error("invalid library specification"); - } else { - int len = strlen((char *)tokc.cstr->data); - char *file = tcc_malloc(len + 4); /* filetype, "-l", and \0 at the end */ - file[0] = TCC_FILETYPE_BINARY; - file[1] = '-'; - file[2] = 'l'; - strcpy(&file[3],(char *)tokc.cstr->data); - dynarray_add((void ***)&s1->files, &s1->nb_files, file); - } - next(); - tok = TOK_LINEFEED; - } else { - tcc_warning("unknown specifier '%s' in #pragma comment", get_tok_str(tok, &tokc)); - } - } else { - tcc_warning("#pragma comment(lib) is ignored"); - } - } else if (tok == TOK_push_macro || tok == TOK_pop_macro) { - int t = tok, v; - Sym *s; - if (next_nomacro(), tok != '(') - goto pragma_err; - if (next_nomacro(), tok != TOK_STR) + } else if (tok == TOK_comment) { + char *file; + next(); + skip('('); + if (tok != TOK_lib) + goto pragma_warn; + next(); + skip(','); + if (tok != TOK_STR) goto pragma_err; - v = tok_alloc(tokc.cstr->data, tokc.cstr->size - 1)->tok; - if (next_nomacro(), tok != ')') + file = tcc_strdup((char *)tokc.cstr->data); + dynarray_add((void ***)&s1->pragma_libs, &s1->nb_pragma_libs, file); + next(); + if (tok != ')') goto pragma_err; - if (t == TOK_push_macro) { - while (NULL == (s = define_find(v))) - define_push(v, 0, NULL, NULL); - s->type.ref = s; /* set push boundary */ - } else { - for (s = define_stack; s; s = s->prev) - if (s->v == v && s->type.ref == s) { - s->type.ref = NULL; - break; - } - } - if (s) - table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL; - else - tcc_warning("unbalanced #pragma pop_macro"); - - /* print info when tcc is called with "-E -dD" switches */ - if (s1->dflag && s1->ppfp) { - if (file) { - int c = file->line_num - file->line_ref - 1; - if (c > 0) { - while (c--) - fputs("\n", tcc_state->ppfp); - file->line_ref = file->line_num; - } - } - fprintf(s1->ppfp, "// #pragma %s_macro(\"%s\")", - (t == TOK_push_macro) ? "push" : "pop", - get_tok_str(v, NULL)); - } - } else if (tok == TOK_once) { - add_cached_include(s1, file->filename, TOK_once); } else { - tcc_warning("unknown #pragma %s", get_tok_str(tok, &tokc)); +pragma_warn: + if (s1->warn_unsupported) + tcc_warning("#pragma %s is ignored", get_tok_str(tok, &tokc)); } return; + pragma_err: tcc_error("malformed #pragma directive"); + return; } /* is_bof is true if first non space token at beginning of file */ @@ -1505,9 +1471,13 @@ ST_FUNC void preprocess(int is_bof) Sym *s; saved_parse_flags = parse_flags; - parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | - PARSE_FLAG_LINEFEED; - parse_flags |= (saved_parse_flags & PARSE_FLAG_ASM_FILE); + parse_flags = PARSE_FLAG_PREPROCESS + | PARSE_FLAG_TOK_NUM + | PARSE_FLAG_TOK_STR + | PARSE_FLAG_LINEFEED + | (parse_flags & PARSE_FLAG_ASM_FILE) + ; + next_nomacro(); redo: switch(tok) { @@ -1735,29 +1705,29 @@ include_done: goto the_end; } break; + case TOK_PPNUM: + n = strtoul((char*)tokc.cstr->data, &q, 10); + goto _line_num; case TOK_LINE: next(); if (tok != TOK_CINT) - tcc_error("A #line format is wrong"); - case TOK_PPNUM: - if (tok != TOK_CINT) { - char *p = tokc.cstr->data; - tokc.i = strtoul(p, (char **)&p, 10); - } - i = file->line_num; - file->line_num = tokc.i - 1; +_line_err: + tcc_error("wrong #line format"); + n = tokc.i; +_line_num: next(); if (tok != TOK_LINEFEED) { - if (tok != TOK_STR) { - if ((parse_flags & PARSE_FLAG_ASM_FILE) == 0) { - file->line_num = i; - tcc_error("#line format is wrong"); - } - break; - } - pstrcpy(file->filename, sizeof(file->filename), - (char *)tokc.cstr->data); + if (tok == TOK_STR) + pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.cstr->data); + else if (parse_flags & PARSE_FLAG_ASM_FILE) + break; + else + goto _line_err; + --n; } + if (file->fd > 0) + total_lines += file->line_num - n; + file->line_num = n; if (s1->do_debug) put_stabs(file->filename, N_BINCL, 0, 0, 0); break; @@ -1785,20 +1755,19 @@ include_done: case TOK_PRAGMA: pragma_parse(s1); break; + case TOK_LINEFEED: + goto the_end; default: - if (tok == TOK_LINEFEED || tok == '!' || tok == TOK_PPNUM) { - /* '!' is ignored to allow C scripts. numbers are ignored - to emulate cpp behaviour */ - } else { - if (!(parse_flags & PARSE_FLAG_ASM_FILE)) - tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc)); - else { - /* this is a gas line comment in an 'S' file. */ - file->buf_ptr = parse_line_comment(file->buf_ptr); - goto the_end; - } - } - break; + /* ignore gas line comment in an 'S' file. */ + if (saved_parse_flags & PARSE_FLAG_ASM_FILE) + goto ignore; + if (tok == '!' && is_bof) + /* '!' is ignored at beginning to allow C scripts. */ + goto ignore; + tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc)); + ignore: + file->buf_ptr = parse_line_comment(file->buf_ptr); + goto the_end; } /* ignore other preprocess commands or #! for C scripts */ while (tok != TOK_LINEFEED) @@ -1914,6 +1883,52 @@ static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long cstr_wccat(outstr, '\0'); } +void parse_string(const char *s, int len) +{ + uint8_t buf[1000], *p = buf; + int is_long, sep; + + if ((is_long = *s == 'L')) + ++s, --len; + sep = *s++; + len -= 2; + if (len >= sizeof buf) + p = tcc_malloc(len + 1); + memcpy(p, s, len); + p[len] = 0; + + cstr_reset(&tokcstr); + parse_escape_string(&tokcstr, p, is_long); + if (p != buf) + tcc_free(p); + + if (sep == '\'') { + int char_size; + /* XXX: make it portable */ + if (!is_long) + char_size = 1; + else + char_size = sizeof(nwchar_t); + if (tokcstr.size <= char_size) + tcc_error("empty character constant"); + if (tokcstr.size > 2 * char_size) + tcc_warning("multi-character character constant"); + if (!is_long) { + tokc.i = *(int8_t *)tokcstr.data; + tok = TOK_CCHAR; + } else { + tokc.i = *(nwchar_t *)tokcstr.data; + tok = TOK_LCHAR; + } + } else { + tokc.cstr = &tokcstr; + if (!is_long) + tok = TOK_STR; + else + tok = TOK_LSTR; + } +} + /* we use 64 bit numbers */ #define BN_SIZE 2 @@ -2256,7 +2271,11 @@ static inline void next_nomacro1(void) case '\t': tok = c; p++; - goto keep_tok_flags; + if (parse_flags & PARSE_FLAG_SPACES) + goto keep_tok_flags; + while (isidnum_table[*p - CH_EOF] & IS_SPC) + ++p; + goto redo_no_start; case '\f': case '\v': case '\r': @@ -2264,28 +2283,12 @@ static inline void next_nomacro1(void) goto redo_no_start; case '\\': /* first look if it is in fact an end of buffer */ - if (p >= file->buf_end) { - file->buf_ptr = p; - handle_eob(); - p = file->buf_ptr; - if (p >= file->buf_end) - goto parse_eof; - else - goto redo_no_start; - } else { - file->buf_ptr = p; - ch = *p; - if (parse_flags & PARSE_FLAG_ACCEPT_STRAYS) { - if (handle_stray_noerror() != 0) { - goto parse_simple; - } - } else { - handle_stray(); - } - p = file->buf_ptr; + c = handle_stray1(p); + p = file->buf_ptr; + if (c == '\\') + goto parse_simple; + if (c != CH_EOF) goto redo_no_start; - } - parse_eof: { TCCState *s1 = tcc_state; if ((parse_flags & PARSE_FLAG_LINEFEED) @@ -2340,11 +2343,6 @@ maybe_newline: case '#': /* XXX: simplify */ PEEKC(c, p); - if (is_space(c) && (parse_flags & PARSE_FLAG_ASM_FILE)) { - p = parse_line_comment(p); - goto redo_no_start; - } - else if ((tok_flags & TOK_FLAG_BOL) && (parse_flags & PARSE_FLAG_PREPROCESS)) { file->buf_ptr = p; @@ -2368,8 +2366,9 @@ maybe_newline: /* dollar is allowed to start identifiers when not parsing asm */ case '$': - if (!tcc_state->dollars_in_identifiers - || (parse_flags & PARSE_FLAG_ASM_FILE)) goto parse_simple; + if (!(isidnum_table[c - CH_EOF] & IS_ID) + || (parse_flags & PARSE_FLAG_ASM_FILE)) + goto parse_simple; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': @@ -2390,15 +2389,8 @@ maybe_newline: p1 = p; h = TOK_HASH_INIT; h = TOK_HASH_FUNC(h, c); - p++; - for(;;) { - c = *p; - if (!isidnum_table[c-CH_EOF] - && (tcc_state->dollars_in_identifiers ? (c != '$') : 1)) - break; + while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) h = TOK_HASH_FUNC(h, c); - p++; - } if (c != '\\') { TokenSym **pts; int len; @@ -2429,8 +2421,7 @@ maybe_newline: p--; PEEKC(c, p); parse_ident_slow: - while (isidnum_table[c-CH_EOF] - || (tcc_state->dollars_in_identifiers ? (c == '$') : 0)) { + while (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) { cstr_ccat(&tokcstr, c); PEEKC(c, p); } @@ -2455,10 +2446,10 @@ maybe_newline: } } break; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - cstr_reset(&tokcstr); /* after the first digit, accept digits, alpha, '.' or sign if prefixed by 'eEpP' */ @@ -2467,9 +2458,11 @@ maybe_newline: t = c; cstr_ccat(&tokcstr, c); PEEKC(c, p); - if (!(isnum(c) || isid(c) || c == '.' || - ((c == '+' || c == '-') && - (t == 'e' || t == 'E' || t == 'p' || t == 'P')))) + if (!((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) + || c == '.' + || ((c == '+' || c == '-') + && (t == 'e' || t == 'E' || t == 'p' || t == 'P') + ))) break; } /* We add a trailing '\0' to ease parsing */ @@ -2477,6 +2470,7 @@ maybe_newline: tokc.cstr = &tokcstr; tok = TOK_PPNUM; break; + case '.': /* special dot handling because it can also start a number */ PEEKC(c, p); @@ -2486,12 +2480,8 @@ maybe_newline: goto parse_num; } else if (c == '.') { PEEKC(c, p); - if (c != '.') { - if ((parse_flags & PARSE_FLAG_ASM_FILE) == 0) - expect("'.'"); - tok = '.'; - break; - } + if (c != '.') + expect("'.'"); PEEKC(c, p); tok = TOK_DOTS; } else { @@ -2502,48 +2492,15 @@ maybe_newline: case '\"': is_long = 0; str_const: - { - CString str; - int sep; - - sep = c; - - /* parse the string */ - cstr_new(&str); - p = parse_pp_string(p, sep, &str); - cstr_ccat(&str, '\0'); - - /* eval the escape (should be done as TOK_PPNUM) */ - cstr_reset(&tokcstr); - parse_escape_string(&tokcstr, str.data, is_long); - cstr_free(&str); - - if (sep == '\'') { - int char_size; - /* XXX: make it portable */ - if (!is_long) - char_size = 1; - else - char_size = sizeof(nwchar_t); - if (tokcstr.size <= char_size) - tcc_error("empty character constant"); - if (tokcstr.size > 2 * char_size) - tcc_warning("multi-character character constant"); - if (!is_long) { - tokc.i = *(int8_t *)tokcstr.data; - tok = TOK_CCHAR; - } else { - tokc.i = *(nwchar_t *)tokcstr.data; - tok = TOK_LCHAR; - } - } else { - tokc.cstr = &tokcstr; - if (!is_long) - tok = TOK_STR; - else - tok = TOK_LSTR; - } - } + cstr_reset(&tokcstr); + if (is_long) + cstr_ccat(&tokcstr, 'L'); + cstr_ccat(&tokcstr, c); + p = parse_pp_string(p, c, &tokcstr); + cstr_ccat(&tokcstr, c); + cstr_ccat(&tokcstr, '\0'); + tokc.cstr = &tokcstr; + tok = TOK_PPSTR; break; case '<': @@ -2563,7 +2520,6 @@ maybe_newline: tok = TOK_LT; } break; - case '>': PEEKC(c, p); if (c == '=') { @@ -2681,12 +2637,7 @@ maybe_newline: p++; break; default: - if ((parse_flags & PARSE_FLAG_ASM_FILE) == 0) - tcc_error("unrecognized character \\x%02x", c); - else { - tok = ' '; - p++; - } + tcc_error("unrecognized character \\x%02x", c); break; } tok_flags = 0; @@ -2720,23 +2671,30 @@ ST_FUNC void next_nomacro(void) { do { next_nomacro_spc(); - } while (is_space(tok)); + } while (tok < 256 && (isidnum_table[tok - CH_EOF] & IS_SPC)); } + +static void macro_subst( + TokenString *tok_str, + Sym **nested_list, + const int *macro_str, + int can_read_stream + ); + /* substitute arguments in replacement lists in macro_str by the values in args (field d) and return allocated string */ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) { - int last_tok, t, spc; + int t, t0, t1, spc; const int *st; Sym *s; CValue cval; TokenString str; CString cstr; - CString cstr2; tok_str_new(&str); - last_tok = 0; + t0 = t1 = 0; while(1) { TOK_GET(&t, ¯o_str, &cval); if (!t) @@ -2745,73 +2703,86 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) /* stringize */ TOK_GET(&t, ¯o_str, &cval); if (!t) - break; + goto bad_stringy; s = sym_find2(args, t); if (s) { cstr_new(&cstr); + cstr_ccat(&cstr, '\"'); st = s->d; spc = 0; while (*st) { TOK_GET(&t, &st, &cval); - if (t != TOK_PLCHLDR && !check_space(t, &spc)) - cstr_cat(&cstr, get_tok_str(t, &cval)); + if (t != TOK_PLCHLDR + && t != TOK_NOSUBST + && 0 == check_space(t, &spc)) { + const char *s = get_tok_str(t, &cval); + while (*s) { + if (t == TOK_PPSTR && *s != '\'') + add_char(&cstr, *s); + else + cstr_ccat(&cstr, *s); + ++s; + } + } } cstr.size -= spc; + cstr_ccat(&cstr, '\"'); cstr_ccat(&cstr, '\0'); #ifdef PP_DEBUG - printf("stringize: %s\n", (char *)cstr.data); + printf("\nstringize: <%s>\n", (char *)cstr.data); #endif /* add string */ - cstr_new(&cstr2); - /* emulate GCC behaviour and parse escapes in the token string */ - parse_escape_string(&cstr2, cstr.data, 0); - cstr_free(&cstr); - cval.cstr = &cstr2; - tok_str_add2(&str, TOK_STR, &cval); + cval.cstr = &cstr; + tok_str_add2(&str, TOK_PPSTR, &cval); cstr_free(cval.cstr); } else { - tok_str_add2(&str, t, &cval); + bad_stringy: + expect("macro parameter after '#'"); } } else if (t >= TOK_IDENT) { s = sym_find2(args, t); if (s) { + int l0 = str.len; st = s->d; /* if '##' is present before or after, no arg substitution */ - if (*macro_str == TOK_TWOSHARPS || last_tok == TOK_TWOSHARPS) { - /* special case for var arg macros : ## eats the - ',' if empty VA_ARGS variable. */ - /* XXX: test of the ',' is not 100% - reliable. should fix it to avoid security - problems */ - if (gnu_ext && s->type.t && - last_tok == TOK_TWOSHARPS && - str.len >= 2 && str.str[str.len - 2] == ',') { - str.len -= 2; - tok_str_add(&str, TOK_GNUCOMMA); + if (*macro_str == TOK_TWOSHARPS || t1 == TOK_TWOSHARPS) { + /* special case for var arg macros : ## eats the ',' + if empty VA_ARGS variable. */ + if (t1 == TOK_TWOSHARPS && t0 == ',' && gnu_ext && s->type.t) { + if (*st == 0) { + /* suppress ',' '##' */ + str.len -= 2; + } else { + /* suppress '##' and add variable */ + str.len--; + goto add_var; + } + } else { + for(;;) { + int t1; + TOK_GET(&t1, &st, &cval); + if (!t1) + break; + tok_str_add2(&str, t1, &cval); + } } - for(;;) { - int t1; - TOK_GET(&t1, &st, &cval); - if (!t1) - break; - tok_str_add2(&str, t1, &cval); - } } else { + add_var: /* NOTE: the stream cannot be read when macro substituing an argument */ - macro_subst(&str, nested_list, st, NULL); + macro_subst(&str, nested_list, st, 0); } + if (str.len == l0) /* exanded to empty string */ + tok_str_add(&str, TOK_PLCHLDR); } else { tok_str_add(&str, t); } } else { tok_str_add2(&str, t, &cval); } - last_tok = t; + t0 = t1, t1 = t; } - if (str.len == 0) - tok_str_add(&str, TOK_PLCHLDR); tok_str_add(&str, 0); return str.str; } @@ -2822,16 +2793,75 @@ static char const ab_month_name[12][4] = "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +/* peek or read [ws_str == NULL] next token from function macro call, + walking up macro levels up to the file if necessary */ +static int next_argstream(Sym **nested_list, int can_read_stream, TokenString *ws_str) +{ + int t; + const int *p; + Sym *sa; + + for (;;) { + if (macro_ptr) { + p = macro_ptr, t = *p; + if (ws_str) { + while (is_space(t) || TOK_LINEFEED == t) + tok_str_add(ws_str, t), t = *++p; + } + if (t == 0 && can_read_stream) { + end_macro(); + /* also, end of scope for nested defined symbol */ + sa = *nested_list; + while (sa && sa->v == -1) + sa = sa->prev; + if (sa) + sa->v = -1; + continue; + } + } else { + ch = handle_eob(); + if (ws_str) { + while (is_space(ch) || ch == '\n' || ch == '/') { + if (ch == '/') { + int c; + uint8_t *p = file->buf_ptr; + PEEKC(c, p); + if (c == '*') { + p = parse_comment(p); + file->buf_ptr = p - 1; + } else if (c == '/') { + p = parse_line_comment(p); + file->buf_ptr = p - 1; + } else + break; + ch = ' '; + } + tok_str_add(ws_str, ch); + cinp(); + } + } + t = ch; + } + + if (ws_str) + return t; + next_nomacro_spc(); + return tok; + } +} + /* do macro substitution of current token with macro 's' and add result to (tok_str,tok_len). 'nested_list' is the list of all macros we got inside to avoid recursing. Return non zero if no substitution needs to be done */ -static int macro_subst_tok(TokenString *tok_str, - Sym **nested_list, Sym *s, struct macro_level **can_read_stream) +static int macro_subst_tok( + TokenString *tok_str, + Sym **nested_list, + Sym *s, + int can_read_stream) { Sym *args, *sa, *sa1; - int mstr_allocated, parlevel, *mstr, t, t1, spc; - const int *p; + int parlevel, *mstr, t, t1, spc; TokenString str; char *cstrval; CValue cval; @@ -2872,89 +2902,48 @@ static int macro_subst_tok(TokenString *tok_str, tok_str_add2(tok_str, t1, &cval); cstr_free(&cstr); } else { - int mtok = tok; int saved_parse_flags = parse_flags; - parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED; mstr = s->d; - mstr_allocated = 0; if (s->type.t == MACRO_FUNC) { - TokenString ws_str; /* whitespace between macro name and - * argument list */ + /* whitespace between macro name and argument list */ + TokenString ws_str; tok_str_new(&ws_str); + spc = 0; - /* NOTE: we do not use next_nomacro to avoid eating the - next token. XXX: find better solution */ - redo: - if (macro_ptr) { - p = macro_ptr; - while (is_space(t = *p) || TOK_LINEFEED == t) { - if (saved_parse_flags & PARSE_FLAG_SPACES) - tok_str_add(&ws_str, t); - ++p; - } - if (t == 0 && can_read_stream) { - /* end of macro stream: we must look at the token - after in the file */ - struct macro_level *ml = *can_read_stream; - macro_ptr = NULL; - if (ml) - { - macro_ptr = ml->p; - ml->p = NULL; - *can_read_stream = ml -> prev; - } - /* also, end of scope for nested defined symbol */ - (*nested_list)->v = -1; - goto redo; - } - } else { - ch = tcc_peekc_slow(file); - while (is_space(ch) || ch == '\n' || ch == '/') - { - if (ch == '/') - { - int c; - uint8_t *p = file->buf_ptr; - PEEKC(c, p); - if (c == '*') { - p = parse_comment(p); - file->buf_ptr = p - 1; - } else if (c == '/') { - p = parse_line_comment(p); - file->buf_ptr = p - 1; - } else - break; - ch = ' '; - } - if (saved_parse_flags & PARSE_FLAG_SPACES) - tok_str_add(&ws_str, ch); - cinp(); - } - t = ch; - } + parse_flags |= PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED + | PARSE_FLAG_ACCEPT_STRAYS; + + /* get next token from argument stream */ + t = next_argstream(nested_list, can_read_stream, &ws_str); if (t != '(') { /* not a macro substitution after all, restore the * macro token plus all whitespace we've read. * whitespace is intentionally not merged to preserve * newlines. */ - int i; - tok_str_add(tok_str, mtok); - for(i=0; inext; /* NOTE: empty args are allowed, except if no args */ for(;;) { + do { + next_argstream(nested_list, can_read_stream, NULL); + } while (is_space(tok) || TOK_LINEFEED == tok); + empty_arg: /* handle '()' case */ if (!args && !sa && tok == ')') break; @@ -2977,13 +2966,11 @@ static int macro_subst_tok(TokenString *tok_str, tok = ' '; if (!check_space(tok, &spc)) tok_str_add2(&str, tok, &tokc); - next_nomacro_spc(); + next_argstream(nested_list, can_read_stream, NULL); } if (parlevel) expect(")"); str.len -= spc; - if (str.len == 0) - tok_str_add(&str, TOK_PLCHLDR); tok_str_add(&str, 0); sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0); sa1->d = str.str; @@ -2992,19 +2979,19 @@ static int macro_subst_tok(TokenString *tok_str, /* special case for gcc var args: add an empty var arg argument if it is omitted */ if (sa && sa->type.t && gnu_ext) - continue; - else - break; + goto empty_arg; + break; } if (tok != ',') expect(","); - next_nomacro(); } if (sa) { tcc_error("macro '%s' used with too few args", get_tok_str(s->v, 0)); } + parse_flags = saved_parse_flags; + /* now subst each arg */ mstr = macro_arg_subst(nested_list, mstr, args); /* free memory */ @@ -3015,258 +3002,232 @@ static int macro_subst_tok(TokenString *tok_str, sym_free(sa); sa = sa1; } - mstr_allocated = 1; } + sym_push2(nested_list, s->v, 0, 0); parse_flags = saved_parse_flags; macro_subst(tok_str, nested_list, mstr, can_read_stream); + /* pop nested defined symbol */ sa1 = *nested_list; *nested_list = sa1->prev; sym_free(sa1); - if (mstr_allocated) + if (mstr != s->d) tok_str_free(mstr); } return 0; } +int paste_tokens(int t1, CValue *v1, int t2, CValue *v2) +{ + CString cstr; + int n; + + cstr_new(&cstr); + if (t1 != TOK_PLCHLDR) + cstr_cat(&cstr, get_tok_str(t1, v1)); + n = cstr.size; + if (t2 != TOK_PLCHLDR) + cstr_cat(&cstr, get_tok_str(t2, v2)); + cstr_ccat(&cstr, '\0'); + + tcc_open_bf(tcc_state, ":paste:", cstr.size); + memcpy(file->buffer, cstr.data, cstr.size); + for (;;) { + next_nomacro1(); + if (0 == *file->buf_ptr) + break; + if (is_space(tok)) + continue; + tcc_warning("pasting <%.*s> and <%s> does not give a valid preprocessing token", + n, cstr.data, (char*)cstr.data + n); + break; + } + tcc_close(); + + //printf("paste <%s>\n", (char*)cstr.data); + cstr_free(&cstr); + return 0; +} + /* handle the '##' operator. Return NULL if no '##' seen. Otherwise return the resulting string (which must be freed). */ -static inline int *macro_twosharps(const int *macro_str) +static inline int *macro_twosharps(const int *ptr0) { - const int *ptr; int t; + CValue cval; TokenString macro_str1; - CString cstr; - int n, start_of_nosubsts; + int start_of_nosubsts = -1; + const int *ptr; /* we search the first '##' */ - for(ptr = macro_str;;) { - CValue cval; + for (ptr = ptr0;;) { TOK_GET(&t, &ptr, &cval); if (t == TOK_TWOSHARPS) break; - /* nothing more to do if end of string */ if (t == 0) return NULL; } - /* we saw '##', so we need more processing to handle it */ - start_of_nosubsts = -1; tok_str_new(¯o_str1); - for(ptr = macro_str;;) { - TOK_GET(&tok, &ptr, &tokc); - if (tok == 0) + + //tok_print(" $$$", ptr0); + for (ptr = ptr0;;) { + TOK_GET(&t, &ptr, &cval); + if (t == 0) break; - if (tok == TOK_TWOSHARPS) + if (t == TOK_TWOSHARPS) continue; - if (tok == TOK_NOSUBST && start_of_nosubsts < 0) - start_of_nosubsts = macro_str1.len; while (*ptr == TOK_TWOSHARPS) { + int t1; CValue cv1; /* given 'a##b', remove nosubsts preceding 'a' */ if (start_of_nosubsts >= 0) macro_str1.len = start_of_nosubsts; - /* given 'a##b', skip '##' */ - t = *++ptr; /* given 'a##b', remove nosubsts preceding 'b' */ - while (t == TOK_NOSUBST) - t = *++ptr; - if (t && t != TOK_TWOSHARPS) { - CValue cval; - TOK_GET(&t, &ptr, &cval); - /* We concatenate the two tokens */ - cstr_new(&cstr); - if (tok != TOK_PLCHLDR) - cstr_cat(&cstr, get_tok_str(tok, &tokc)); - n = cstr.size; - if (t != TOK_PLCHLDR || tok == TOK_PLCHLDR) - cstr_cat(&cstr, get_tok_str(t, &cval)); - cstr_ccat(&cstr, '\0'); - - tcc_open_bf(tcc_state, ":paste:", cstr.size); - memcpy(file->buffer, cstr.data, cstr.size); - for (;;) { - next_nomacro1(); - if (0 == *file->buf_ptr) - break; - tok_str_add2(¯o_str1, tok, &tokc); - tcc_warning("pasting \"%.*s\" and \"%s\" does not give a valid preprocessing token", - n, cstr.data, (char*)cstr.data + n); + while ((t1 = *++ptr) == TOK_NOSUBST) + ; + if (t1 && t1 != TOK_TWOSHARPS) { + TOK_GET(&t1, &ptr, &cv1); + if (t != TOK_PLCHLDR || t1 != TOK_PLCHLDR) { + paste_tokens(t, &cval, t1, &cv1); + t = tok, cval = tokc; } - tcc_close(); - cstr_free(&cstr); } } - if (tok != TOK_NOSUBST) { - tok_str_add2(¯o_str1, tok, &tokc); - tok = ' '; + if (t == TOK_NOSUBST) { + if (start_of_nosubsts < 0) + start_of_nosubsts = macro_str1.len; + } else { start_of_nosubsts = -1; } - tok_str_add2(¯o_str1, tok, &tokc); + tok_str_add2(¯o_str1, t, &cval); } tok_str_add(¯o_str1, 0); + //tok_print(" ###", macro_str1.str); return macro_str1.str; } - /* do macro substitution of macro_str and add result to (tok_str,tok_len). 'nested_list' is the list of all macros we got inside to avoid recursing. */ -static void macro_subst(TokenString *tok_str, Sym **nested_list, - const int *macro_str, struct macro_level ** can_read_stream) +static void macro_subst( + TokenString *tok_str, + Sym **nested_list, + const int *macro_str, + int can_read_stream + ) { Sym *s; - int *macro_str1; const int *ptr; - int t, spc; + int t, spc, nosubst; CValue cval; - struct macro_level ml; - int force_blank; - int gnucomma_index = -1; + int *macro_str1 = NULL; /* first scan for '##' operator handling */ ptr = macro_str; - macro_str1 = macro_twosharps(ptr); + spc = nosubst = 0; - if (macro_str1) - ptr = macro_str1; - spc = 0; - force_blank = 0; + /* first scan for '##' operator handling */ + if (can_read_stream) { + macro_str1 = macro_twosharps(ptr); + if (macro_str1) + ptr = macro_str1; + } while (1) { - /* NOTE: ptr == NULL can only happen if tokens are read from - file stream due to a macro function call */ - if (ptr == NULL) - break; TOK_GET(&t, &ptr, &cval); if (t == 0) break; - if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS)) { - tcc_error("stray '\\' in program"); - } - if (t == TOK_NOSUBST) { - /* following token has already been subst'd. just copy it on */ - tok_str_add2(tok_str, TOK_NOSUBST, NULL); - TOK_GET(&t, &ptr, &cval); - goto no_subst; - } - if (t == TOK_GNUCOMMA) { - if (gnucomma_index != -1) - tcc_error("two GNU commas in the same macro"); - gnucomma_index = tok_str->len; - tok_str_add(tok_str, ','); - TOK_GET(&t, &ptr, &cval); - } - s = define_find(t); - if (s != NULL) { - int old_len = tok_str->len; + + if (t >= TOK_IDENT && 0 == nosubst) { + s = define_find(t); + if (s == NULL) + goto no_subst; + /* if nested substitution, do nothing */ if (sym_find2(*nested_list, t)) { /* and mark it as TOK_NOSUBST, so it doesn't get subst'd again */ tok_str_add2(tok_str, TOK_NOSUBST, NULL); goto no_subst; } - ml.p = macro_ptr; - if (can_read_stream) - ml.prev = *can_read_stream, *can_read_stream = &ml; - macro_ptr = (int *)ptr; - tok = t; - macro_subst_tok(tok_str, nested_list, s, can_read_stream); + + { + TokenString str; + str.str = (int*)ptr; + begin_macro(&str, 2); + + tok = t; + macro_subst_tok(tok_str, nested_list, s, can_read_stream); + + if (str.alloc == 3) { + /* already finished by reading function macro arguments */ + break; + } + + ptr = macro_ptr; + end_macro (); + } + spc = tok_str->len && is_space(tok_str->str[tok_str->len-1]); - ptr = (int *)macro_ptr; - macro_ptr = ml.p; - if (can_read_stream && *can_read_stream == &ml) - *can_read_stream = ml.prev; - if (parse_flags & PARSE_FLAG_SPACES) - force_blank = 1; - if (old_len == tok_str->len) - tok_str_add(tok_str, TOK_PLCHLDR); + } else { - no_subst: - if (force_blank) { - tok_str_add(tok_str, ' '); - spc = 1; - force_blank = 0; - } - if (!check_space(t, &spc)) + + if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS)) + tcc_error("stray '\\' in program"); + +no_subst: + if (!check_space(t, &spc)) tok_str_add2(tok_str, t, &cval); + nosubst = 0; + if (t == TOK_NOSUBST) + nosubst = 1; } - if (gnucomma_index != -1) { - if (tok_str->len >= gnucomma_index+2) { - if (tok_str->str[gnucomma_index+1] == TOK_PLCHLDR) - tok_str->len -= 2; - gnucomma_index = -1; - } - } - if (tok_str->len && tok_str->str[tok_str->len-1] == TOK_PLCHLDR) - tok_str->len--; } if (macro_str1) tok_str_free(macro_str1); + } /* return next token with macro substitution */ ST_FUNC void next(void) { - Sym *nested_list, *s; - TokenString str; - struct macro_level *ml; - redo: if (parse_flags & PARSE_FLAG_SPACES) next_nomacro_spc(); else next_nomacro(); - if (!macro_ptr) { - /* if not reading from macro substituted string, then try - to substitute macros */ - if (tok >= TOK_IDENT && - (parse_flags & PARSE_FLAG_PREPROCESS)) { - s = define_find(tok); - if (s) { - /* we have a macro: we try to substitute */ - tok_str_new(&str); - nested_list = NULL; - ml = NULL; - if (macro_subst_tok(&str, &nested_list, s, &ml) == 0) { - /* substitution done, NOTE: maybe empty */ - tok_str_add(&str, 0); - macro_ptr = str.str; - macro_ptr_allocated = str.str; - goto redo; - } else { - tok_str_add(&str, 0); - if (str.len > 1) { - macro_ptr = str.str + 1; - macro_ptr_allocated = str.str; - } - tok = str.str[0]; - } - } - } - } else { - if (tok == 0) { - /* end of macro or end of unget buffer */ - if (unget_buffer_enabled) { - macro_ptr = unget_saved_macro_ptr; - unget_buffer_enabled = 0; - } else { - /* end of macro string: free it */ - tok_str_free(macro_ptr_allocated); - macro_ptr_allocated = NULL; - macro_ptr = NULL; - } + + if (macro_ptr) { + if (tok == TOK_NOSUBST || tok == TOK_PLCHLDR) { + /* discard preprocessor markers */ goto redo; - } else if (tok == TOK_NOSUBST) { - /* discard preprocessor's nosubst markers */ + } else if (tok == 0) { + /* end of macro or unget token string */ + end_macro(); + goto redo; + } + } else if (tok >= TOK_IDENT && (parse_flags & PARSE_FLAG_PREPROCESS)) { + Sym *s; + /* if reading from file, try to substitute macros */ + s = define_find(tok); + if (s) { + static TokenString str; /* using static string for speed */ + Sym *nested_list = NULL; + tok_str_new(&str); + nested_list = NULL; + macro_subst_tok(&str, &nested_list, s, 1); + tok_str_add(&str, 0); + begin_macro(&str, 0); goto redo; } } - /* convert preprocessor tokens into C tokens */ - if (tok == TOK_PPNUM && - (parse_flags & PARSE_FLAG_TOK_NUM)) { - parse_number((char *)tokc.cstr->data); + if (tok == TOK_PPNUM) { + if (parse_flags & PARSE_FLAG_TOK_NUM) + parse_number((char *)tokc.cstr->data); + } else if (tok == TOK_PPSTR) { + if (parse_flags & PARSE_FLAG_TOK_STR) + parse_string((char *)tokc.cstr->data, tokc.cstr->size - 1); } } @@ -3274,29 +3235,14 @@ ST_FUNC void next(void) identifier case handled for labels. */ ST_INLN void unget_tok(int last_tok) { - int i, n; - int *q; - if (unget_buffer_enabled) - { - /* assert(macro_ptr == unget_saved_buffer + 1); - assert(*macro_ptr == 0); */ - } - else - { - unget_saved_macro_ptr = macro_ptr; - unget_buffer_enabled = 1; - } - q = unget_saved_buffer; - macro_ptr = q; - *q++ = tok; - n = tok_ext_size(tok) - 1; - for(i=0;iifdef_stack_ptr = s1->ifdef_stack; file->ifdef_stack_ptr = s1->ifdef_stack_ptr; - vtop = vstack - 1; + pvtop = vtop = vstack - 1; s1->pack_stack[0] = 0; s1->pack_stack_ptr = s1->pack_stack; + + isidnum_table['$' - CH_EOF] = + tcc_state->dollars_in_identifiers ? IS_ID : 0; } ST_FUNC void preprocess_new(void) @@ -3318,15 +3267,13 @@ ST_FUNC void preprocess_new(void) const char *p, *r; /* init isid table */ + for(i = CH_EOF; i<256; i++) + isidnum_table[i - CH_EOF] + = is_space(i) ? IS_SPC + : isid(i) ? IS_ID + : isnum(i) ? IS_NUM + : 0; - for(i=CH_EOF;i<256;i++) - isidnum_table[i-CH_EOF] = isid(i) || isnum(i); - - /* add all tokens */ - if (table_ident) { - tcc_free (table_ident); - table_ident = NULL; - } memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *)); tok_ident = TOK_IDENT; @@ -3343,80 +3290,102 @@ ST_FUNC void preprocess_new(void) } } -static void line_macro_output(BufferedFile *f, const char *s, TCCState *s1) +ST_FUNC void preprocess_delete(void) { - switch (s1->Pflag) { - case LINE_MACRO_OUTPUT_FORMAT_STD: - /* "tcc -E -P1" case */ - fprintf(s1->ppfp, "# line %d \"%s\"\n", f->line_num, f->filename); - break; + int i, n; - case LINE_MACRO_OUTPUT_FORMAT_NONE: - /* "tcc -E -P" case: don't output a line directive */ - break; + /* free -D and compiler defines */ + free_defines(NULL); - case LINE_MACRO_OUTPUT_FORMAT_GCC: - default: - /* "tcc -E" case: a gcc standard by default */ - fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename, s); - break; + /* cleanup from error/setjmp */ + while (macro_stack) + end_macro(); + macro_ptr = NULL; + + /* free tokens */ + n = tok_ident - TOK_IDENT; + for(i = 0; i < n; i++) + tcc_free(table_ident[i]); + tcc_free(table_ident); + table_ident = NULL; +} + +static void pp_line(TCCState *s1, BufferedFile *f, int level) +{ + int d; + if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_NONE) + return; + if (level == 0 && f->line_ref && (d = f->line_num - f->line_ref) < 8) { + while (d > 0) + fputs("\n", s1->ppfp), --d; + } else if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_STD) { + fprintf(s1->ppfp, "#line %d \"%s\"\n", f->line_num, f->filename); + } else { + fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename, + level > 0 ? " 1" : level < 0 ? " 2" : ""); } + f->line_ref = f->line_num; } /* Preprocess the current file */ ST_FUNC int tcc_preprocess(TCCState *s1) { - BufferedFile *file_ref, **iptr, **iptr_new; - int token_seen, d; - const char *s; + Sym *define_start; + BufferedFile **iptr; + int token_seen, spcs, level; preprocess_init(s1); + define_start = define_stack; ch = file->buf_ptr[0]; tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; - parse_flags &= PARSE_FLAG_ASM_FILE; - parse_flags |= PARSE_FLAG_PREPROCESS | PARSE_FLAG_LINEFEED | PARSE_FLAG_SPACES | PARSE_FLAG_ACCEPT_STRAYS; - token_seen = 0; - file->line_ref = 0; - file_ref = NULL; - iptr = s1->include_stack_ptr; + parse_flags = PARSE_FLAG_PREPROCESS + | (parse_flags & PARSE_FLAG_ASM_FILE) + | PARSE_FLAG_LINEFEED + | PARSE_FLAG_SPACES + | PARSE_FLAG_ACCEPT_STRAYS + ; + +#ifdef PP_BENCH + do next(); while (tok != TOK_EOF); return 0; +#endif + + token_seen = spcs = 0; + pp_line(s1, file, 0); for (;;) { + iptr = s1->include_stack_ptr; next(); - if (tok == TOK_EOF) { + if (tok == TOK_EOF) break; - } else if (file != file_ref) { - if (file_ref) - line_macro_output(file_ref, "", s1); - goto print_line; - } else if (tok == TOK_LINEFEED) { - if (!token_seen) + level = s1->include_stack_ptr - iptr; + if (level) { + if (level > 0) + pp_line(s1, *iptr, 0); + pp_line(s1, file, level); + } + + if (0 == token_seen) { + if (tok == ' ') { + ++spcs; continue; - file->line_ref++; - token_seen = 0; - } else if (!token_seen) { - d = file->line_num - file->line_ref; - if (file != file_ref || d >= 8) { -print_line: - s = ""; - if (tcc_state->Pflag == LINE_MACRO_OUTPUT_FORMAT_GCC) { - iptr_new = s1->include_stack_ptr; - s = iptr_new > iptr ? " 1" - : iptr_new < iptr ? " 2" - : iptr_new > s1->include_stack ? " 3" - : "" - ; - } - line_macro_output(file, s, s1); - } else { - while (d > 0) - fputs("\n", s1->ppfp), --d; } - file->line_ref = (file_ref = file)->line_num; - token_seen = tok != TOK_LINEFEED; - if (!token_seen) + if (tok == TOK_LINEFEED) { + spcs = 0; continue; + } + pp_line(s1, file, 0); + while (spcs) + fputs(" ", s1->ppfp), --spcs; + token_seen = 1; + + } else if (tok == TOK_LINEFEED) { + ++file->line_ref; + token_seen = 0; } + fputs(get_tok_str(tok, &tokc), s1->ppfp); } + + free_defines(define_start); return 0; } diff --git a/tests/Makefile b/tests/Makefile index 98ccb1eb..270eaa71 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -19,7 +19,7 @@ TESTS = \ test3 \ abitest \ vla_test-run \ - moretests + tests2-dir pp-dir BTESTS = test1b test3b btest @@ -36,7 +36,7 @@ ifneq ($(ARCH),i386) TESTS := $(filter-out $(BTESTS),$(TESTS)) endif ifdef CONFIG_WIN32 - TESTS := w32-prep $(filter-out $(BTESTS),$(TESTS)) + TESTS := $(filter-out $(BTESTS),$(TESTS)) endif ifeq ($(TARGETOS),Darwin) TESTS := $(filter-out hello-exe test3 $(BTESTS),$(TESTS)) @@ -60,9 +60,9 @@ ifeq ($(TARGETOS),Darwin) endif # run local version of tcc with local libraries and includes -TCCFLAGS = -B$(TOP) -I$(TOP) -I$(top_srcdir) -I$(top_srcdir)/include +TCCFLAGS = -B$(TOP) -I$(TOP) -I$(top_srcdir) -I$(top_srcdir)/include -L$(TOP) ifdef CONFIG_WIN32 - TCCFLAGS = -B$(top_srcdir)/win32 -I$(top_srcdir) -I$(top_srcdir)/include -I$(TOP) -L$(TOP) + TCCFLAGS = -B$(top_srcdir)/win32 -I$(top_srcdir) -I$(top_srcdir)/include -L$(TOP) endif XTCCFLAGS = -B$(TOP) -B$(top_srcdir)/win32 -I$(TOP) -I$(top_srcdir) -I$(top_srcdir)/include @@ -97,17 +97,14 @@ hello-run: ../examples/ex1.c libtest: libtcc_test$(EXESUF) $(LIBTCC1) @echo ------------ $@ ------------ - ./libtcc_test$(EXESUF) lib_path=.. + ./libtcc_test$(EXESUF) $(TCCFLAGS) libtcc_test$(EXESUF): libtcc_test.c $(top_builddir)/$(LIBTCC) $(CC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) $(NATIVE_DEFINES) $(LIBS) $(LINK_LIBTCC) $(LDFLAGS) -I$(top_srcdir) -moretests: +%-dir: @echo ------------ $@ ------------ - $(MAKE) -C tests2 - -w32-prep: - cp ../libtcc1.a ../lib + $(MAKE) -k -C $* # test.ref - generate using cc test.ref: tcctest.c @@ -223,9 +220,8 @@ endif abitest: $(ABITESTS) @echo ------------ $@ ------------ - ./abitest-cc$(EXESUF) lib_path=.. include="$(top_srcdir)/include" - if [ "$(CONFIG_arm_eabi)" != "yes" ]; then \ - ./abitest-tcc$(EXESUF) lib_path=.. include="$(top_srcdir)/include"; fi + ./abitest-cc$(EXESUF) $(TCCFLAGS) + if [ "$(CONFIG_arm_eabi)" != "yes" ]; then ./abitest-tcc$(EXESUF) $(TCCFLAGS); fi vla_test$(EXESUF): vla_test.c $(TCC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) @@ -253,5 +249,4 @@ clean: $(MAKE) -C tests2 $@ rm -vf *~ *.o *.a *.bin *.i *.ref *.out *.out? *.out?b *.cc \ *-cc *-tcc *.exe \ - hello libtcc_test vla_test tcctest[1234] ex? tcc_g tcclib.h \ - ../lib/libtcc1.a + hello libtcc_test vla_test tcctest[1234] ex? tcc_g diff --git a/tests/abitest.c b/tests/abitest.c index 9cdeb87e..896e97b7 100644 --- a/tests/abitest.c +++ b/tests/abitest.c @@ -13,8 +13,24 @@ #define LONG_DOUBLE_LITERAL(x) x ## L #endif -static const char *tccdir = NULL; -static const char *include_dir = NULL; +static int g_argc; +static char **g_argv; + +static void set_options(TCCState *s, int argc, char **argv) +{ + int i; + for (i = 1; i < argc; ++i) { + char *a = argv[i]; + if (a[0] == '-') { + if (a[1] == 'B') + tcc_set_lib_path(s, a+2); + else if (a[1] == 'I') + tcc_add_include_path(s, a+2); + else if (a[1] == 'L') + tcc_add_library_path(s, a+2); + } + } +} typedef int (*callback_type) (void*); @@ -29,12 +45,9 @@ static int run_callback(const char *src, callback_type callback) { s = tcc_new(); if (!s) return -1; - if (tccdir) - tcc_set_lib_path(s, tccdir); - if (include_dir) { - if (tcc_add_include_path(s, include_dir) == -1) - return -1; - } + + set_options(s, g_argc, g_argv); + if (tcc_set_output_type(s, TCC_OUTPUT_MEMORY) == -1) return -1; if (tcc_compile_string(s, src) == -1) @@ -611,13 +624,11 @@ int main(int argc, char **argv) { /* if tcclib.h and libtcc1.a are not installed, where can we find them */ for (i = 1; i < argc; ++i) { - if (!memcmp(argv[i], "lib_path=",9)) - tccdir = argv[i] + 9; - else if (!memcmp(argv[i], "run_test=", 9)) + if (!memcmp(argv[i], "run_test=", 9)) testname = argv[i] + 9; - else if (!memcmp(argv[i], "include=", 8)) - include_dir = argv[i] + 8; - } + } + + g_argv = argv, g_argc = argc; RUN_TEST(ret_int_test); RUN_TEST(ret_longlong_test); diff --git a/tests/libtcc_test.c b/tests/libtcc_test.c index 414cc9bf..109e4884 100644 --- a/tests/libtcc_test.c +++ b/tests/libtcc_test.c @@ -16,7 +16,7 @@ int add(int a, int b) } char my_program[] = -"#include // printf()\n" +"#include \n" /* include the "Simple libc header for TCC" */ "extern int add(int a, int b);\n" "int fib(int n)\n" "{\n" @@ -37,6 +37,7 @@ char my_program[] = int main(int argc, char **argv) { TCCState *s; + int i; int (*func)(int); s = tcc_new(); @@ -46,8 +47,17 @@ int main(int argc, char **argv) } /* if tcclib.h and libtcc1.a are not installed, where can we find them */ - if (argc == 2 && !memcmp(argv[1], "lib_path=",9)) - tcc_set_lib_path(s, argv[1]+9); + for (i = 1; i < argc; ++i) { + char *a = argv[i]; + if (a[0] == '-') { + if (a[1] == 'B') + tcc_set_lib_path(s, a+2); + else if (a[1] == 'I') + tcc_add_include_path(s, a+2); + else if (a[1] == 'L') + tcc_add_library_path(s, a+2); + } + } /* MUST BE CALLED before any compilation */ tcc_set_output_type(s, TCC_OUTPUT_MEMORY); diff --git a/tests/pp/01.c b/tests/pp/01.c new file mode 100644 index 00000000..2fc3d796 --- /dev/null +++ b/tests/pp/01.c @@ -0,0 +1,6 @@ +#define hash_hash # ## # +#define mkstr(a) # a +#define in_between(a) mkstr(a) +#define join(c, d) in_between(c hash_hash d) +char p[] = join(x, y); +// char p[] = "x ## y"; diff --git a/tests/pp/01.expect b/tests/pp/01.expect new file mode 100644 index 00000000..cf5b1534 --- /dev/null +++ b/tests/pp/01.expect @@ -0,0 +1 @@ +char p[] = "x ## y"; diff --git a/tests/pp/02.c b/tests/pp/02.c new file mode 100644 index 00000000..feb1254e --- /dev/null +++ b/tests/pp/02.c @@ -0,0 +1,28 @@ +#define x 3 +#define f(a) f(x * (a)) +#undef x +#define x 2 +#define g f +#define z z[0] +#define h g(~ +#define m(a) a(w) +#define w 0,1 +#define t(a) a +#define p() int +#define q(x) x +#define r(x,y) x ## y +#define str(x) # x +f(y+1) + f(f(z)) % t(t(g)(0) + t)(1); +g(x+(3,4)-w) | h 5) & m +(f)^m(m); +char c[2][6] = { str(hello), str() }; +/* + * f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1); + * f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1); + * char c[2][6] = { "hello", "" }; + */ +#define L21 f(y+1) + f(f(z)) % t(t(g)(0) + t)(1); +#define L22 g(x+(3,4)-w) | h 5) & m\ +(f)^m(m); +L21 +L22 diff --git a/tests/pp/02.expect b/tests/pp/02.expect new file mode 100644 index 00000000..16f42aee --- /dev/null +++ b/tests/pp/02.expect @@ -0,0 +1,5 @@ +f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1); +f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1); +char c[2][6] = { "hello", "" }; +f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1); +f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1); diff --git a/tests/pp/03.c b/tests/pp/03.c new file mode 100644 index 00000000..a659245e --- /dev/null +++ b/tests/pp/03.c @@ -0,0 +1,15 @@ +#define str(s) # s +#define xstr(s) str(s) +#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \ + x ## s, x ## t) +#define INCFILE(n) vers ## n +#define glue(a, b) a ## b +#define xglue(a, b) glue(a, b) +#define HIGHLOW "hello" +#define LOW LOW ", world" +debug(1, 2); +fputs(str(strncmp("abc\0d", "abc", '\4') // this goes away + == 0) str(: @\n), s); +\#include xstr(INCFILE(2).h) +glue(HIGH, LOW); +xglue(HIGH, LOW) diff --git a/tests/pp/03.expect b/tests/pp/03.expect new file mode 100644 index 00000000..44aad0ae --- /dev/null +++ b/tests/pp/03.expect @@ -0,0 +1,5 @@ +printf("x" "1" "= %d, x" "2" "= %s", x1, x2); +fputs("strncmp(\"abc\\0d\", \"abc\", '\\4') == 0" ": @\n", s); +\#include "vers2.h" +"hello"; +"hello" ", world" diff --git a/tests/pp/04.c b/tests/pp/04.c new file mode 100644 index 00000000..0068f375 --- /dev/null +++ b/tests/pp/04.c @@ -0,0 +1,4 @@ +#define foobar 1 +#define C(x,y) x##y +#define D(x) (C(x,bar)) +D(foo) diff --git a/tests/pp/04.expect b/tests/pp/04.expect new file mode 100644 index 00000000..7c67b010 --- /dev/null +++ b/tests/pp/04.expect @@ -0,0 +1 @@ +(1) diff --git a/tests/pp/05.c b/tests/pp/05.c new file mode 100644 index 00000000..72749416 --- /dev/null +++ b/tests/pp/05.c @@ -0,0 +1,7 @@ +#define t(x,y,z) x ## y ## z +#define xxx(s) int s[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), \ + t(10,,), t(,11,), t(,,12), t(,,) }; + +int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), + t(10,,), t(,11,), t(,,12), t(,,) }; +xxx(j) diff --git a/tests/pp/05.expect b/tests/pp/05.expect new file mode 100644 index 00000000..9f9be378 --- /dev/null +++ b/tests/pp/05.expect @@ -0,0 +1,3 @@ +int j[] = { 123, 45, 67, 89, + 10, 11, 12, }; +int j[] = { 123, 45, 67, 89, 10, 11, 12, }; diff --git a/tests/pp/06.c b/tests/pp/06.c new file mode 100644 index 00000000..28cfddec --- /dev/null +++ b/tests/pp/06.c @@ -0,0 +1,5 @@ +#define X(a,b, \ + c,d) \ + foo + +X(1,2,3,4) diff --git a/tests/pp/06.expect b/tests/pp/06.expect new file mode 100644 index 00000000..257cc564 --- /dev/null +++ b/tests/pp/06.expect @@ -0,0 +1 @@ +foo diff --git a/tests/pp/07.c b/tests/pp/07.c new file mode 100644 index 00000000..b22b22bb --- /dev/null +++ b/tests/pp/07.c @@ -0,0 +1,4 @@ +#define a() YES +#define b() a +b() +b()() diff --git a/tests/pp/07.expect b/tests/pp/07.expect new file mode 100644 index 00000000..ad0e20a6 --- /dev/null +++ b/tests/pp/07.expect @@ -0,0 +1,2 @@ +a +YES diff --git a/tests/pp/08.c b/tests/pp/08.c new file mode 100644 index 00000000..93018e11 --- /dev/null +++ b/tests/pp/08.c @@ -0,0 +1,4 @@ +// test macro expansion in arguments +#define s_pos s_s.s_pos +#define foo(x) (x) +foo(hej.s_pos) diff --git a/tests/pp/08.expect b/tests/pp/08.expect new file mode 100644 index 00000000..2b2e3eeb --- /dev/null +++ b/tests/pp/08.expect @@ -0,0 +1 @@ +(hej.s_s.s_pos) diff --git a/tests/pp/09.c b/tests/pp/09.c new file mode 100644 index 00000000..315b297c --- /dev/null +++ b/tests/pp/09.c @@ -0,0 +1,4 @@ +#define C(a,b,c) a##b##c +#define N(x,y) C(x,_,y) +#define A_O aaaaoooo +N(A,O) diff --git a/tests/pp/09.expect b/tests/pp/09.expect new file mode 100644 index 00000000..adce0f95 --- /dev/null +++ b/tests/pp/09.expect @@ -0,0 +1 @@ +aaaaoooo diff --git a/tests/pp/10.c b/tests/pp/10.c new file mode 100644 index 00000000..f180eff1 --- /dev/null +++ b/tests/pp/10.c @@ -0,0 +1,10 @@ +#define f(x) x +#define g(x) f(x) f(x +#define i(x) g(x)) g(x +#define h(x) i(x))) i(x +#define k(x) i(x))) i(x)))) +f(x) +g(x)) +i(x))) +h(x)))) +k(x)))) diff --git a/tests/pp/10.expect b/tests/pp/10.expect new file mode 100644 index 00000000..bd18e18e --- /dev/null +++ b/tests/pp/10.expect @@ -0,0 +1,5 @@ +x +x x +x x x x +x x x x x x x x +x x x x x x x x)))) diff --git a/tests/pp/11.c b/tests/pp/11.c new file mode 100644 index 00000000..c0edf629 --- /dev/null +++ b/tests/pp/11.c @@ -0,0 +1,31 @@ +#define D1(s, ...) s +#define D2(s, ...) s D1(__VA_ARGS__) +#define D3(s, ...) s D2(__VA_ARGS__) +#define D4(s, ...) s D3(__VA_ARGS__) + +D1(a) +D2(a, b) +D3(a, b, c) +D4(a, b, c, d) + +x D4(a, b, c, d) y +x D4(a, b, c) y +x D4(a, b) y +x D4(a) y +x D4() y + +#define GNU_COMMA(X,Y...) X,## Y + +x GNU_COMMA(A,B,C) y +x GNU_COMMA(A,B) y +x GNU_COMMA(A) y +x GNU_COMMA() y + +#define __sun_attr___noreturn__ __attribute__((__noreturn__)) +#define ___sun_attr_inner(__a) __sun_attr_##__a +#define __sun_attr__(__a) ___sun_attr_inner __a +#define __NORETURN __sun_attr__((__noreturn__)) +__NORETURN +#define X(...) +#define Y(...) 1 __VA_ARGS__ 2 +Y(X X() ()) diff --git a/tests/pp/11.expect b/tests/pp/11.expect new file mode 100644 index 00000000..6b9806c6 --- /dev/null +++ b/tests/pp/11.expect @@ -0,0 +1,15 @@ +a +a b +a b c +a b c d +x a b c d y +x a b c y +x a b y +x a y +x y +x A,B,C y +x A,B y +x A y +x y +__attribute__((__noreturn__)) +1 2 diff --git a/tests/pp/Makefile b/tests/pp/Makefile new file mode 100644 index 00000000..c656f9a8 --- /dev/null +++ b/tests/pp/Makefile @@ -0,0 +1,35 @@ +# +# credits: 01..13.c from the pcc cpp-tests suite +# + +TCC = ../../tcc +TESTS = $(patsubst %.c,%.test,$(wildcard *.c)) + +all test : $(TESTS) + +%.test: %.c %.expect + @echo PPTest $* ... + @$(TCC) -E -P $< >$*.output 2>&1 ; \ + diff -Nu -b -B -I "^#" $(EXTRA_DIFF_OPTS) $*.expect $*.output \ + && rm -f $*.output + +# automatically generate .expect files with gcc: +%.expect : + gcc -E -P $*.c >$*.expect 2>&1 + +# tell make not to delete +.PRECIOUS: %.expect + +clean: + rm -vf *.output + +# 02.test : EXTRA_DIFF_OPTS = -w +# 03.test : EXTRA_DIFF_OPTS = -w +# 04.test : EXTRA_DIFF_OPTS = -w +# 10.test : EXTRA_DIFF_OPTS = -w + +# diff options: +# -b ighore space changes +# -w ighore all whitespace +# -B ignore blank lines +# -I ignore lines matching RE diff --git a/tests/tcctest.c b/tests/tcctest.c index 49fa587f..a8225004 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -316,6 +316,7 @@ void macro_test(void) printf("__LINE__=%d __FILE__=%s\n", __LINE__, __FILE__); +#if 0 #line 200 printf("__LINE__=%d __FILE__=%s\n", __LINE__, __FILE__); @@ -323,6 +324,7 @@ void macro_test(void) printf("__LINE__=%d __FILE__=%s\n", __LINE__, __FILE__); #line 227 "tcctest.c" +#endif /* not strictly preprocessor, but we test it there */ #ifdef C99_MACROS diff --git a/tests/tests2/65_macro_concat_start.c b/tests/tests2/65_macro_concat_start.c deleted file mode 100644 index d63d67a3..00000000 --- a/tests/tests2/65_macro_concat_start.c +++ /dev/null @@ -1,2 +0,0 @@ -#define paste(A,B) ##A B -paste(x,y) diff --git a/tests/tests2/65_macro_concat_start.expect b/tests/tests2/65_macro_concat_start.expect deleted file mode 100644 index 88ed6c50..00000000 --- a/tests/tests2/65_macro_concat_start.expect +++ /dev/null @@ -1 +0,0 @@ -65_macro_concat_start.c:1: error: '##' invalid at start of macro diff --git a/tests/tests2/66_macro_concat_end.c b/tests/tests2/66_macro_concat_end.c deleted file mode 100644 index 55dcaefb..00000000 --- a/tests/tests2/66_macro_concat_end.c +++ /dev/null @@ -1,2 +0,0 @@ -#define paste(A,B) A B## -paste(x,y) diff --git a/tests/tests2/66_macro_concat_end.expect b/tests/tests2/66_macro_concat_end.expect deleted file mode 100644 index 224e5c92..00000000 --- a/tests/tests2/66_macro_concat_end.expect +++ /dev/null @@ -1 +0,0 @@ -66_macro_concat_end.c:2: error: '##' invalid at end of macro diff --git a/tests/tests2/68_macro_param_list_err_1.c b/tests/tests2/68_macro_param_list_err_1.c deleted file mode 100644 index fd08dc76..00000000 --- a/tests/tests2/68_macro_param_list_err_1.c +++ /dev/null @@ -1,9 +0,0 @@ -#include -#define hexCh(c (c >= 10 ? 'a' + c - 10 : '0' + c) - -int main(void) -{ - int c = 0xa; - printf("hex: %c\n", hexCh(c)); - return 0; -} diff --git a/tests/tests2/68_macro_param_list_err_1.expect b/tests/tests2/68_macro_param_list_err_1.expect deleted file mode 100644 index 3246640d..00000000 --- a/tests/tests2/68_macro_param_list_err_1.expect +++ /dev/null @@ -1 +0,0 @@ -68_macro_param_list_err_1.c:2: error: '(' may not appear in parameter list diff --git a/tests/tests2/69_macro_param_list_err_2.c b/tests/tests2/69_macro_param_list_err_2.c deleted file mode 100644 index 06f0b418..00000000 --- a/tests/tests2/69_macro_param_list_err_2.c +++ /dev/null @@ -1,9 +0,0 @@ -#include -#define hexCh(c/3) (c >= 10 ? 'a' + c - 10 : '0' + c) - -int main(void) -{ - int c = 0xa; - printf("hex: %c\n", hexCh(c)); - return 0; -} diff --git a/tests/tests2/69_macro_param_list_err_2.expect b/tests/tests2/69_macro_param_list_err_2.expect deleted file mode 100644 index 6d6b9d1b..00000000 --- a/tests/tests2/69_macro_param_list_err_2.expect +++ /dev/null @@ -1 +0,0 @@ -69_macro_param_list_err_2.c:2: error: '/' may not appear in parameter list diff --git a/tests/tests2/72_long_long_constant.c b/tests/tests2/72_long_long_constant.c index a698fd62..66082133 100644 --- a/tests/tests2/72_long_long_constant.c +++ b/tests/tests2/72_long_long_constant.c @@ -13,5 +13,7 @@ int main() printf("Error: 2147483647 < 0\n"); return 2; } + else + printf("long long constant test ok.\n"); return 0; } diff --git a/tests/tests2/72_long_long_constant.expect b/tests/tests2/72_long_long_constant.expect new file mode 100644 index 00000000..dda9e660 --- /dev/null +++ b/tests/tests2/72_long_long_constant.expect @@ -0,0 +1 @@ +long long constant test ok. diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile dissimilarity index 63% index a4416742..254fa5ca 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -1,143 +1,76 @@ -TOP = ../.. -include $(TOP)/Makefile - -# clear CFLAGS and LDFLAGS -CFLAGS := -LDFLAGS := - -ifdef CONFIG_WIN32 - TCCFLAGS = -B$(top_srcdir)/win32 -I$(top_srcdir)/include -L$(TOP) -else - TCCFLAGS = -B$(TOP) -I$(top_srcdir)/include -lm -endif - -ifeq ($(TARGETOS),Darwin) - CFLAGS += -Wl,-flat_namespace,-undefined,warning - TCCFLAGS += -D_ANSI_SOURCE - export MACOSX_DEPLOYMENT_TARGET:=10.2 -endif - -TCC = $(TOP)/tcc $(TCCFLAGS) - -TESTS = \ - 00_assignment.test \ - 01_comment.test \ - 02_printf.test \ - 03_struct.test \ - 04_for.test \ - 05_array.test \ - 06_case.test \ - 07_function.test \ - 08_while.test \ - 09_do_while.test \ - 10_pointer.test \ - 11_precedence.test \ - 12_hashdefine.test \ - 13_integer_literals.test \ - 14_if.test \ - 15_recursion.test \ - 16_nesting.test \ - 17_enum.test \ - 18_include.test \ - 19_pointer_arithmetic.test \ - 20_pointer_comparison.test \ - 21_char_array.test \ - 22_floating_point.test \ - 23_type_coercion.test \ - 24_math_library.test \ - 25_quicksort.test \ - 26_character_constants.test \ - 27_sizeof.test \ - 28_strings.test \ - 29_array_address.test \ - 30_hanoi.test \ - 31_args.test \ - 32_led.test \ - 33_ternary_op.test \ - 34_array_assignment.test \ - 35_sizeof.test \ - 36_array_initialisers.test \ - 37_sprintf.test \ - 38_multiple_array_index.test \ - 39_typedef.test \ - 40_stdio.test \ - 41_hashif.test \ - 42_function_pointer.test \ - 43_void_param.test \ - 44_scoped_declarations.test \ - 45_empty_for.test \ - 46_grep.test \ - 47_switch_return.test \ - 48_nested_break.test \ - 49_bracket_evaluation.test \ - 50_logical_second_arg.test \ - 51_static.test \ - 52_unnamed_enum.test \ - 54_goto.test \ - 55_lshift_type.test \ - 56_btype_excess-1.test \ - 57_btype_excess-2.test \ - 58_function_redefinition.test \ - 59_function_array.test \ - 60_enum_redefinition.test \ - 61_undefined_enum.test \ - 62_enumerator_redefinition.test \ - 63_local_enumerator_redefinition.test \ - 64_macro_nesting.test \ - 65_macro_concat_start.test \ - 66_macro_concat_end.test \ - 67_macro_concat.test \ - 68_macro_param_list_err_1.test \ - 69_macro_param_list_err_2.test \ - 70_floating_point_literals.test \ - 71_macro_empty_arg.test \ - 72_long_long_constant.test \ - 73_arm64.test \ - 74_nocode_wanted.test \ - 75_array_in_struct_init.test \ - 76_dollars_in_identifiers.test \ - 77_push_pop_macro.test \ - 78_vla_label.test \ - 79_vla_continue.test - -# 34_array_assignment.test -- array assignment is not in C standard - -SKIP = 34_array_assignment.test - -# some tests do not pass on all platforms, remove them for now -ifeq ($(CONFIG_arm_eabi),yes) # not ARM soft-float - SKIP += 22_floating_point.test -endif -ifeq ($(TARGETOS),Darwin) - SKIP += 40_stdio.test -endif -ifdef CONFIG_WIN32 - SKIP += 24_math_library.test # don't have round() - SKIP += 28_strings.test # don't have r/index() / strings.h -endif -ifeq ($(ARCH),x86-64) - SKIP += 73_arm64.test -endif - -# Some tests might need arguments -ARGS = -31_args.test : ARGS = arg1 arg2 arg3 arg4 arg5 -46_grep.test : ARGS = '[^* ]*[:a:d: ]+\:\*-/: $$' 46_grep.c - -# Some tests might need different flags -FLAGS = -76_dollars_in_identifiers.test : FLAGS = -fdollars-in-identifiers - -all test: $(filter-out $(SKIP),$(TESTS)) - -%.test: %.c - @echo Test: $*... - - @$(TCC) -run $(FLAGS) $< $(ARGS) 2>&1 | grep -v 'warning: soft float ABI currently not supported: default to softfp' >$*.output || true - @diff -Nbu $*.expect $*.output && rm -f $*.output - - @($(TCC) $(FLAGS) $< -o $*.exe && ./$*.exe $(ARGS)) 2>&1 | grep -v 'warning: soft float ABI currently not supported: default to softfp' >$*.output2 || true - @diff -Nbu $*.expect $*.output2 && rm -f $*.output2 $*.exe - -clean: - rm -vf fred.txt *.output* *.exe +TOP = ../.. +include $(TOP)/Makefile + +# clear CFLAGS and LDFLAGS +CFLAGS := +LDFLAGS := + +ifdef CONFIG_WIN32 + TCCFLAGS = -B$(top_srcdir)/win32 -I$(top_srcdir)/include -L$(TOP) +else + TCCFLAGS = -B$(TOP) -I$(top_srcdir)/include -lm +endif + +ifeq ($(TARGETOS),Darwin) + CFLAGS += -Wl,-flat_namespace,-undefined,warning + TCCFLAGS += -D_ANSI_SOURCE + export MACOSX_DEPLOYMENT_TARGET:=10.2 +endif + +TCC = $(TOP)/tcc $(TCCFLAGS) + +TESTS = $(patsubst %.c,%.test,$(wildcard *.c)) + +# 34_array_assignment.test -- array assignment is not in C standard +SKIP = 34_array_assignment.test + +# some tests do not pass on all platforms, remove them for now +ifeq ($(CONFIG_arm_eabi),yes) # not ARM soft-float + SKIP += 22_floating_point.test +endif +ifeq ($(TARGETOS),Darwin) + SKIP += 40_stdio.test +endif +ifdef CONFIG_WIN32 + SKIP += 24_math_library.test # don't have round() + SKIP += 28_strings.test # don't have r/index() / strings.h +endif +ifeq ($(ARCH),x86-64) + SKIP += 73_arm64.test +endif + +# Some tests might need arguments +ARGS = +31_args.test : ARGS = arg1 arg2 arg3 arg4 arg5 +46_grep.test : ARGS = '[^* ]*[:a:d: ]+\:\*-/: $$' 46_grep.c + +# Some tests might need different flags +FLAGS = +76_dollars_in_identifiers.test : FLAGS = -fdollars-in-identifiers + +# Filter some always-warning +FILTER = +ifeq (-$(findstring arm,$(ARCH))-,-arm-) +FILTER = 2>&1 | grep -v 'warning: soft float ABI currently not supported' +endif + +all test: $(filter-out $(SKIP),$(TESTS)) + +%.test: %.c %.expect + @echo Test: $*... +# test -run + @$(TCC) $(FLAGS) -run $< $(ARGS) $(FILTER) >$*.output 2>&1 || true + @diff -Nbu $*.expect $*.output && rm -f $*.output +# test exe (disabled for speed) +# @($(TCC) $(FLAGS) $< -o $*.exe && ./$*.exe $(ARGS)) $(FiLTER) >$*.output2 2>&1 ; \ +# diff -Nbu $*.expect $*.output2 && rm -f $*.output2 $*.exe + +# automatically generate .expect files with gcc: +%.expect : + (gcc $*.c -o a.exe && ./a.exe $(ARGS)) >$*.expect 2>&1; rm -f a.exe + +# tell make not to delete +.PRECIOUS: %.expect + +clean: + rm -vf fred.txt *.output a.exe diff --git a/x86_64-gen.c b/x86_64-gen.c index 8495737a..2d69b75c 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -1254,12 +1254,12 @@ void gfunc_call(int nb_args) in on the stack and swap it back to its original position if it is a register. */ SValue tmp = vtop[0]; + int arg_stored = 1; + vtop[0] = vtop[-i]; vtop[-i] = tmp; - mode = classify_x86_64_arg(&vtop->type, NULL, &size, &align, ®_count); - int arg_stored = 1; switch (vtop->type.t & VT_BTYPE) { case VT_STRUCT: if (mode == x86_64_mode_sse) { @@ -1413,9 +1413,10 @@ void gfunc_call(int nb_args) } else if (mode == x86_64_mode_integer) { /* simple type */ /* XXX: implicit cast ? */ + int d; gen_reg -= reg_count; r = gv(RC_INT); - int d = arg_prepare_reg(gen_reg); + d = arg_prepare_reg(gen_reg); orex(1,d,r,0x89); /* mov */ o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); if (reg_count == 2) { @@ -2225,7 +2226,6 @@ ST_FUNC void gen_vla_sp_restore(int addr) { /* Subtract from the stack pointer, and push the resulting value onto the stack */ ST_FUNC void gen_vla_alloc(CType *type, int align) { - int r; #ifdef TCC_TARGET_PE /* alloca does more than just adjust %rsp on Windows */ vpush_global_sym(&func_old_type, TOK_alloca); @@ -2233,6 +2233,7 @@ ST_FUNC void gen_vla_alloc(CType *type, int align) { gfunc_call(1); vset(type, REG_IRET, 0); #else + int r; r = gv(RC_INT); /* allocation size */ /* sub r,%rsp */ o(0x2b48); -- 2.11.4.GIT