mirror of
https://github.com/nArnoSNES/tcc-65816.git
synced 2024-10-08 07:54:59 +00:00
10673 lines
307 KiB
C
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 */
|
|
|