tcc-65816/tcc.c
2017-06-09 13:52:12 +02:00

10673 lines
307 KiB
C

/*
* TCC - Tiny C Compiler
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include "config.h"
#ifdef CONFIG_TCCBOOT
#include "tccboot.h"
#define CONFIG_TCC_STATIC
#else
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <setjmp.h>
#include <time.h>
#ifdef WIN32
#include <sys/timeb.h>
#endif
#ifndef WIN32
#include <sys/time.h>
#include <sys/ucontext.h>
#endif
#endif /* !CONFIG_TCCBOOT */
#include "elf.h"
#include "stab.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif
#include "libtcc.h"
/* parser debug */
//#define PARSE_DEBUG
/* preprocessor debug */
//#define PP_DEBUG
/* include file debug */
//#define INC_DEBUG
//#define MEM_DEBUG
/* assembler debug */
//#define ASM_DEBUG
/* target selection */
//#define TCC_TARGET_I386 /* i386 code generator */
//#define TCC_TARGET_ARM /* ARMv4 code generator */
//#define TCC_TARGET_C67 /* TMS320C67xx code generator */
/* default target is I386 */
#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \
!defined(TCC_TARGET_C67) && !defined(TCC_TARGET_816)
#define TCC_TARGET_I386
#endif
#if !defined(WIN32) && !defined(TCC_UCLIBC) && !defined(TCC_TARGET_ARM) && \
!defined(TCC_TARGET_C67) && !defined(TCC_TARGET_816)
#define CONFIG_TCC_BCHECK /* enable bound checking code */
#endif
#if defined(WIN32) && !defined(TCC_TARGET_PE)
#define CONFIG_TCC_STATIC
#endif
/* define it to include assembler support */
#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_C67) && !defined(TCC_TARGET_816)
#define CONFIG_TCC_ASM
#endif
/* object format selection */
#if defined(TCC_TARGET_C67)
#define TCC_TARGET_COFF
#endif
#define FALSE 0
#define false 0
#define TRUE 1
#define true 1
typedef int BOOL;
/* path to find crt1.o, crti.o and crtn.o. Only needed when generating
executables or dlls */
#define CONFIG_TCC_CRT_PREFIX "/usr/lib"
#define INCLUDE_STACK_SIZE 32
#define IFDEF_STACK_SIZE 64
#define VSTACK_SIZE 256
#define STRING_MAX_SIZE 1024
#define PACK_STACK_SIZE 8
#define TOK_HASH_SIZE 8192 /* must be a power of two */
#define TOK_ALLOC_INCR 512 /* must be a power of two */
#define TOK_MAX_SIZE 4 /* token max size in int unit when stored in string */
/* token symbol management */
typedef struct TokenSym {
struct TokenSym *hash_next;
struct Sym *sym_define; /* direct pointer to define */
struct Sym *sym_label; /* direct pointer to label */
struct Sym *sym_struct; /* direct pointer to structure */
struct Sym *sym_identifier; /* direct pointer to identifier */
int tok; /* token number */
int len;
char str[1];
} TokenSym;
typedef struct CString {
int size; /* size in bytes */
void *data; /* either 'char *' or 'int *' */
int size_allocated;
void *data_allocated; /* if non NULL, data has been malloced */
} CString;
/* type definition */
typedef struct CType {
int t;
struct Sym *ref;
int extra;
} CType;
/* constant value */
typedef union CValue {
long double ld;
double d;
float f;
int i;
unsigned int ui;
unsigned int ul; /* address (should be unsigned long on 64 bit cpu) */
long long ll;
unsigned long long ull;
struct CString *cstr;
void *ptr;
int tab[1];
} CValue;
/* value on stack */
typedef struct SValue {
CType type; /* type */
unsigned short r; /* register + flags */
unsigned short r2; /* second register, used for 'long long'
type. If not used, set to VT_CONST */
CValue c; /* constant, if VT_CONST */
struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST) */
} SValue;
/* symbol management */
typedef struct Sym {
int v; /* symbol token */
long r; /* associated register */
long c; /* associated number */
CType type; /* associated type */
struct Sym *next; /* next related symbol */
struct Sym *prev; /* prev symbol in stack */
struct Sym *prev_tok; /* previous symbol for this token */
} Sym;
/* section definition */
/* XXX: use directly ELF structure for parameters ? */
/* special flag to indicate that the section should not be linked to
the other ones */
#define SHF_PRIVATE 0x80000000
typedef struct Section {
unsigned long data_offset; /* current data offset */
unsigned char *data; /* section data */
unsigned long data_allocated; /* used for realloc() handling */
int sh_name; /* elf section name (only used during output) */
int sh_num; /* elf section number */
int sh_type; /* elf section type */
int sh_flags; /* elf section flags */
int sh_info; /* elf section info */
int sh_addralign; /* elf section alignment */
int sh_entsize; /* elf entry size */
unsigned long sh_size; /* section size (only used during output) */
unsigned long sh_addr; /* address at which the section is relocated */
unsigned long sh_offset; /* address at which the section is relocated */
int nb_hashed_syms; /* used to resize the hash table */
struct Section *link; /* link to another section */
struct Section *reloc; /* corresponding section for relocation, if any */
struct Section *hash; /* hash table for symbols */
struct Section *next;
char name[1]; /* section name */
} Section;
typedef struct DLLReference {
int level;
char name[1];
} DLLReference;
/* GNUC attribute definition */
typedef struct AttributeDef {
int aligned;
int packed;
Section *section;
unsigned char func_call; /* FUNC_CDECL, FUNC_STDCALL, FUNC_FASTCALLx */
unsigned char dllexport;
} AttributeDef;
#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */
#define SYM_FIELD 0x20000000 /* struct/union field symbol space */
#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */
/* stored in 'Sym.c' field */
#define FUNC_NEW 1 /* ansi function prototype */
#define FUNC_OLD 2 /* old function prototype */
#define FUNC_ELLIPSIS 3 /* ansi function prototype with ... */
/* stored in 'Sym.r' field */
#define FUNC_CDECL 0 /* standard c call */
#define FUNC_STDCALL 1 /* pascal c call */
#define FUNC_FASTCALL1 2 /* first param in %eax */
#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */
#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */
/* field 'Sym.t' for macros */
#define MACRO_OBJ 0 /* object like macro */
#define MACRO_FUNC 1 /* function like macro */
/* field 'Sym.r' for C labels */
#define LABEL_DEFINED 0 /* label is defined */
#define LABEL_FORWARD 1 /* label is forward defined */
#define LABEL_DECLARED 2 /* label is declared but never used */
/* type_decl() types */
#define TYPE_ABSTRACT 1 /* type without variable */
#define TYPE_DIRECT 2 /* type with variable */
#define IO_BUF_SIZE 8192
typedef struct BufferedFile {
uint8_t *buf_ptr;
uint8_t *buf_end;
int fd;
int line_num; /* current line number - here to simplify code */
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 inc_type; /* type of include */
char inc_filename[512]; /* filename specified by the user */
char filename[1024]; /* current filename - here to simplify code */
unsigned char buffer[IO_BUF_SIZE + 1]; /* extra size for CH_EOB char */
} BufferedFile;
#define CH_EOB '\\' /* end of buffer or '\0' char in file */
#define CH_EOF (-1) /* end of file */
/* parsing state (used to save parser state to reparse part of the
source several times) */
typedef struct ParseState {
int *macro_ptr;
int line_num;
int tok;
CValue tokc;
} ParseState;
/* used to record tokens */
typedef struct TokenString {
int *str;
int len;
int allocated_len;
int last_line_num;
} TokenString;
/* include file cache, used to find files faster and also to eliminate
inclusion if the include file is protected by #ifndef ... #endif */
typedef struct CachedInclude {
int ifndef_macro;
int hash_next; /* -1 if none */
char type; /* '"' or '>' to give include type */
char filename[1]; /* path specified in #include */
} CachedInclude;
#define CACHED_INCLUDES_HASH_SIZE 512
/* parser */
static struct BufferedFile *file;
static int ch, tok;
static CValue tokc;
static CString tokcstr; /* current parsed string, if any */
/* additional informations about token */
static int tok_flags;
#define TOK_FLAG_BOL 0x0001 /* beginning of line before */
#define TOK_FLAG_BOF 0x0002 /* beginning of file before */
#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */
static int *macro_ptr, *macro_ptr_allocated;
static int *unget_saved_macro_ptr;
static int unget_saved_buffer[TOK_MAX_SIZE + 1];
static int unget_buffer_enabled;
static int parse_flags;
#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */
#define PARSE_FLAG_TOK_NUM 0x0002 /* return numbers instead of TOK_PPNUM */
#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a
token. line feed is also
returned at eof */
#define PARSE_FLAG_ASM_COMMENTS 0x0008 /* '#' can be used for line comment */
static Section *text_section, *data_section, *rodata_section, *bss_section; /* predefined sections */
static Section *cur_text_section; /* current section where function code is
generated */
#ifdef CONFIG_TCC_ASM
static Section *last_text_section; /* to handle .previous asm directive */
#endif
/* bound check related sections */
static Section *bounds_section; /* contains global data bound description */
static Section *lbounds_section; /* contains local data bound description */
/* symbol sections */
static Section *symtab_section, *strtab_section;
/* debug sections */
static Section *stab_section, *stabstr_section;
/* loc : local variable index
ind : output code index
rsym: return symbol
anon_sym: anonymous symbol index
*/
static int rsym, anon_sym, ind, loc;
/* expression generation modifiers */
static int const_wanted; /* true if constant wanted */
static int nocode_wanted; /* true if no code generation wanted for an expression */
static int global_expr; /* true if compound literals must be allocated
globally (used during initializers parsing */
static CType func_vt; /* current function return type (used by return
instruction) */
static int func_vc;
static int last_line_num, last_ind, func_ind; /* debug last line number and pc */
static int tok_ident;
static TokenSym **table_ident;
static TokenSym *hash_ident[TOK_HASH_SIZE];
static char token_buf[STRING_MAX_SIZE + 1];
static char *funcname;
static Sym *global_stack, *local_stack;
static Sym *define_stack;
static Sym *global_label_stack, *local_label_stack;
/* symbol allocator */
#define SYM_POOL_NB (8192 / sizeof(Sym))
static Sym *sym_free_first;
static SValue vstack[VSTACK_SIZE], *vtop;
/* some predefined types */
static CType char_pointer_type, func_old_type, int_type, ptr_type;
/* true if isid(c) || isnum(c) */
static unsigned char isidnum_table[256];
/* compile with debug symbol (and use them if error during execution) */
static int do_debug = 0;
/* compile with built-in memory and bounds checker */
static int do_bounds_check = 0;
/* display benchmark infos */
#if !defined(LIBTCC)
static int do_bench = 0;
#endif
static int total_lines;
static int total_bytes;
/* use GNU C extensions */
static int gnu_ext = 1;
/* use Tiny C extensions */
static int tcc_ext = 1;
/* max number of callers shown if error */
static int num_callers = 6;
/* XXX: get rid of this ASAP */
static struct TCCState *tcc_state;
/* give the path of the tcc libraries */
static const char *tcc_lib_path = CONFIG_TCCDIR;
struct TCCState {
int output_type;
BufferedFile **include_stack_ptr;
int *ifdef_stack_ptr;
/* include file handling */
char **include_paths;
int nb_include_paths;
char **sysinclude_paths;
int nb_sysinclude_paths;
CachedInclude **cached_includes;
int nb_cached_includes;
char **library_paths;
int nb_library_paths;
/* array of all loaded dlls (including those referenced by loaded
dlls) */
DLLReference **loaded_dlls;
int nb_loaded_dlls;
/* sections */
Section **sections;
int nb_sections; /* number of sections, including first dummy section */
/* got handling */
Section *got;
Section *plt;
unsigned long *got_offsets;
int nb_got_offsets;
/* give the correspondance from symtab indexes to dynsym indexes */
int *symtab_to_dynsym;
/* temporary dynamic symbol sections (for dll loading) */
Section *dynsymtab_section;
/* exported dynamic symbol section */
Section *dynsym;
int nostdinc; /* if true, no standard headers are added */
int nostdlib; /* if true, no standard libraries are added */
int nocommon; /* if true, do not use common symbols for .bss data */
/* if true, static linking is performed */
int static_link;
/* if true, all symbols are exported */
int rdynamic;
/* if true, only link in referenced objects from archive */
int alacarte_link;
/* address of text section */
unsigned long text_addr;
int has_text_addr;
/* output format, see TCC_OUTPUT_FORMAT_xxx */
int output_format;
/* C language options */
int char_is_unsigned;
int leading_underscore;
/* warning switches */
int warn_write_strings;
int warn_unsupported;
int warn_error;
int warn_none;
int warn_implicit_function_declaration;
/* error handling */
void *error_opaque;
void (*error_func)(void *opaque, const char *msg);
int error_set_jmp_enabled;
jmp_buf error_jmp_buf;
int nb_errors;
/* tiny assembler state */
Sym *asm_labels;
/* see include_stack_ptr */
BufferedFile *include_stack[INCLUDE_STACK_SIZE];
/* see ifdef_stack_ptr */
int ifdef_stack[IFDEF_STACK_SIZE];
/* see cached_includes */
int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE];
/* pack stack */
int pack_stack[PACK_STACK_SIZE];
int *pack_stack_ptr;
int optimize;
};
/* The current value can be: */
#define VT_VALMASK 0x00ff
#define VT_CONST 0x00f0 /* constant in vc
(must be first non register value) */
#define VT_LLOCAL 0x00f1 /* lvalue, offset on stack */
#define VT_LOCAL 0x00f2 /* offset on stack */
#define VT_CMP 0x00f3 /* the value is stored in processor flags (in vc) */
#define VT_JMP 0x00f4 /* value is the consequence of jmp true (even) */
#define VT_JMPI 0x00f5 /* value is the consequence of jmp false (odd) */
#define VT_LVAL 0x0100 /* var is an lvalue */
#define VT_SYM 0x0200 /* a symbol value is added */
#define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for
char/short stored in integer registers) */
#define VT_MUSTBOUND 0x0800 /* bound checking must be done before
dereferencing value */
#define VT_BOUNDED 0x8000 /* value is bounded. The address of the
bounding function call point is in vc */
#define VT_LVAL_BYTE 0x1000 /* lvalue is a byte */
#define VT_LVAL_SHORT 0x2000 /* lvalue is a short */
#define VT_LVAL_UNSIGNED 0x4000 /* lvalue is unsigned */
#define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)
/* types */
#define VT_INT 0 /* integer type */
#define VT_BYTE 1 /* signed byte type */
#define VT_SHORT 2 /* short type */
#define VT_VOID 3 /* void type */
#define VT_PTR 4 /* pointer */
#define VT_ENUM 5 /* enum definition */
#define VT_FUNC 6 /* function type */
#define VT_STRUCT 7 /* struct/union definition */
#define VT_FLOAT 8 /* IEEE float */
#define VT_DOUBLE 9 /* IEEE double */
#define VT_LDOUBLE 10 /* IEEE long double */
#define VT_BOOL 11 /* ISOC99 boolean type */
#define VT_LLONG 12 /* 64 bit integer */
#define VT_LONG 13 /* long integer (NEVER USED as type, only
during parsing) */
#define VT_BTYPE 0x000f /* mask for basic type */
#define VT_UNSIGNED 0x0010 /* unsigned type */
#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
#define VT_BITFIELD 0x0040 /* bitfield modifier */
#define VT_CONSTANT 0x0800 /* const modifier */
#define VT_VOLATILE 0x1000 /* volatile modifier */
#define VT_SIGNED 0x2000 /* signed type */
/* storage */
#define VT_EXTERN 0x00000080 /* extern definition */
#define VT_STATIC 0x00000100 /* static variable */
#define VT_TYPEDEF 0x00000200 /* typedef definition */
#define VT_INLINE 0x00000400 /* inline definition */
#define VT_STATICLOCAL 0x00004000
#define VT_STRUCT_SHIFT 16 /* shift for bitfield shift values */
/* type mask (except storage) */
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
#define VT_TYPE (~(VT_STORAGE) & ~(VT_STATICLOCAL))
/* token values */
/* warning: the following compare tokens depend on i386 asm code */
#define TOK_ULT 0x92
#define TOK_UGE 0x93
#define TOK_EQ 0x94
#define TOK_NE 0x95
#define TOK_ULE 0x96
#define TOK_UGT 0x97
#define TOK_LT 0x9c
#define TOK_GE 0x9d
#define TOK_LE 0x9e
#define TOK_GT 0x9f
#define TOK_LAND 0xa0
#define TOK_LOR 0xa1
#define TOK_DEC 0xa2
#define TOK_MID 0xa3 /* inc/dec, to void constant */
#define TOK_INC 0xa4
#define TOK_UDIV 0xb0 /* unsigned division */
#define TOK_UMOD 0xb1 /* unsigned modulo */
#define TOK_PDIV 0xb2 /* fast division with undefined rounding for pointers */
#define TOK_CINT 0xb3 /* number in tokc */
#define TOK_CCHAR 0xb4 /* char constant in tokc */
#define TOK_STR 0xb5 /* pointer to string in tokc */
#define TOK_TWOSHARPS 0xb6 /* ## preprocessing token */
#define TOK_LCHAR 0xb7
#define TOK_LSTR 0xb8
#define TOK_CFLOAT 0xb9 /* float constant */
#define TOK_LINENUM 0xba /* line number info */
#define TOK_CDOUBLE 0xc0 /* double constant */
#define TOK_CLDOUBLE 0xc1 /* long double constant */
#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_CUINT 0xc8 /* unsigned int constant */
#define TOK_CLLONG 0xc9 /* long long constant */
#define TOK_CULLONG 0xca /* unsigned long long constant */
#define TOK_ARROW 0xcb
#define TOK_DOTS 0xcc /* three dots */
#define TOK_SHR 0xcd /* unsigned shift right */
#define TOK_PPNUM 0xce /* preprocessor number */
#define TOK_SHL 0x01 /* shift left */
#define TOK_SAR 0x02 /* signed shift right */
/* assignement operators : normal operator or 0x80 */
#define TOK_A_MOD 0xa5
#define TOK_A_AND 0xa6
#define TOK_A_MUL 0xaa
#define TOK_A_ADD 0xab
#define TOK_A_SUB 0xad
#define TOK_A_DIV 0xaf
#define TOK_A_XOR 0xde
#define TOK_A_OR 0xfc
#define TOK_A_SHL 0x81
#define TOK_A_SAR 0x82
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#ifndef countof
#define countof(tab) (sizeof(tab) / sizeof((tab)[0]))
#endif
/* WARNING: the content of this string encodes token numbers */
static char tok_two_chars[] = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266";
#define TOK_EOF (-1) /* end of file */
#define TOK_LINEFEED 10 /* line feed */
/* all identificators and strings have token above that */
#define TOK_IDENT 256
/* only used for i386 asm opcodes definitions */
#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x)
#define DEF_BWL(x) \
DEF(TOK_ASM_ ## x ## b, #x "b") \
DEF(TOK_ASM_ ## x ## w, #x "w") \
DEF(TOK_ASM_ ## x ## l, #x "l") \
DEF(TOK_ASM_ ## x, #x)
#define DEF_WL(x) \
DEF(TOK_ASM_ ## x ## w, #x "w") \
DEF(TOK_ASM_ ## x ## l, #x "l") \
DEF(TOK_ASM_ ## x, #x)
#define DEF_FP1(x) \
DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \
DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \
DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \
DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s")
#define DEF_FP(x) \
DEF(TOK_ASM_ ## f ## x, "f" #x ) \
DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \
DEF_FP1(x)
#define DEF_ASMTEST(x) \
DEF_ASM(x ## o) \
DEF_ASM(x ## no) \
DEF_ASM(x ## b) \
DEF_ASM(x ## c) \
DEF_ASM(x ## nae) \
DEF_ASM(x ## nb) \
DEF_ASM(x ## nc) \
DEF_ASM(x ## ae) \
DEF_ASM(x ## e) \
DEF_ASM(x ## z) \
DEF_ASM(x ## ne) \
DEF_ASM(x ## nz) \
DEF_ASM(x ## be) \
DEF_ASM(x ## na) \
DEF_ASM(x ## nbe) \
DEF_ASM(x ## a) \
DEF_ASM(x ## s) \
DEF_ASM(x ## ns) \
DEF_ASM(x ## p) \
DEF_ASM(x ## pe) \
DEF_ASM(x ## np) \
DEF_ASM(x ## po) \
DEF_ASM(x ## l) \
DEF_ASM(x ## nge) \
DEF_ASM(x ## nl) \
DEF_ASM(x ## ge) \
DEF_ASM(x ## le) \
DEF_ASM(x ## ng) \
DEF_ASM(x ## nle) \
DEF_ASM(x ## g)
#define TOK_ASM_int TOK_INT
enum tcc_token {
TOK_LAST = TOK_IDENT - 1,
#define DEF(id, str) id,
#include "tcctok.h"
#undef DEF
};
static const char tcc_keywords[] =
#define DEF(id, str) str "\0"
#include "tcctok.h"
#undef DEF
;
#define TOK_UIDENT TOK_DEFINE
#ifdef WIN32
int __stdcall GetModuleFileNameA(void *, char *, int);
void *__stdcall GetProcAddress(void *, const char *);
void *__stdcall GetModuleHandleA(const char *);
void *__stdcall LoadLibraryA(const char *);
int __stdcall FreeConsole(void);
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#ifndef __GNUC__
#define strtold (long double)strtod
#define strtof (float)strtod
#define strtoll (long long)strtol
#endif
#elif defined(TCC_UCLIBC) || defined(__FreeBSD__)
/* currently incorrect */
long double strtold(const char *nptr, char **endptr)
{
return (long double)strtod(nptr, endptr);
}
float strtof(const char *nptr, char **endptr)
{
return (float)strtod(nptr, endptr);
}
#else
/* XXX: need to define this to use them in non ISOC99 context */
extern float strtof (const char *__nptr, char **__endptr);
extern long double strtold (const char *__nptr, char **__endptr);
#endif
static char *pstrcpy(char *buf, int buf_size, const char *s);
static char *pstrcat(char *buf, int buf_size, const char *s);
static const char *tcc_basename(const char *name);
static void next(void);
static void next_nomacro(void);
static void parse_expr_type(CType *type);
static void expr_type(CType *type);
static void unary_type(CType *type);
static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
int case_reg, int is_expr);
static int expr_const(void);
static void expr_eq(void);
static void gexpr(void);
static void gen_inline_functions(void);
static void decl(int l);
static void decl_initializer(CType *type, Section *sec, unsigned long c,
int first, int size_only);
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
int has_init, int v, int scope);
int gv(int rc);
void gv2(int rc1, int rc2);
void move_reg(int r, int s);
void save_regs(int n);
void save_reg(int r);
void vpop(void);
void vswap(void);
void vdup(void);
int get_reg(int rc);
int get_reg_ex(int rc,int rc2);
struct macro_level {
struct macro_level *prev;
int *p;
};
static void macro_subst(TokenString *tok_str, Sym **nested_list,
const int *macro_str, struct macro_level **can_read_stream);
void gen_op(int op);
void force_charshort_cast(int t);
static void gen_cast(CType *type);
void vstore(void);
static Sym *sym_find(int v);
static Sym *sym_push(int v, CType *type, int r, int c);
/* type handling */
static int type_size(CType *type, int *a);
static inline CType *pointed_type(CType *type);
static int pointed_size(CType *type);
static int lvalue_type(int t);
static int parse_btype(CType *type, AttributeDef *ad);
static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
static int is_compatible_types(CType *type1, CType *type2);
int ieee_finite(double d);
void error(const char *fmt, ...);
void vpushi(int v);
void vrott(int n);
void vnrott(int n);
void lexpand_nr(void);
static void vpush_global_sym(CType *type, int v);
void vset(CType *type, int r, int v);
void type_to_str(char *buf, int buf_size,
CType *type, const char *varstr);
char *get_tok_str(int v, CValue *cv);
static Sym *get_sym_ref(CType *type, Section *sec,
unsigned long offset, unsigned long size);
static Sym *external_global_sym(int v, CType *type, int r);
/* section generation */
static void section_realloc(Section *sec, unsigned long new_size);
static void *section_ptr_add(Section *sec, unsigned long size);
static void put_extern_sym(Sym *sym, Section *section,
unsigned long value, unsigned long size);
static void greloc(Section *s, Sym *sym, unsigned long addr, int type);
static int put_elf_str(Section *s, const char *sym);
static int put_elf_sym(Section *s,
unsigned long value, unsigned long size,
int info, int other, int shndx, const char *name);
static int add_elf_sym(Section *s, unsigned long value, unsigned long size,
int info, int other, int sh_num, const char *name);
static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
int type, int symbol);
static void put_stabs(const char *str, int type, int other, int desc,
unsigned long value);
static void put_stabs_r(const char *str, int type, int other, int desc,
unsigned long value, Section *sec, int sym_index);
static void put_stabn(int type, int other, int desc, int value);
static void put_stabd(int type, int other, int desc);
static int tcc_add_dll(TCCState *s, const char *filename, int flags);
#define AFF_PRINT_ERROR 0x0001 /* print error if file not found */
#define AFF_REFERENCED_DLL 0x0002 /* load a referenced dll from another dll */
static int tcc_add_file_internal(TCCState *s, const char *filename, int flags);
/* tccasm.c */
#ifdef CONFIG_TCC_ASM
typedef struct ExprValue {
uint32_t v;
Sym *sym;
} ExprValue;
#define MAX_ASM_OPERANDS 30
typedef struct ASMOperand {
int id; /* GCC 3 optionnal identifier (0 if number only supported */
char *constraint;
char asm_str[16]; /* computed asm string for operand */
SValue *vt; /* C value of the expression */
int ref_index; /* if >= 0, gives reference to a output constraint */
int input_index; /* if >= 0, gives reference to an input constraint */
int priority; /* priority, used to assign registers */
int reg; /* if >= 0, register number used for this operand */
int is_llong; /* true if double register value */
int is_memory; /* true if memory operand */
int is_rw; /* for '+' modifier */
} ASMOperand;
static void asm_expr(TCCState *s1, ExprValue *pe);
static int asm_int_expr(TCCState *s1);
static int find_constraint(ASMOperand *operands, int nb_operands,
const char *name, const char **pp);
static int tcc_assemble(TCCState *s1, int do_preprocess);
#endif
static void asm_instr(void);
static void asm_global_instr(void);
/* true if float/double/long double type */
static inline int is_float(int t)
{
int bt;
bt = t & VT_BTYPE;
return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT;
}
void warning(const char *fmt, ...);
#ifdef TCC_TARGET_I386
#include "i386-gen.c"
#endif
#ifdef TCC_TARGET_ARM
#include "arm-gen.c"
#endif
#ifdef TCC_TARGET_C67
#include "c67-gen.c"
#endif
#ifdef TCC_TARGET_816
#include "816-gen.c"
#endif
/********************************************************/
/* we use our own 'finite' function to avoid potential problems with
non standard math libs */
/* XXX: endianness dependent */
int ieee_finite(double d)
{
int *p = (int *)&d;
return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
}
/* copy a string and truncate it. */
static char *pstrcpy(char *buf, int buf_size, const char *s)
{
char *q, *q_end;
int c;
if (buf_size > 0) {
q = buf;
q_end = buf + buf_size - 1;
while (q < q_end) {
c = *s++;
if (c == '\0')
break;
*q++ = c;
}
*q = '\0';
}
return buf;
}
/* strcat and truncate. */
static char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
static int strstart(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = p;
return 1;
}
/* memory management */
#ifdef MEM_DEBUG
int mem_cur_size;
int mem_max_size;
#endif
static inline void tcc_free(void *ptr)
{
#ifdef MEM_DEBUG
mem_cur_size -= malloc_usable_size(ptr);
#endif
free(ptr);
}
static void *tcc_malloc(unsigned long size)
{
void *ptr;
ptr = malloc(size);
if (!ptr && size)
error("memory full");
#ifdef MEM_DEBUG
mem_cur_size += malloc_usable_size(ptr);
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
#endif
return ptr;
}
static void *tcc_mallocz(unsigned long size)
{
void *ptr;
ptr = tcc_malloc(size);
memset(ptr, 0, size);
return ptr;
}
static inline void *tcc_realloc(void *ptr, unsigned long size)
{
void *ptr1;
#ifdef MEM_DEBUG
mem_cur_size -= malloc_usable_size(ptr);
#endif
ptr1 = realloc(ptr, size);
#ifdef MEM_DEBUG
/* NOTE: count not correct if alloc error, but not critical */
mem_cur_size += malloc_usable_size(ptr1);
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
#endif
return ptr1;
}
static char *tcc_strdup(const char *str)
{
char *ptr;
ptr = tcc_malloc(strlen(str) + 1);
strcpy(ptr, str);
return ptr;
}
#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)
static void dynarray_add(void ***ptab, int *nb_ptr, void *data)
{
int nb, nb_alloc;
void **pp;
nb = *nb_ptr;
pp = *ptab;
/* every power of two we double array size */
if ((nb & (nb - 1)) == 0) {
if (!nb)
nb_alloc = 1;
else
nb_alloc = nb * 2;
pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
if (!pp)
error("memory full");
*ptab = pp;
}
pp[nb++] = data;
*nb_ptr = nb;
}
/* symbol allocator */
static Sym *__sym_malloc(void)
{
Sym *sym_pool, *sym, *last_sym;
int i;
sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym));
last_sym = sym_free_first;
sym = sym_pool;
for(i = 0; i < SYM_POOL_NB; i++) {
sym->next = last_sym;
last_sym = sym;
sym++;
}
sym_free_first = last_sym;
return last_sym;
}
static inline Sym *sym_malloc(void)
{
Sym *sym;
sym = sym_free_first;
if (!sym)
sym = __sym_malloc();
sym_free_first = sym->next;
return sym;
}
static inline void sym_free(Sym *sym)
{
sym->next = sym_free_first;
sym_free_first = sym;
}
Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
{
Section *sec;
sec = tcc_mallocz(sizeof(Section) + strlen(name));
strcpy(sec->name, name);
sec->sh_type = sh_type;
sec->sh_flags = sh_flags;
switch(sh_type) {
case SHT_HASH:
case SHT_REL:
case SHT_DYNSYM:
case SHT_SYMTAB:
case SHT_DYNAMIC:
sec->sh_addralign = 4;
break;
case SHT_STRTAB:
sec->sh_addralign = 1;
break;
default:
sec->sh_addralign = 32; /* default conservative alignment */
break;
}
/* only add section if not private */
if (!(sh_flags & SHF_PRIVATE)) {
sec->sh_num = s1->nb_sections;
dynarray_add((void ***)&s1->sections, &s1->nb_sections, sec);
}
return sec;
}
static void free_section(Section *s)
{
tcc_free(s->data);
tcc_free(s);
}
/* realloc section and set its content to zero */
static void section_realloc(Section *sec, unsigned long new_size)
{
unsigned long size;
unsigned char *data;
size = sec->data_allocated;
if (size == 0)
size = 1;
while (size < new_size)
size = size * 2;
data = tcc_realloc(sec->data, size);
if (!data)
error("memory full");
memset(data + sec->data_allocated, 0, size - sec->data_allocated);
sec->data = data;
sec->data_allocated = size;
}
/* reserve at least 'size' bytes in section 'sec' from
sec->data_offset. */
static void *section_ptr_add(Section *sec, unsigned long size)
{
unsigned long offset, offset1;
offset = sec->data_offset;
offset1 = offset + size;
//fprintf(stderr,"section_ptr_add sec %s data %p size %ld offset %ld offset1 %ld allocd %ld\n", sec->name, sec->data, size,offset,offset1,sec->data_allocated);
if (offset1 > sec->data_allocated)
section_realloc(sec, offset1);
sec->data_offset = offset1;
return sec->data + offset;
}
/* return a reference to a section, and create it if it does not
exists */
Section *find_section(TCCState *s1, const char *name)
{
Section *sec;
int i;
for(i = 1; i < s1->nb_sections; i++) {
sec = s1->sections[i];
if (!strcmp(name, sec->name))
return sec;
}
/* sections are created as PROGBITS */
return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
}
#define SECTION_ABS ((void *)1)
/* update sym->c so that it points to an external symbol in section
'section' with value 'value' */
static void put_extern_sym2(Sym *sym, Section *section,
unsigned long value, unsigned long size,
int can_add_underscore)
{
int sym_type, sym_bind, sh_num, info;
Elf32_Sym *esym;
const char *name;
char buf1[256];
if (section == NULL)
sh_num = SHN_UNDEF;
else if (section == SECTION_ABS)
sh_num = SHN_ABS;
else
sh_num = section->sh_num;
if (!sym->c) {
if ((sym->type.t & VT_BTYPE) == VT_FUNC)
sym_type = STT_FUNC;
else
sym_type = STT_OBJECT;
if (sym->type.t & VT_STATIC)
sym_bind = STB_LOCAL;
else
sym_bind = STB_GLOBAL;
name = get_tok_str(sym->v, NULL);
#ifdef CONFIG_TCC_BCHECK
if (do_bounds_check) {
char buf[32];
/* XXX: avoid doing that for statics ? */
/* if bound checking is activated, we change some function
names by adding the "__bound" prefix */
switch(sym->v) {
#if 0
/* XXX: we rely only on malloc hooks */
case TOK_malloc:
case TOK_free:
case TOK_realloc:
case TOK_memalign:
case TOK_calloc:
#endif
case TOK_memcpy:
case TOK_memmove:
case TOK_memset:
case TOK_strlen:
case TOK_strcpy:
strcpy(buf, "__bound_");
strcat(buf, name);
name = buf;
break;
}
}
#endif
if (tcc_state->leading_underscore && can_add_underscore) {
buf1[0] = '_';
pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
name = buf1;
}
if (sym->type.t & VT_STATIC /* && name[0] != 'L' && name[1] != '.' */) {
//fprintf(stderr,"verstaticung von %s (current_fn %s)\n", name,current_fn);
if ((sym->type.t & VT_STATICLOCAL) && current_fn[0] != 0 /*&& !((sym->type.t & VT_BTYPE) == VT_FUNC)*/)
sprintf(buf1, "%s_FUNC_%s_", static_prefix, current_fn);
else
strcpy(buf1, static_prefix);
strcat(buf1, name);
name = buf1;
}
info = ELF32_ST_INFO(sym_bind, sym_type);
sym->c = add_elf_sym(symtab_section, value, size, info, 0, sh_num, name);
} else {
esym = &((Elf32_Sym *)symtab_section->data)[sym->c];
esym->st_value = value;
esym->st_size = size;
esym->st_shndx = sh_num;
}
}
static void put_extern_sym(Sym *sym, Section *section,
unsigned long value, unsigned long size)
{
put_extern_sym2(sym, section, value, size, 1);
}
/* add a new relocation entry to symbol 'sym' in section 's' */
static void greloc(Section *s, Sym *sym, unsigned long offset, int type)
{
//fprintf(stderr,"greloc section %p offset %d type %d name %s const %d\n",s,offset,type,get_tok_str(sym->v, NULL),sym->c);
if (!sym->c)
put_extern_sym(sym, NULL, 0, 0);
/* now we can add ELF relocation info */
put_elf_reloc(symtab_section, s, offset, type, sym->c);
}
static inline int isid(int c)
{
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
c == '_';
}
static inline int isnum(int c)
{
return c >= '0' && c <= '9';
}
static inline int isoct(int c)
{
return c >= '0' && c <= '7';
}
static inline int toup(int c)
{
if (c >= 'a' && c <= 'z')
return c - 'a' + 'A';
else
return c;
}
static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
{
int len;
len = strlen(buf);
vsnprintf(buf + len, buf_size - len, fmt, ap);
}
static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
strcat_vprintf(buf, buf_size, fmt, ap);
va_end(ap);
}
void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
{
char buf[2048];
BufferedFile **f;
buf[0] = '\0';
if (file) {
for(f = s1->include_stack; f < s1->include_stack_ptr; f++)
strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
(*f)->filename, (*f)->line_num);
if (file->line_num > 0) {
strcat_printf(buf, sizeof(buf),
"%s:%d: ", file->filename, file->line_num);
} else {
strcat_printf(buf, sizeof(buf),
"%s: ", file->filename);
}
} else {
strcat_printf(buf, sizeof(buf),
"tcc: ");
}
if (is_warning)
strcat_printf(buf, sizeof(buf), "warning: ");
strcat_vprintf(buf, sizeof(buf), fmt, ap);
if (!s1->error_func) {
/* default case: stderr */
fprintf(stderr, "%s\n", buf);
} else {
s1->error_func(s1->error_opaque, buf);
}
if (!is_warning || s1->warn_error)
s1->nb_errors++;
}
#ifdef LIBTCC
void tcc_set_error_func(TCCState *s, void *error_opaque,
void (*error_func)(void *opaque, const char *msg))
{
s->error_opaque = error_opaque;
s->error_func = error_func;
}
#endif
/* error without aborting current compilation */
void error_noabort(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
va_start(ap, fmt);
error1(s1, 0, fmt, ap);
va_end(ap);
}
void error(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
va_start(ap, fmt);
error1(s1, 0, fmt, ap);
va_end(ap);
/* better than nothing: in some cases, we accept to handle errors */
if (s1->error_set_jmp_enabled) {
longjmp(s1->error_jmp_buf, 1);
} else {
/* XXX: eliminate this someday */
exit(1);
}
}
void expect(const char *msg)
{
error("%s expected", msg);
}
void warning(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
if (s1->warn_none)
return;
va_start(ap, fmt);
error1(s1, 1, fmt, ap);
va_end(ap);
}
void skip(int c)
{
if (tok != c)
error("'%c' expected", c);
next();
}
static void test_lvalue(void)
{
if (!(vtop->r & VT_LVAL))
expect("lvalue");
}
/* allocate a new token */
static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
{
TokenSym *ts, **ptable;
int i;
if (tok_ident >= SYM_FIRST_ANOM)
error("memory full");
/* expand token table if needed */
i = tok_ident - TOK_IDENT;
if ((i % TOK_ALLOC_INCR) == 0) {
ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *));
if (!ptable)
error("memory full");
table_ident = ptable;
}
ts = tcc_malloc(sizeof(TokenSym) + len);
table_ident[i] = ts;
ts->tok = tok_ident++;
ts->sym_define = NULL;
ts->sym_label = NULL;
ts->sym_struct = NULL;
ts->sym_identifier = NULL;
ts->len = len;
ts->hash_next = NULL;
memcpy(ts->str, str, len);
ts->str[len] = '\0';
*pts = ts;
return ts;
}
#define TOK_HASH_INIT 1
#define TOK_HASH_FUNC(h, c) ((h) * 263 + (c))
/* find a token and add it if not found */
static TokenSym *tok_alloc(const char *str, int len)
{
TokenSym *ts, **pts;
int i;
unsigned int h;
h = TOK_HASH_INIT;
for(i=0;i<len;i++)
h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]);
h &= (TOK_HASH_SIZE - 1);
pts = &hash_ident[h];
for(;;) {
ts = *pts;
if (!ts)
break;
if (ts->len == len && !memcmp(ts->str, str, len))
return ts;
pts = &(ts->hash_next);
}
return tok_alloc_new(pts, str, len);
}
/* CString handling */
static void cstr_realloc(CString *cstr, int new_size)
{
int size;
void *data;
size = cstr->size_allocated;
if (size == 0)
size = 8; /* no need to allocate a too small first string */
while (size < new_size)
size = size * 2;
data = tcc_realloc(cstr->data_allocated, size);
if (!data)
error("memory full");
cstr->data_allocated = data;
cstr->size_allocated = size;
cstr->data = data;
}
/* add a byte */
static inline void cstr_ccat(CString *cstr, int ch)
{
int size;
size = cstr->size + 1;
if (size > cstr->size_allocated)
cstr_realloc(cstr, size);
((unsigned char *)cstr->data)[size - 1] = ch;
cstr->size = size;
}
static void cstr_cat(CString *cstr, const char *str)
{
int c;
for(;;) {
c = *str;
if (c == '\0')
break;
cstr_ccat(cstr, c);
str++;
}
}
/* add a wide char */
static void cstr_wccat(CString *cstr, int ch)
{
int size;
size = cstr->size + sizeof(int);
if (size > cstr->size_allocated)
cstr_realloc(cstr, size);
*(int *)(((unsigned char *)cstr->data) + size - sizeof(int)) = ch;
cstr->size = size;
}
static void cstr_new(CString *cstr)
{
memset(cstr, 0, sizeof(CString));
}
/* free string and reset it to NULL */
static void cstr_free(CString *cstr)
{
tcc_free(cstr->data_allocated);
cstr_new(cstr);
}
#define cstr_reset(cstr) cstr_free(cstr)
/* XXX: unicode ? */
static void add_char(CString *cstr, int c)
{
if (c == '\'' || c == '\"' || c == '\\') {
/* XXX: could be more precise if char or string */
cstr_ccat(cstr, '\\');
}
if (c >= 32 && c <= 126) {
cstr_ccat(cstr, c);
} else {
cstr_ccat(cstr, '\\');
if (c == '\n') {
cstr_ccat(cstr, 'n');
} else {
cstr_ccat(cstr, '0' + ((c >> 6) & 7));
cstr_ccat(cstr, '0' + ((c >> 3) & 7));
cstr_ccat(cstr, '0' + (c & 7));
}
}
}
/* XXX: buffer overflow */
/* XXX: float tokens */
char *get_tok_str(int v, CValue *cv)
{
static char buf[STRING_MAX_SIZE + 1];
static CString cstr_buf;
CString *cstr;
unsigned char *q;
char *p;
int i, len;
/* NOTE: to go faster, we give a fixed buffer for small strings */
cstr_reset(&cstr_buf);
cstr_buf.data = buf;
cstr_buf.size_allocated = sizeof(buf);
p = buf;
switch(v) {
case TOK_CINT:
case TOK_CUINT:
/* XXX: not quite exact, but only useful for testing */
sprintf(p, "%u", cv->ui);
break;
case TOK_CLLONG:
case TOK_CULLONG:
/* XXX: not quite exact, but only useful for testing */
sprintf(p, "%Lu", cv->ull);
break;
case TOK_CCHAR:
case TOK_LCHAR:
cstr_ccat(&cstr_buf, '\'');
add_char(&cstr_buf, cv->i);
cstr_ccat(&cstr_buf, '\'');
cstr_ccat(&cstr_buf, '\0');
break;
case TOK_PPNUM:
cstr = cv->cstr;
len = cstr->size - 1;
for(i=0;i<len;i++)
add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]);
cstr_ccat(&cstr_buf, '\0');
break;
case TOK_STR:
case TOK_LSTR:
cstr = cv->cstr;
cstr_ccat(&cstr_buf, '\"');
if (v == TOK_STR) {
len = cstr->size - 1;
for(i=0;i<len;i++)
add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]);
} else {
len = (cstr->size / sizeof(int)) - 1;
for(i=0;i<len;i++)
add_char(&cstr_buf, ((int *)cstr->data)[i]);
}
cstr_ccat(&cstr_buf, '\"');
cstr_ccat(&cstr_buf, '\0');
break;
case TOK_LT:
v = '<';
goto addv;
case TOK_GT:
v = '>';
goto addv;
case TOK_A_SHL:
return strcpy(p, "<<=");
case TOK_A_SAR:
return strcpy(p, ">>=");
default:
if (v < TOK_IDENT) {
/* search in two bytes table */
q = tok_two_chars;
while (*q) {
if (q[2] == v) {
*p++ = q[0];
*p++ = q[1];
*p = '\0';
return buf;
}
q += 3;
}
addv:
*p++ = v;
*p = '\0';
} else if (v < tok_ident) {
return table_ident[v - TOK_IDENT]->str;
} else if (v >= SYM_FIRST_ANOM) {
/* special name for anonymous symbol */
sprintf(p, "L.%d", v - SYM_FIRST_ANOM);
} else {
/* should never happen */
return NULL;
}
break;
}
return cstr_buf.data;
}
/* push, without hashing */
static Sym *sym_push2(Sym **ps, int v, int t, int c)
{
Sym *s;
s = sym_malloc();
s->v = v;
s->type.t = t;
s->c = c;
s->next = NULL;
/* add in stack */
s->prev = *ps;
*ps = s;
return s;
}
/* find a symbol and return its associated structure. 's' is the top
of the symbol stack */
static Sym *sym_find2(Sym *s, int v)
{
while (s) {
if (s->v == v)
return s;
s = s->prev;
}
return NULL;
}
/* structure lookup */
static inline Sym *struct_find(int v)
{
v -= TOK_IDENT;
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
return NULL;
return table_ident[v]->sym_struct;
}
/* find an identifier */
static inline Sym *sym_find(int v)
{
v -= TOK_IDENT;
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
return NULL;
return table_ident[v]->sym_identifier;
}
/* push a given symbol on the symbol stack */
static Sym *sym_push(int v, CType *type, int r, int c)
{
Sym *s, **ps;
TokenSym *ts;
if (local_stack)
ps = &local_stack;
else
ps = &global_stack;
s = sym_push2(ps, v, type->t, c);
s->type.ref = type->ref;
s->r = r;
/* don't record fields or anonymous symbols */
/* XXX: simplify */
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
/* record symbol in token array */
//fprintf(stderr,"table_ident index 0x%x\n",(v & ~(SYM_STRUCT)) - TOK_IDENT);
ts = table_ident[(v & ~(SYM_STRUCT)) - TOK_IDENT];
if (v & SYM_STRUCT)
ps = &ts->sym_struct;
else
ps = &ts->sym_identifier;
s->prev_tok = *ps;
*ps = s;
}
return s;
}
/* push a global identifier */
static Sym *global_identifier_push(int v, int t, int c)
{
Sym *s, **ps;
s = sym_push2(&global_stack, v, t, c);
/* don't record anonymous symbol */
if (v < SYM_FIRST_ANOM) {
//fprintf(stderr,"table_ident index 0x%x\n",v - TOK_IDENT);
ps = &table_ident[v - TOK_IDENT]->sym_identifier;
/* modify the top most local identifier, so that
sym_identifier will point to 's' when popped */
while (*ps != NULL)
ps = &(*ps)->prev_tok;
s->prev_tok = NULL;
*ps = s;
}
return s;
}
/* pop symbols until top reaches 'b' */
static void sym_pop(Sym **ptop, Sym *b)
{
Sym *s, *ss, **ps;
TokenSym *ts;
int v;
s = *ptop;
while(s != b) {
ss = s->prev;
v = s->v;
/* remove symbol in token array */
/* XXX: simplify */
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
if (v & SYM_STRUCT)
ps = &ts->sym_struct;
else
ps = &ts->sym_identifier;
*ps = s->prev_tok;
}
sym_free(s);
s = ss;
}
*ptop = b;
}
/* I/O layer */
BufferedFile *tcc_open(TCCState *s1, const char *filename)
{
int fd;
BufferedFile *bf;
fd = open(filename, O_RDONLY | O_BINARY);
if (fd < 0)
return NULL;
bf = tcc_malloc(sizeof(BufferedFile));
if (!bf) {
close(fd);
return NULL;
}
bf->fd = fd;
bf->buf_ptr = bf->buffer;
bf->buf_end = bf->buffer;
bf->buffer[0] = CH_EOB; /* put eob symbol */
pstrcpy(bf->filename, sizeof(bf->filename), filename);
bf->line_num = 1;
bf->ifndef_macro = 0;
bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
// printf("opening '%s'\n", filename);
return bf;
}
void tcc_close(BufferedFile *bf)
{
total_lines += bf->line_num;
close(bf->fd);
tcc_free(bf);
}
/* fill input buffer and peek next char */
static int tcc_peekc_slow(BufferedFile *bf)
{
int len;
/* only tries to read if really end of buffer */
if (bf->buf_ptr >= bf->buf_end) {
if (bf->fd != -1) {
#if defined(PARSE_DEBUG)
len = 8;
#else
len = IO_BUF_SIZE;
#endif
len = read(bf->fd, bf->buffer, len);
if (len < 0)
len = 0;
} else {
len = 0;
}
total_bytes += len;
bf->buf_ptr = bf->buffer;
bf->buf_end = bf->buffer + len;
*bf->buf_end = CH_EOB;
}
if (bf->buf_ptr < bf->buf_end) {
return bf->buf_ptr[0];
} else {
bf->buf_ptr = bf->buf_end;
return CH_EOF;
}
}
/* return the current character, handling end of block if necessary
(but not stray) */
static int handle_eob(void)
{
return tcc_peekc_slow(file);
}
/* read next char from current input file and handle end of input buffer */
static inline void inp(void)
{
ch = *(++(file->buf_ptr));
/* end of buffer/file handling */
if (ch == CH_EOB)
ch = handle_eob();
}
/* handle '\[\r]\n' */
static void handle_stray(void)
{
while (ch == '\\') {
inp();
if (ch == '\n') {
file->line_num++;
inp();
} else if (ch == '\r') {
inp();
if (ch != '\n')
goto fail;
file->line_num++;
inp();
} else {
fail:
error("stray '\\' in program");
}
}
}
/* skip the stray and handle the \\n case. Output an error if
incorrect char after the stray */
static int handle_stray1(uint8_t *p)
{
int c;
if (p >= file->buf_end) {
file->buf_ptr = p;
c = handle_eob();
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;
}
return c;
}
/* handle just the EOB case, but not stray */
#define PEEKC_EOB(c, p)\
{\
p++;\
c = *p;\
if (c == '\\') {\
file->buf_ptr = p;\
c = handle_eob();\
p = file->buf_ptr;\
}\
}
/* handle the complicated stray case */
#define PEEKC(c, p)\
{\
p++;\
c = *p;\
if (c == '\\') {\
c = handle_stray1(p);\
p = file->buf_ptr;\
}\
}
/* input with '\[\r]\n' handling. Note that this function cannot
handle other characters after '\', so you cannot call it inside
strings or comments */
static void minp(void)
{
inp();
if (ch == '\\')
handle_stray();
}
/* single line C++ comments */
static uint8_t *parse_line_comment(uint8_t *p)
{
int c;
p++;
for(;;) {
c = *p;
redo:
if (c == '\n' || c == CH_EOF) {
break;
} else if (c == '\\') {
file->buf_ptr = p;
c = handle_eob();
p = file->buf_ptr;
if (c == '\\') {
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
PEEKC_EOB(c, p);
} else if (c == '\r') {
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
PEEKC_EOB(c, p);
}
}
} else {
goto redo;
}
} else {
p++;
}
}
return p;
}
/* C comments */
static uint8_t *parse_comment(uint8_t *p)
{
int c;
p++;
for(;;) {
/* fast skip loop */
for(;;) {
c = *p;
if (c == '\n' || c == '*' || c == '\\')
break;
p++;
c = *p;
if (c == '\n' || c == '*' || c == '\\')
break;
p++;
}
/* now we can handle all the cases */
if (c == '\n') {
file->line_num++;
p++;
} else if (c == '*') {
p++;
for(;;) {
c = *p;
if (c == '*') {
p++;
} else if (c == '/') {
goto end_of_comment;
} else if (c == '\\') {
file->buf_ptr = p;
c = handle_eob();
p = file->buf_ptr;
if (c == '\\') {
/* skip '\[\r]\n', otherwise just skip the stray */
while (c == '\\') {
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
PEEKC_EOB(c, p);
} else if (c == '\r') {
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
PEEKC_EOB(c, p);
}
} else {
goto after_star;
}
}
}
} else {
break;
}
}
after_star: ;
} else {
/* stray, eob or eof */
file->buf_ptr = p;
c = handle_eob();
p = file->buf_ptr;
if (c == CH_EOF) {
error("unexpected end of file in comment");
} else if (c == '\\') {
p++;
}
}
}
end_of_comment:
p++;
return p;
}
#define cinp minp
/* space exlcuding newline */
static inline int is_space(int ch)
{
return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r';
}
static inline void skip_spaces(void)
{
while (is_space(ch))
cinp();
}
/* parse a string without interpreting escapes */
static uint8_t *parse_pp_string(uint8_t *p,
int sep, CString *str)
{
int c;
p++;
for(;;) {
c = *p;
if (c == sep) {
break;
} else if (c == '\\') {
file->buf_ptr = p;
c = handle_eob();
p = file->buf_ptr;
if (c == CH_EOF) {
unterminated_string:
/* XXX: indicate line number of start of string */
error("missing terminating %c character", sep);
} else if (c == '\\') {
/* escape : just skip \[\r]\n */
PEEKC_EOB(c, p);
if (c == '\n') {
file->line_num++;
p++;
} else if (c == '\r') {
PEEKC_EOB(c, p);
if (c != '\n')
expect("'\n' after '\r'");
file->line_num++;
p++;
} else if (c == CH_EOF) {
goto unterminated_string;
} else {
if (str) {
cstr_ccat(str, '\\');
cstr_ccat(str, c);
}
p++;
}
}
} else if (c == '\n') {
file->line_num++;
goto add_char;
} else if (c == '\r') {
PEEKC_EOB(c, p);
if (c != '\n') {
if (str)
cstr_ccat(str, '\r');
} else {
file->line_num++;
goto add_char;
}
} else {
add_char:
if (str)
cstr_ccat(str, c);
p++;
}
}
p++;
return p;
}
/* skip block of text until #else, #elif or #endif. skip also pairs of
#if/#endif */
void preprocess_skip(void)
{
int a, start_of_line, c;
uint8_t *p;
p = file->buf_ptr;
start_of_line = 1;
a = 0;
for(;;) {
redo_no_start:
c = *p;
switch(c) {
case ' ':
case '\t':
case '\f':
case '\v':
case '\r':
p++;
goto redo_no_start;
case '\n':
start_of_line = 1;
file->line_num++;
p++;
goto redo_no_start;
case '\\':
file->buf_ptr = p;
c = handle_eob();
if (c == CH_EOF) {
expect("#endif");
} else if (c == '\\') {
/* XXX: incorrect: should not give an error */
ch = file->buf_ptr[0];
handle_stray();
}
p = file->buf_ptr;
goto redo_no_start;
/* skip strings */
case '\"':
case '\'':
p = parse_pp_string(p, c, NULL);
break;
/* skip comments */
case '/':
file->buf_ptr = p;
ch = *p;
minp();
p = file->buf_ptr;
if (ch == '*') {
p = parse_comment(p);
} else if (ch == '/') {
p = parse_line_comment(p);
}
break;
case '#':
p++;
if (start_of_line) {
file->buf_ptr = p;
next_nomacro();
p = file->buf_ptr;
if (a == 0 &&
(tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF))
goto the_end;
if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF)
a++;
else if (tok == TOK_ENDIF)
a--;
}
break;
default:
p++;
break;
}
start_of_line = 0;
}
the_end: ;
file->buf_ptr = p;
}
/* ParseState handling */
/* XXX: currently, no include file info is stored. Thus, we cannot display
accurate messages if the function or data definition spans multiple
files */
/* save current parse state in 's' */
void save_parse_state(ParseState *s)
{
s->line_num = file->line_num;
s->macro_ptr = macro_ptr;
s->tok = tok;
s->tokc = tokc;
}
/* restore parse state from 's' */
void restore_parse_state(ParseState *s)
{
file->line_num = s->line_num;
macro_ptr = s->macro_ptr;
tok = s->tok;
tokc = s->tokc;
}
/* return the number of additional 'ints' necessary to store the
token */
static inline int tok_ext_size(int t)
{
switch(t) {
/* 4 bytes */
case TOK_CINT:
case TOK_CUINT:
case TOK_CCHAR:
case TOK_LCHAR:
case TOK_CFLOAT: // FIXME: is that correct?
#ifdef TCC_TARGET_816
case TOK_CDOUBLE:
case TOK_CLDOUBLE:
#endif
case TOK_LINENUM:
return 1;
case TOK_STR:
case TOK_LSTR:
case TOK_PPNUM:
error("unsupported token");
return 1;
#ifndef TCC_TARGET_816
case TOK_CLDOUBLE:
return LDOUBLE_SIZE / 4;
case TOK_CDOUBLE:
#endif
case TOK_CLLONG:
case TOK_CULLONG:
return 2;
default:
return 0;
}
}
/* token string handling */
static inline void tok_str_new(TokenString *s)
{
s->str = NULL;
s->len = 0;
s->allocated_len = 0;
s->last_line_num = -1;
}
static void tok_str_free(int *str)
{
tcc_free(str);
}
static int *tok_str_realloc(TokenString *s)
{
int *str, len;
if (s->allocated_len == 0) {
len = 8;
} else {
len = s->allocated_len * 2;
}
str = tcc_realloc(s->str, len * sizeof(int));
if (!str)
error("memory full");
s->allocated_len = len;
s->str = str;
return str;
}
static void tok_str_add(TokenString *s, int t)
{
int len, *str;
len = s->len;
str = s->str;
if (len >= s->allocated_len)
str = tok_str_realloc(s);
str[len++] = t;
s->len = len;
}
static void tok_str_add2(TokenString *s, int t, CValue *cv)
{
int len, *str;
len = s->len;
str = s->str;
/* allocate space for worst case */
if (len + TOK_MAX_SIZE > s->allocated_len)
str = tok_str_realloc(s);
str[len++] = t;
switch(t) {
case TOK_CINT:
case TOK_CUINT:
case TOK_CCHAR:
case TOK_LCHAR:
case TOK_CFLOAT:
#ifdef TCC_TARGET_816
case TOK_CDOUBLE:
#endif
case TOK_LINENUM:
str[len++] = cv->tab[0];
break;
case TOK_PPNUM:
case TOK_STR:
case TOK_LSTR:
{
int nb_words;
CString *cstr;
nb_words = (sizeof(CString) + cv->cstr->size + 3) >> 2;
while ((len + nb_words) > s->allocated_len)
str = tok_str_realloc(s);
cstr = (CString *)(str + len);
cstr->data = NULL;
cstr->size = cv->cstr->size;
cstr->data_allocated = NULL;
cstr->size_allocated = cstr->size;
memcpy((char *)cstr + sizeof(CString),
cv->cstr->data, cstr->size);
len += nb_words;
}
break;
#ifndef TCC_TARGET_816
case TOK_CDOUBLE:
#endif
case TOK_CLLONG:
case TOK_CULLONG:
#if LDOUBLE_SIZE == 8
case TOK_CLDOUBLE:
#endif
str[len++] = cv->tab[0];
str[len++] = cv->tab[1];
break;
#if LDOUBLE_SIZE == 12
case TOK_CLDOUBLE:
str[len++] = cv->tab[0];
str[len++] = cv->tab[1];
str[len++] = cv->tab[2];
#elif LDOUBLE_SIZE != 8
#error add long double size support
#endif
break;
default:
break;
}
s->len = len;
}
/* add the current parse token in token string 's' */
static void tok_str_add_tok(TokenString *s)
{
CValue cval;
/* save line number info */
if (file->line_num != s->last_line_num) {
s->last_line_num = file->line_num;
cval.i = s->last_line_num;
tok_str_add2(s, TOK_LINENUM, &cval);
}
tok_str_add2(s, tok, &tokc);
}
#if LDOUBLE_SIZE == 12
#define LDOUBLE_GET(p, cv) \
cv.tab[0] = p[0]; \
cv.tab[1] = p[1]; \
cv.tab[2] = p[2];
#elif LDOUBLE_SIZE == 8
#define LDOUBLE_GET(p, cv) \
cv.tab[0] = p[0]; \
cv.tab[1] = p[1];
#else
#error add long double size support
#endif
/* get a token from an integer array and increment pointer
accordingly. we code it as a macro to avoid pointer aliasing. */
#define TOK_GET(t, p, cv) \
{ \
t = *p++; \
switch(t) { \
case TOK_CINT: \
case TOK_CUINT: \
case TOK_CCHAR: \
case TOK_LCHAR: \
case TOK_CFLOAT: /* this is correct (compiler ints array) */ \
case TOK_CDOUBLE: /* FIXME: make portable */ \
case TOK_LINENUM: \
cv.tab[0] = *p++; \
break; \
case TOK_STR: \
case TOK_LSTR: \
case TOK_PPNUM: \
cv.cstr = (CString *)p; \
cv.cstr->data = (char *)p + sizeof(CString);\
p += (sizeof(CString) + cv.cstr->size + 3) >> 2;\
break; \
/* case TOK_CDOUBLE: */ \
case TOK_CLLONG: \
case TOK_CULLONG: \
cv.tab[0] = p[0]; \
cv.tab[1] = p[1]; \
p += 2; \
break; \
case TOK_CLDOUBLE: \
LDOUBLE_GET(p, cv); \
p += LDOUBLE_SIZE / 4; \
break; \
default: \
break; \
} \
}
/* defines handling */
static inline void define_push(int v, int macro_type, int *str, Sym *first_arg)
{
Sym *s;
s = sym_push2(&define_stack, v, macro_type, (long)str);
s->next = first_arg;
table_ident[v - TOK_IDENT]->sym_define = s;
}
/* undefined a define symbol. Its name is just set to zero */
static void define_undef(Sym *s)
{
int v;
v = s->v;
if (v >= TOK_IDENT && v < tok_ident)
table_ident[v - TOK_IDENT]->sym_define = NULL;
s->v = 0;
}
static inline Sym *define_find(int v)
{
v -= TOK_IDENT;
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
return NULL;
return table_ident[v]->sym_define;
}
/* free define stack until top reaches 'b' */
static void free_defines(Sym *b)
{
Sym *top, *top1;
int v;
top = define_stack;
while (top != b) {
top1 = top->prev;
/* do not free args or predefined defines */
if (top->c)
tok_str_free((int *)top->c);
v = top->v;
if (v >= TOK_IDENT && v < tok_ident)
table_ident[v - TOK_IDENT]->sym_define = NULL;
sym_free(top);
top = top1;
}
define_stack = b;
}
/* label lookup */
static Sym *label_find(int v)
{
v -= TOK_IDENT;
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
return NULL;
return table_ident[v]->sym_label;
}
static Sym *label_push(Sym **ptop, int v, int flags)
{
Sym *s, **ps;
s = sym_push2(ptop, v, 0, 0);
s->r = flags;
ps = &table_ident[v - TOK_IDENT]->sym_label;
if (ptop == &global_label_stack) {
/* modify the top most local identifier, so that
sym_identifier will point to 's' when popped */
while (*ps != NULL)
ps = &(*ps)->prev_tok;
}
s->prev_tok = *ps;
*ps = s;
return s;
}
/* pop labels until element last is reached. Look if any labels are
undefined. Define symbols if '&&label' was used. */
static void label_pop(Sym **ptop, Sym *slast)
{
Sym *s, *s1;
for(s = *ptop; s != slast; s = s1) {
s1 = s->prev;
if (s->r == LABEL_DECLARED) {
warning("label '%s' declared but not used", get_tok_str(s->v, NULL));
} else if (s->r == LABEL_FORWARD) {
error("label '%s' used but not defined",
get_tok_str(s->v, NULL));
} else {
if (s->c) {
/* define corresponding symbol. A size of
1 is put. */
put_extern_sym(s, cur_text_section, (long)s->next, 1);
}
}
/* remove label */
table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok;
sym_free(s);
}
*ptop = slast;
}
/* eval an expression for #if/#elif */
static int expr_preprocess(void)
{
int c, t;
TokenString str;
tok_str_new(&str);
while (tok != TOK_LINEFEED && tok != TOK_EOF) {
next(); /* do macro subst */
if (tok == TOK_DEFINED) {
next_nomacro();
t = tok;
if (t == '(')
next_nomacro();
c = define_find(tok) != 0;
if (t == '(')
next_nomacro();
tok = TOK_CINT;
tokc.i = c;
} else if (tok >= TOK_IDENT) {
/* if undefined macro */
tok = TOK_CINT;
tokc.i = 0;
}
tok_str_add_tok(&str);
}
tok_str_add(&str, -1); /* simulate end of file */
tok_str_add(&str, 0);
/* now evaluate C constant expression */
macro_ptr = str.str;
next();
c = expr_const();
macro_ptr = NULL;
tok_str_free(str.str);
return c != 0;
}
#if defined(PARSE_DEBUG) || defined(PP_DEBUG)
static void tok_print(int *str)
{
int t;
CValue cval;
while (1) {
TOK_GET(t, str, cval);
if (!t)
break;
printf(" %s", get_tok_str(t, &cval));
}
printf("\n");
}
#endif
/* parse after #define */
static void parse_define(void)
{
Sym *s, *first, **ps;
int v, t, varg, is_vaargs, c;
TokenString str;
v = tok;
if (v < TOK_IDENT)
error("invalid macro name '%s'", get_tok_str(tok, &tokc));
/* XXX: should check if same macro (ANSI) */
first = NULL;
t = MACRO_OBJ;
/* '(' must be just after macro definition for MACRO_FUNC */
c = file->buf_ptr[0];
if (c == '\\')
c = handle_stray1(file->buf_ptr);
if (c == '(') {
next_nomacro();
next_nomacro();
ps = &first;
while (tok != ')') {
varg = tok;
next_nomacro();
is_vaargs = 0;
if (varg == TOK_DOTS) {
varg = TOK___VA_ARGS__;
is_vaargs = 1;
} else if (tok == TOK_DOTS && gnu_ext) {
is_vaargs = 1;
next_nomacro();
}
if (varg < TOK_IDENT)
error("badly punctuated parameter list");
s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0);
*ps = s;
ps = &s->next;
if (tok != ',')
break;
next_nomacro();
}
t = MACRO_FUNC;
}
tok_str_new(&str);
next_nomacro();
/* EOF testing necessary for '-D' handling */
while (tok != TOK_LINEFEED && tok != TOK_EOF) {
tok_str_add2(&str, tok, &tokc);
next_nomacro();
}
tok_str_add(&str, 0);
#ifdef PP_DEBUG
printf("define %s %d: ", get_tok_str(v, NULL), t);
tok_print(str.str);
#endif
define_push(v, t, str.str, first);
}
static inline int hash_cached_include(int type, const char *filename)
{
const unsigned char *s;
unsigned int h;
h = TOK_HASH_INIT;
h = TOK_HASH_FUNC(h, type);
s = filename;
while (*s) {
h = TOK_HASH_FUNC(h, *s);
s++;
}
h &= (CACHED_INCLUDES_HASH_SIZE - 1);
return h;
}
/* XXX: use a token or a hash table to accelerate matching ? */
static CachedInclude *search_cached_include(TCCState *s1,
int type, const char *filename)
{
CachedInclude *e;
int i, h;
h = hash_cached_include(type, filename);
i = s1->cached_includes_hash[h];
for(;;) {
if (i == 0)
break;
e = s1->cached_includes[i - 1];
if (e->type == type && !strcmp(e->filename, filename))
return e;
i = e->hash_next;
}
return NULL;
}
static inline void add_cached_include(TCCState *s1, int type,
const char *filename, int ifndef_macro)
{
CachedInclude *e;
int h;
if (search_cached_include(s1, type, filename))
return;
#ifdef INC_DEBUG
printf("adding cached '%s' %s\n", filename, get_tok_str(ifndef_macro, NULL));
#endif
e = tcc_malloc(sizeof(CachedInclude) + strlen(filename));
if (!e)
return;
e->type = type;
strcpy(e->filename, filename);
e->ifndef_macro = ifndef_macro;
dynarray_add((void ***)&s1->cached_includes, &s1->nb_cached_includes, e);
/* add in hash table */
h = hash_cached_include(type, filename);
e->hash_next = s1->cached_includes_hash[h];
s1->cached_includes_hash[h] = s1->nb_cached_includes;
}
static void pragma_parse(TCCState *s1)
{
int val;
next();
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) {
next();
if (s1->pack_stack_ptr <= s1->pack_stack) {
stk_error:
error("out of pack stack");
}
s1->pack_stack_ptr--;
} else {
val = 0;
if (tok != ')') {
if (tok == TOK_ASM_push) {
next();
if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1)
goto stk_error;
s1->pack_stack_ptr++;
skip(',');
}
if (tok != TOK_CINT) {
pack_error:
error("invalid pack pragma");
}
val = tokc.i;
if (val < 1 || val > 16 || (val & (val - 1)) != 0)
goto pack_error;
next();
}
*s1->pack_stack_ptr = val;
skip(')');
}
}
}
/* is_bof is true if first non space token at beginning of file */
static void preprocess(int is_bof)
{
TCCState *s1 = tcc_state;
int size, i, c, n, saved_parse_flags;
char buf[1024], *q, *p;
char buf1[1024];
BufferedFile *f;
Sym *s;
CachedInclude *e;
saved_parse_flags = parse_flags;
parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM |
PARSE_FLAG_LINEFEED;
next_nomacro();
redo:
switch(tok) {
case TOK_DEFINE:
next_nomacro();
parse_define();
break;
case TOK_UNDEF:
next_nomacro();
s = define_find(tok);
/* undefine symbol by putting an invalid name */
if (s)
define_undef(s);
break;
case TOK_INCLUDE:
case TOK_INCLUDE_NEXT:
ch = file->buf_ptr[0];
/* XXX: incorrect if comments : use next_nomacro with a special mode */
skip_spaces();
if (ch == '<') {
c = '>';
goto read_name;
} else if (ch == '\"') {
c = ch;
read_name:
/* XXX: better stray handling */
minp();
q = buf;
while (ch != c && ch != '\n' && ch != CH_EOF) {
if ((q - buf) < sizeof(buf) - 1)
*q++ = ch;
minp();
}
*q = '\0';
minp();
#if 0
/* eat all spaces and comments after include */
/* XXX: slightly incorrect */