From 14658993425878be300aae2e879560698e0c6c4c Mon Sep 17 00:00:00 2001 From: bellard Date: Thu, 3 Jan 2002 21:14:59 +0000 Subject: [PATCH] added full long long support - added section support - added __attribute__ parsing - added -U option --- i386-gen.c | 276 +++++++----- tcc.c | 1422 +++++++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 1341 insertions(+), 357 deletions(-) diff --git a/i386-gen.c b/i386-gen.c index 11f48984..7741bccb 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -24,8 +24,12 @@ /* a register can belong to several classes */ #define RC_INT 0x0001 /* generic integer register */ #define RC_FLOAT 0x0002 /* generic float register */ -#define RC_IRET 0x0004 /* function returned integer register */ -#define RC_FRET 0x0008 /* function returned float register */ +#define RC_EAX 0x0004 +#define RC_FRET 0x0008 /* function return: float register */ +#define RC_ECX 0x0010 +#define RC_EDX 0x0020 +#define RC_IRET RC_EAX /* function return: integer register */ +#define RC_LRET RC_EDX /* function return: second integer register */ /* pretty names for the registers */ enum { @@ -37,14 +41,15 @@ enum { int reg_classes[NB_REGS] = { /* eax */ RC_INT | RC_IRET, - /* ecx */ RC_INT, - /* edx */ RC_INT, + /* ecx */ RC_INT | RC_ECX, + /* edx */ RC_INT | RC_EDX, /* st0 */ RC_FLOAT | RC_FRET, }; /* return registers for function */ -#define REG_IRET REG_EAX -#define REG_FRET REG_ST0 +#define REG_IRET REG_EAX /* single word int return register */ +#define REG_LRET REG_EDX /* second word return register (for long long) */ +#define REG_FRET REG_ST0 /* float return register */ /* defined if function parameters must be evaluated in reverse order */ #define INVERT_FUNC_PARAMS @@ -313,8 +318,14 @@ void gfunc_param(GFuncContext *c) /* simple type (currently always same size) */ /* XXX: implicit cast ? */ r = gv(RC_INT); + if ((vtop->t & VT_BTYPE) == VT_LLONG) { + size = 8; + o(0x50 + vtop->r2); /* push r */ + } else { + size = 4; + } o(0x50 + r); /* push r */ - c->args_size += 4; + c->args_size += size; } vtop--; } @@ -395,73 +406,139 @@ int gtst(int inv, int t) /* generate an integer binary operation */ void gen_opi(int op) { - int t, r, fr; + int r, fr, opc, c; - vswap(); - r = gv(RC_INT); - vswap(); - fr = gv(RC_INT); - vtop--; - - if (op == '+') { - o(0x01); - o(0xc0 + r + fr * 8); - } else if (op == '-') { - o(0x29); - o(0xc0 + r + fr * 8); - } else if (op == '&') { - o(0x21); - o(0xc0 + r + fr * 8); - } else if (op == '^') { - o(0x31); - o(0xc0 + r + fr * 8); - } else if (op == '|') { - o(0x09); - o(0xc0 + r + fr * 8); - } else if (op == '*') { + switch(op) { + case '+': + case TOK_ADDC1: /* add with carry generation */ + opc = 0; + gen_op8: + vswap(); + r = gv(RC_INT); + vswap(); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) { + /* constant case */ + c = vtop->c.i; + if (c == (char)c) { + /* XXX: generate inc and dec for smaller code ? */ + o(0x83); + o(0xc0 | (opc << 3) | r); + g(c); + } else { + o(0x81); + oad(0xc0 | (opc << 3) | r, c); + } + } else { + fr = gv(RC_INT); + o((opc << 3) | 0x01); + o(0xc0 + r + fr * 8); + } + vtop--; + if (op >= TOK_ULT && op <= TOK_GT) { + vtop--; + vset(VT_INT, VT_CMP, op); + } + break; + case '-': + case TOK_SUBC1: /* sub with carry generation */ + opc = 5; + goto gen_op8; + case TOK_ADDC2: /* add with carry use */ + opc = 2; + goto gen_op8; + case TOK_SUBC2: /* sub with carry use */ + opc = 3; + goto gen_op8; + case '&': + opc = 4; + goto gen_op8; + case '^': + opc = 6; + goto gen_op8; + case '|': + opc = 1; + goto gen_op8; + case '*': + vswap(); + r = gv(RC_INT); + vswap(); + fr = gv(RC_INT); + vtop--; o(0xaf0f); /* imul fr, r */ o(0xc0 + fr + r * 8); - } else if (op == TOK_SHL | op == TOK_SHR | op == TOK_SAR) { - /* op2 is %ecx */ - if (fr != 1) { - if (r == 1) { - r = fr; - fr = 1; - o(0x87); /* xchg r, %ecx */ - o(0xc1 + r * 8); - } else - move_reg(1, fr); + break; + case TOK_SHL: + opc = 4; + goto gen_shift; + case TOK_SHR: + opc = 5; + goto gen_shift; + case TOK_SAR: + opc = 7; + gen_shift: + vswap(); + r = gv(RC_INT); + vswap(); + opc = 0xc0 | (opc << 3); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) { + /* constant case */ + c = vtop->c.i & 0x1f; + o(0xc1); /* shl/shr/sar $xxx, r */ + o(opc | r); + g(c); + } else { + /* we generate the shift in ecx */ + gv(RC_ECX); + /* the first op may have been spilled, so we reload it if + needed */ + vswap(); + r = gv(RC_INT); + vswap(); + o(0xd3); /* shl/shr/sar %cl, r */ + o(opc | r); } - o(0xd3); /* shl/shr/sar %cl, r */ - if (op == TOK_SHL) - o(0xe0 + r); - else if (op == TOK_SHR) - o(0xe8 + r); - else - o(0xf8 + r); + vtop--; vtop->r = r; - } else if (op == '/' | op == TOK_UDIV | op == TOK_PDIV | - op == '%' | op == TOK_UMOD) { - save_reg(2); /* save edx */ - t = save_reg_forced(fr); /* save fr and get op2 location */ - move_reg(0, r); /* op1 is %eax */ - if (op == TOK_UDIV | op == TOK_UMOD) { - o(0xf7d231); /* xor %edx, %edx, div t(%ebp), %eax */ - oad(0xb5, t); + break; + case '/': + case TOK_UDIV: + case TOK_PDIV: + case '%': + case TOK_UMOD: + case TOK_UMULL: + vswap(); + r = gv(RC_EAX); /* first operand must be in eax */ + vswap(); + /* XXX: need better constraint */ + fr = gv(RC_ECX); /* second operand in ecx */ + vswap(); + r = gv(RC_EAX); /* reload first operand if flushed */ + vswap(); + vtop--; + save_reg(REG_EDX); + if (op == TOK_UMULL) { + o(0xf7); /* mul fr */ + o(0xe0 + fr); + vtop->r2 = REG_EDX; + r = REG_EAX; } else { - o(0xf799); /* cltd, idiv t(%ebp), %eax */ - oad(0xbd, t); + if (op == TOK_UDIV || op == TOK_UMOD) { + o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ + o(0xf0 + fr); + } else { + o(0xf799); /* cltd, idiv fr, %eax */ + o(0xf8 + fr); + } + if (op == '%' || op == TOK_UMOD) + r = REG_EDX; + else + r = REG_EAX; } - if (op == '%' | op == TOK_UMOD) - r = REG_EDX; - else - r = REG_EAX; vtop->r = r; - } else { - vtop--; - o(0x39); - o(0xc0 + r + fr * 8); /* cmp fr, r */ - vset(VT_INT, VT_CMP, op); + break; + default: + opc = 7; + goto gen_op8; } } @@ -473,12 +550,12 @@ void gen_opf(int op) int a, ft, fc, swapped; /* convert constants to memory references */ - if ((vtop[-1].r & (VT_CONST | VT_LVAL)) == VT_CONST) { + if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { vswap(); gv(RC_FLOAT); vswap(); } - if ((vtop[0].r & (VT_CONST | VT_LVAL)) == VT_CONST) + if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) gv(RC_FLOAT); /* must put at least one value in the floating point register */ @@ -488,7 +565,7 @@ void gen_opf(int op) gv(RC_FLOAT); vswap(); } - if (op >= TOK_EQ && op <= TOK_GT) { + if (op >= TOK_ULT && op <= TOK_GT) { /* load on stack second operand */ load(REG_ST0, vtop); if (op == TOK_GE || op == TOK_GT) @@ -561,11 +638,26 @@ void gen_opf(int op) } } -/* convert integers to fp 't' type */ +/* FPU control word for rounding to nearest mode */ +/* XXX: should move that into tcc lib support code ! */ +static unsigned short __tcc_fpu_control = 0x137f; +/* FPU control word for round to zero mode for int convertion */ +static unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00; + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ void gen_cvt_itof(int t) { gv(RC_INT); - if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) { + if ((vtop->t & VT_BTYPE) == VT_LLONG) { + /* signed long long to float/double/long double (unsigned case + is handled generically) */ + o(0x50 + vtop->r2); /* push r2 */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x242cdf); /* fildll (%esp) */ + o(0x08c483); /* add $8, %esp */ + } else if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_INT | VT_UNSIGNED)) { /* unsigned int to float/double/long double */ o(0x6a); /* push $0 */ g(0x00); @@ -581,38 +673,38 @@ void gen_cvt_itof(int t) vtop->r = REG_ST0; } -/* FPU control word for rounding to nearest mode */ -/* XXX: should move that into tcc lib support code ! */ -static unsigned short __tcc_fpu_control = 0x137f; -/* FPU control word for round to zero mode for int convertion */ -static unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00; - /* convert fp to int 't' type */ /* XXX: handle long long case */ void gen_cvt_ftoi(int t) { - int r, size; + int r, r2, size; gv(RC_FLOAT); - if (t == VT_INT | VT_UNSIGNED && - t == VT_LLONG | VT_UNSIGNED && - t == VT_LLONG) + if (t != VT_INT) size = 8; else size = 4; - - r = get_reg(RC_INT); + oad(0x2dd9, (int)&__tcc_int_fpu_control); /* ldcw xxx */ oad(0xec81, size); /* sub $xxx, %esp */ if (size == 4) o(0x1cdb); /* fistpl */ else - o(0x3cdb); /* fistpll */ + o(0x3cdf); /* fistpll */ o(0x24); oad(0x2dd9, (int)&__tcc_fpu_control); /* ldcw xxx */ + r = get_reg(RC_INT); o(0x58 + r); /* pop r */ - if (size == 8) - o(0x04c483); /* add $4, %esp */ + if (size == 8) { + if (t == VT_LLONG) { + vtop->r = r; /* mark reg as used */ + r2 = get_reg(RC_INT); + o(0x58 + r2); /* pop r2 */ + vtop->r2 = r2; + } else { + o(0x04c483); /* add $4, %esp */ + } + } vtop->r = r; } @@ -623,18 +715,6 @@ void gen_cvt_ftof(int t) gv(RC_FLOAT); } -/* pop stack value */ -void vpop(void) -{ - /* for x86, we need to pop the FP stack */ - if ((vtop->r & VT_VALMASK) == REG_ST0) { - o(0xd9dd); /* fstp %st(1) */ - } - vtop--; -} - - - /* end of X86 code generator */ /*************************************************************/ diff --git a/tcc.c b/tcc.c index 23e34f33..58de4232 100644 --- a/tcc.c +++ b/tcc.c @@ -19,9 +19,11 @@ */ #include #include +#include #include #include #include +#include #ifndef CONFIG_TCC_STATIC #include #endif @@ -35,6 +37,10 @@ #define TEXT_SIZE (4*1024*1024) #define DATA_SIZE (4*1024*1024) +/* amount of virtual memory associate to a section (currently, we do + not realloc them) */ +#define SECTION_VSIZE (1024 * 1024) + #define INCLUDE_STACK_SIZE 32 #define IFDEF_STACK_SIZE 64 #define VSTACK_SIZE 64 @@ -71,7 +77,9 @@ typedef union CValue { /* value on stack */ typedef struct SValue { int t; /* type */ - int r; /* register + flags */ + 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 */ } SValue; @@ -101,6 +109,19 @@ typedef struct Reloc { #define RELOC_ADDR32 1 /* 32 bits relocation */ #define RELOC_REL32 2 /* 32 bits relative relocation */ +/* section definition */ +typedef struct Section { + char name[64]; /* section name */ + unsigned char *data; /* section data */ + unsigned char *data_ptr; /* current data pointer */ + struct Section *next; +} Section; + +/* GNUC attribute definition */ +typedef struct AttributeDef { + int aligned; + Section *section; +} AttributeDef; #define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */ #define SYM_FIELD 0x20000000 /* struct/union field symbol space */ @@ -130,15 +151,19 @@ int line_num; int ch, ch1, tok, tok1; CValue tokc, tok1c; +/* sections */ +Section *first_section; +Section *text_section, *data_section, *bss_section; /* predefined sections */ +Section *cur_text_section; /* current section where function code is + generated */ + /* loc : local variable index - glo : global variable index - ind : output code ptr + ind : output code index rsym: return symbol - prog: output code anon_sym: anonymous symbol index */ int rsym, anon_sym, - prog, ind, loc, glo, const_wanted; + prog, ind, loc, const_wanted; int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */ int func_vt, func_vc; /* current function return type (used by @@ -166,16 +191,18 @@ int gnu_ext = 1; int tcc_ext = 1; /* The current value can be: */ -#define VT_VALMASK 0x00ff -#define VT_CONST 0x00f0 /* constant in vc +#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_FORWARD 0x0200 /* value is forward reference */ +#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_FORWARD 0x0200 /* value is forward reference */ +#define VT_MUSTCAST 0x0400 /* value must be casted to be correct (user for + bool/char/short stored in int registers) */ /* types */ #define VT_STRUCT_SHIFT 16 /* structure/enum name shift (16 bits left) */ @@ -228,13 +255,10 @@ int tcc_ext = 1; #define TOK_DEC 0xa2 #define TOK_MID 0xa3 /* inc/dec, to void constant */ #define TOK_INC 0xa4 -#define TOK_ARROW 0xa7 -#define TOK_DOTS 0xa8 /* three dots */ -#define TOK_SHR 0xa9 /* unsigned shift right */ #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_NUM 0xb3 /* number in tokc */ +#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 */ @@ -243,8 +267,19 @@ int tcc_ext = 1; #define TOK_CFLOAT 0xb9 /* float constant */ #define TOK_CDOUBLE 0xc0 /* double constant */ #define TOK_CLDOUBLE 0xc1 /* long double constant */ - - #define TOK_SHL 0x01 /* shift left */ +#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_SHL 0x01 /* shift left */ #define TOK_SAR 0x02 /* signed shift right */ /* assignement operators : normal operator or 0x80 */ @@ -303,7 +338,8 @@ enum { TOK_DEFAULT, TOK_ENUM, TOK_SIZEOF, - + TOK___ATTRIBUTE__, + /* preprocessor only */ TOK_UIDENT, /* first "user" ident (not keyword) */ TOK_DEFINE = TOK_UIDENT, @@ -325,6 +361,13 @@ enum { /* special identifiers */ TOK___FUNC__, TOK_MAIN, + /* attribute identifiers */ + TOK_SECTION, + TOK___SECTION__, + TOK_ALIGNED, + TOK___ALIGNED__, + TOK_UNUSED, + TOK___UNUSED__, }; /* XXX: need to define this to use them in non ISOC99 context */ @@ -339,7 +382,7 @@ void expr_eq(void); void gexpr(void); void decl(int l); void decl_initializer(int t, int r, int c, int first, int size_only); -int decl_initializer_alloc(int t, int sec, int has_init); +int decl_initializer_alloc(int t, AttributeDef *ad, int r, int has_init); int gv(int rc); void move_reg(int r, int s); void save_reg(int r); @@ -352,17 +395,24 @@ void macro_subst(int **tok_str, int *tok_len, Sym **nested_list, int *macro_str); int save_reg_forced(int r); void gen_op(int op); +void force_charshort_cast(int t); void gen_cast(int t); void vstore(void); + +/* type handling */ int type_size(int t, int *a); int pointed_type(int t); int pointed_size(int t); -int parse_btype(int *type_ptr); +int is_compatible_types(int t1, int t2); +int parse_btype(int *type_ptr, AttributeDef *ad); int type_decl(int *v, int t, int td); + void error(const char *fmt, ...); void vpushi(int v); void vset(int t, int r, int v); void greloc(Sym *s, int addr, int type); +void type_to_str(char *buf, int buf_size, + int t, const char *varstr); /* true if float/double/long double type */ static inline int is_float(int t) @@ -421,6 +471,137 @@ void *dlsym(void *handle, char *symbol) #endif +/********************************************************/ +/* runtime library is there */ +/* XXX: we suppose that the host compiler handles 'long long'. It + would not be difficult to suppress this assumption */ + +/* XXX: these functions are defined in libgcc. Should provide a + portable code too. */ +long long __divll(long long a, long long b) +{ + return a / b; +} + +long long __modll(long long a, long long b) +{ + return a % b; +} + +unsigned long long __divull(unsigned long long a, unsigned long long b) +{ + return a / b; +} + +unsigned long long __modull(unsigned long long a, unsigned long long b) +{ + return a % b; +} + +long long __sardi3(long long a, int b) +{ + return a >> b; +} + +unsigned long long __shrdi3(unsigned long long a, int b) +{ + return a >> b; +} + +long long __shldi3(long long a, int b) +{ + return a << b; +} + +float __ulltof(unsigned long long a) +{ + return (float)a; +} + +double __ulltod(unsigned long long a) +{ + return (double)a; +} + +long double __ulltold(unsigned long long a) +{ + return (long double)a; +} + +unsigned long long __ftoull(float a) +{ + return (unsigned long long)a; +} + +unsigned long long __dtoull(double a) +{ + return (unsigned long long)a; +} + +unsigned long long __ldtoull(long double a) +{ + return (unsigned long long)a; +} + + +/********************************************************/ + +/* copy a string and trucate it */ +char *pstrcpy(char *buf, int buf_size, const char *s) +{ + char *q, *q_end; + int c; + + q = buf; + q_end = buf + buf_size - 1; + while (q < q_end) { + c = *s++; + if (c == '\0') + break; + *q++ = c; + } + *q = '\0'; + return buf; +} + +Section *new_section(const char *name) +{ + Section *sec, **psec; + void *data; + + sec = malloc(sizeof(Section)); + if (!sec) + error("memory full"); + pstrcpy(sec->name, sizeof(sec->name), name); + data = mmap(NULL, SECTION_VSIZE, + PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + if (data == (void *)(-1)) + error("could not mmap section '%s'", name); + sec->data = data; + sec->data_ptr = data; + psec = &first_section; + while (*psec != NULL) + psec = &(*psec)->next; + sec->next = NULL; + *psec = sec; + return sec; +} + +/* return a reference to a section, and create it if it does not + exists */ +Section *find_section(const char *name) +{ + Section *sec; + + for(sec = first_section; sec != NULL; sec = sec->next) { + if (!strcmp(name, sec->name)) + return sec; + } + return new_section(name); +} + /* add a new relocation entry to symbol 's' */ void greloc(Sym *s, int addr, int type) { @@ -460,7 +641,7 @@ static inline int isid(int c) static inline int isnum(int c) { - return c >= '0' & c <= '9'; + return c >= '0' && c <= '9'; } static inline int toup(int c) @@ -521,7 +702,7 @@ void test_lvalue(void) expect("lvalue"); } -TokenSym *tok_alloc(char *str, int len) +TokenSym *tok_alloc(const char *str, int len) { TokenSym *ts, **pts, **ptable; int h, i; @@ -597,7 +778,7 @@ char *get_tok_str(int v, CValue *cv) char *p; int i; - if (v == TOK_NUM) { + if (v == TOK_CINT || v == TOK_CUINT) { sprintf(buf, "%u", cv->ui); return buf; } else if (v == TOK_CCHAR || v == TOK_LCHAR) { @@ -672,7 +853,7 @@ Sym *sym_find1(SymStack *st, int v) return s; s = s->hash_next; } - return 0; + return NULL; } Sym *sym_push1(SymStack *st, int v, int t, int c) @@ -863,7 +1044,8 @@ static inline int tok_ext_size(int t) { switch(t) { /* 4 bytes */ - case TOK_NUM: + case TOK_CINT: + case TOK_CUINT: case TOK_CCHAR: case TOK_LCHAR: case TOK_STR: @@ -871,6 +1053,8 @@ static inline int tok_ext_size(int t) case TOK_CFLOAT: return 1; case TOK_CDOUBLE: + case TOK_CLLONG: + case TOK_CULLONG: return 2; case TOK_CLDOUBLE: return LDOUBLE_SIZE / 4; @@ -938,11 +1122,11 @@ int expr_preprocess(void) c = sym_find1(&define_stack, tok) != 0; if (t == '(') next_nomacro(); - tok = TOK_NUM; + tok = TOK_CINT; tokc.i = c; } else if (tok >= TOK_IDENT) { /* if undefined macro */ - tok = TOK_NUM; + tok = TOK_CINT; tokc.i = 0; } tok_add2(&str, &len, tok, &tokc); @@ -974,22 +1158,6 @@ void tok_print(int *str) } #endif -/* XXX: should be more factorized */ -void define_symbol(char *sym) -{ - TokenSym *ts; - int *str, len; - CValue cval; - - ts = tok_alloc(sym, 0); - str = NULL; - len = 0; - cval.i = 1; - tok_add2(&str, &len, TOK_NUM, &cval); - tok_add(&str, &len, 0); - sym_push1(&define_stack, ts->tok, MACRO_OBJ, (int)str); -} - void preprocess(void) { int size, i, c, v, t, *str, len; @@ -1152,7 +1320,7 @@ void preprocess(void) ifdef_stack_ptr--; } else if (tok == TOK_LINE) { next(); - if (tok != TOK_NUM) + if (tok != TOK_CINT) error("#line"); line_num = tokc.i; skip_spaces(); @@ -1177,15 +1345,15 @@ int getn(b) int n, t; n = 0; while (1) { - if (ch >= 'a' & ch <= 'f') + if (ch >= 'a' && ch <= 'f') t = ch - 'a' + 10; - else if (ch >= 'A' & ch <= 'F') + else if (ch >= 'A' && ch <= 'F') t = ch - 'A' + 10; else if (isnum(ch)) t = ch - '0'; else break; - if (t < 0 | t >= b) + if (t < 0 || t >= b) break; n = n * b + t; cinp(); @@ -1271,7 +1439,6 @@ void parse_number(void) { int b, t, shift, frac_bits, s, exp_val; char *q; - unsigned int n, n1; unsigned int bn[BN_SIZE]; double d; @@ -1310,9 +1477,9 @@ void parse_number(void) /* parse all digits. cannot check octal numbers at this stage because of floating point constants */ while (1) { - if (ch >= 'a' & ch <= 'f') + if (ch >= 'a' && ch <= 'f') t = ch - 'a' + 10; - else if (ch >= 'A' & ch <= 'F') + else if (ch >= 'A' && ch <= 'F') t = ch - 'A' + 10; else if (isnum(ch)) t = ch - '0'; @@ -1327,7 +1494,7 @@ void parse_number(void) *q++ = ch; cinp(); } - if (ch == '.' || + if (ch == '.' || ((ch == 'e' || ch == 'E') && b == 10) || ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) { if (b != 10) { @@ -1467,6 +1634,9 @@ void parse_number(void) } } } else { + unsigned long long n, n1; + int lcount; + /* integer number */ *q = '\0'; q = token_buf; @@ -1495,11 +1665,46 @@ void parse_number(void) if (n < n1) error("integer constant overflow"); } - tokc.ui = n; - tok = TOK_NUM; - /* XXX: add unsigned constant support (ANSI) */ - while (ch == 'L' || ch == 'l' || ch == 'U' || ch == 'u') - cinp(); + + /* XXX: not exactly ANSI compliant */ + if ((n & 0xffffffff00000000LL) != 0) { + if ((n >> 63) != 0) + tok = TOK_CULLONG; + else + tok = TOK_CLLONG; + } else if (n > 0x7fffffff) { + tok = TOK_CUINT; + } else { + tok = TOK_CINT; + } + lcount = 0; + for(;;) { + t = toup(ch); + if (t == 'L') { + if (lcount >= 2) + error("three 'l' in integer constant"); + lcount++; + if (lcount == 2) { + if (tok == TOK_CINT) + tok = TOK_CLLONG; + else if (tok == TOK_CUINT) + tok = TOK_CULLONG; + } + cinp(); + } else if (t == 'U') { + if (tok == TOK_CINT) + tok = TOK_CUINT; + else if (tok == TOK_CLLONG) + tok = TOK_CULLONG; + cinp(); + } else { + break; + } + } + if (tok == TOK_CINT || tok == TOK_CUINT) + tokc.ui = n; + else + tokc.ull = n; } } @@ -1577,7 +1782,7 @@ void next_nomacro1(void) tokc.ts = tok_alloc(token_buf, q - token_buf); minp(); } else { - q = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\247..\250##\266"; + q = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266"; /* two chars */ tok = ch; cinp(); @@ -1586,7 +1791,7 @@ void next_nomacro1(void) cinp(); tok = q[2] & 0xff; /* three chars tests */ - if (tok == TOK_SHL | tok == TOK_SAR) { + if (tok == TOK_SHL || tok == TOK_SAR) { if (ch == '=') { tok = tok | 0x80; cinp(); @@ -1712,7 +1917,7 @@ int *macro_twosharps(int *macro_str) /* XXX: we handle only most common cases: ident + ident or ident + number */ if (tok >= TOK_IDENT && - (t >= TOK_IDENT || t == TOK_NUM)) { + (t >= TOK_IDENT || t == TOK_CINT)) { /* XXX: buffer overflow */ p = get_tok_str(tok, &tokc); strcpy(token_buf, p); @@ -1763,7 +1968,7 @@ void macro_subst(int **tok_str, int *tok_len, /* special macros */ if (tok == TOK___LINE__) { cval.i = line_num; - tok_add2(tok_str, tok_len, TOK_NUM, &cval); + tok_add2(tok_str, tok_len, TOK_CINT, &cval); } else if (tok == TOK___FILE__) { cval.ts = tok_alloc(filename, 0); tok_add2(tok_str, tok_len, TOK_STR, &cval); @@ -1920,6 +2125,7 @@ void vsetc(int t, int r, CValue *vc) vtop++; vtop->t = t; vtop->r = r; + vtop->r2 = VT_CONST; vtop->c = *vc; } @@ -1948,70 +2154,80 @@ void vswap(void) vtop[-1] = tmp; } -void vdup(void) +void vpushv(SValue *v) { if (vtop >= vstack + VSTACK_SIZE) error("memory full"); vtop++; - *vtop = vtop[-1]; + *vtop = *v; } -int save_reg_forced(int r) +void vdup(void) { - int i, l, t; - SValue *p, sv; + vpushv(vtop); +} - /* store register */ - loc = (loc - 4) & -3; - sv.t = VT_INT; - sv.r = VT_LOCAL | VT_LVAL; - sv.c.ul = loc; - store(r, &sv); - l = loc; +/* save r to the memory stack, and mark it as being free */ +void save_reg(int r) +{ + int l, i, saved, t, size, align; + SValue *p, sv; /* modify all stack values */ + saved = 0; + l = 0; for(p=vstack;p<=vtop;p++) { i = p->r & VT_VALMASK; - if (i == r) { + if ((p->r & VT_VALMASK) == r || + (p->r2 & VT_VALMASK) == r) { + /* must save value on stack if not already done */ + if (!saved) { + /* store register in the stack */ + t = p->t; + if (!is_float(t) && (t & VT_BTYPE) != VT_LLONG) + t = VT_INT; + size = type_size(t, &align); + loc = (loc - size) & -align; + sv.t = t; + sv.r = VT_LOCAL | VT_LVAL; + sv.c.ul = loc; + store(r, &sv); + /* XXX: x86 specific: need to pop fp register ST0 if saved */ + if (r == REG_ST0) { + o(0xd9dd); /* fstp %st(1) */ + } + /* special long long case */ + if ((p->t & VT_BTYPE) == VT_LLONG) { + sv.c.ul += 4; + store(p->r2, &sv); + } + l = loc; + saved = 1; + } + /* mark that stack entry as being saved on the stack */ if (p->r & VT_LVAL) t = VT_LLOCAL; else t = VT_LOCAL; p->r = VT_LVAL | t; + p->r2 = VT_CONST; p->c.ul = l; } } - return l; -} - -/* save r to memory. and mark it as being free */ -void save_reg(int r) -{ - int i; - SValue *p; - - /* modify all stack values */ - for(p=vstack;p<=vtop;p++) { - i = p->r & VT_VALMASK; - if (i == r) { - save_reg_forced(r); - break; - } - } } /* find a free register of class 'rc'. If none, save one register */ int get_reg(int rc) { - int r, i; + int r; SValue *p; /* find a free register */ for(r=0;rr & VT_VALMASK; - if (i == r) + if ((p->r & VT_VALMASK) == r || + (p->r2 & VT_VALMASK) == r) goto notfound; } return r; @@ -2019,9 +2235,9 @@ int get_reg(int rc) notfound: ; } - /* no register left : free the first one on the stack (very - important to start from the bottom to ensure that we don't - spill registers used in gen_op()) */ + /* no register left : free the first one on the stack (VERY + IMPORTANT to start from the bottom to ensure that we don't + spill registers used in gen_opi()) */ for(p=vstack;p<=vtop;p++) { r = p->r & VT_VALMASK; if (r < VT_CONST && (reg_classes[r] & rc)) { @@ -2061,11 +2277,12 @@ void move_reg(int r, int s) } /* store vtop a register belonging to class 'rc'. lvalues are - converted as values. Cannot be used if cannot be converted to + converted to values. Cannot be used if cannot be converted to register value (such as structures). */ int gv(int rc) { - int r, bit_pos, bit_size, size, align, i; + int r, r2, rc2, bit_pos, bit_size, size, align, i, data_offset; + unsigned long long ll; /* NOTE: get_reg can modify vstack[] */ if (vtop->t & VT_BITFIELD) { @@ -2086,14 +2303,16 @@ int gv(int rc) /* CPUs usually cannot use float constants, so we store them generically in data segment */ size = type_size(vtop->t, &align); - glo = (glo + align - 1) & -align; + data_offset = (int)data_section->data_ptr; + data_offset = (data_offset + align - 1) & -align; /* XXX: not portable yet */ size = size >> 2; for(i=0;ic.tab[i]; + ((int *)data_offset)[i] = vtop->c.tab[i]; vtop->r |= VT_LVAL; - vtop->c.ul = glo; - glo += size << 2; + vtop->c.ul = data_offset; + data_offset += size << 2; + data_section->data_ptr = (unsigned char *)data_offset; } r = vtop->r & VT_VALMASK; /* need to reload if: @@ -2102,15 +2321,357 @@ int gv(int rc) - already a register, but not in the right class */ if (r >= VT_CONST || (vtop->r & VT_LVAL) || - !(reg_classes[r] & rc)) { + !(reg_classes[r] & rc) || + ((vtop->t & VT_BTYPE) == VT_LLONG && + !(reg_classes[vtop->r2] & rc))) { r = get_reg(rc); - load(r, vtop); + if ((vtop->t & VT_BTYPE) == VT_LLONG) { + /* two register type load : expand to two words + temporarily */ + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* load constant */ + ll = vtop->c.ull; + vtop->c.ui = ll; /* first word */ + load(r, vtop); + vtop->r = r; /* save register value */ + vpushi(ll >> 32); /* second word */ + } else if (r >= VT_CONST || + (vtop->r & VT_LVAL)) { + /* load from memory */ + load(r, vtop); + vdup(); + vtop[-1].r = r; /* save register value */ + /* increment pointer to get second word */ + vtop->t = VT_INT; + vtop->r &= ~VT_LVAL; + vpushi(4); + gen_op('+'); + vtop->r |= VT_LVAL; + } else { + /* move registers */ + load(r, vtop); + vdup(); + vtop[-1].r = r; /* save register value */ + vtop->r = vtop[-1].r2; + } + /* allocate second register */ + rc2 = RC_INT; + if (rc == RC_IRET) + rc2 = RC_LRET; + r2 = get_reg(rc2); + load(r2, vtop); + vpop(); + /* write second register */ + vtop->r2 = r2; + } else { + /* one register type load */ + load(r, vtop); + } } vtop->r = r; } return r; } +/* expand long long on stack in two int registers */ +void lexpand(void) +{ + int u; + + u = vtop->t & VT_UNSIGNED; + gv(RC_INT); + vdup(); + vtop[0].r = vtop[-1].r2; + vtop[0].r2 = VT_CONST; + vtop[-1].r2 = VT_CONST; + vtop[0].t = VT_INT | u; + vtop[-1].t = VT_INT | u; +} + +/* build a long long from two ints */ +void lbuild(int t) +{ + vswap(); + gv(RC_INT); + vswap(); + gv(RC_INT); + vtop[-1].r2 = vtop[0].r; + vtop[-1].t = t; + vpop(); +} + +/* rotate n first stack elements to the bottom */ +void vrotb(int n) +{ + int i; + SValue tmp; + + tmp = vtop[-n + 1]; + for(i=-n+1;i!=0;i++) + vtop[i] = vtop[i+1]; + vtop[0] = tmp; +} + +/* pop stack value */ +void vpop(void) +{ + /* for x86, we need to pop the FP stack */ + if ((vtop->r & VT_VALMASK) == REG_ST0) { + o(0xd9dd); /* fstp %st(1) */ + } + vtop--; +} + +/* convert stack entry to register and duplicate its value in another + register */ +void gv_dup(void) +{ + int rc, t, r, r1; + SValue sv; + + t = vtop->t; + if ((t & VT_BTYPE) == VT_LLONG) { + lexpand(); + gv_dup(); + vswap(); + vrotb(3); + gv_dup(); + vrotb(4); + /* stack: H L L1 H1 */ + lbuild(t); + vrotb(3); + vrotb(3); + vswap(); + lbuild(t); + vswap(); + } else { + /* duplicate value */ + rc = RC_INT; + sv.t = VT_INT; + if (is_float(t)) { + rc = RC_FLOAT; + sv.t = t; + } + r = gv(rc); + r1 = get_reg(rc); + sv.r = r; + sv.c.ul = 0; + load(r1, &sv); /* move r to r1 */ + vdup(); + /* duplicates value */ + vtop->r = r1; + } +} + +/* generate CPU independent (unsigned) long long operations */ +void gen_opl(int op) +{ + int t, a, b, op1, c, i; + void *func; + GFuncContext gf; + SValue tmp; + + switch(op) { + case '/': + case TOK_PDIV: + func = __divll; + goto gen_func; + case TOK_UDIV: + func = __divull; + goto gen_func; + case '%': + func = __modll; + goto gen_func; + case TOK_UMOD: + func = __modull; + gen_func: + /* call generic long long function */ + gfunc_start(&gf); + gfunc_param(&gf); + gfunc_param(&gf); + vpushi((int)func); + gfunc_call(&gf); + vpushi(0); + vtop->r = REG_IRET; + vtop->r2 = REG_LRET; + break; + case '^': + case '&': + case '|': + case '*': + case '+': + case '-': + t = vtop->t; + vswap(); + lexpand(); + vrotb(3); + lexpand(); + /* stack: L1 H1 L2 H2 */ + tmp = vtop[0]; + vtop[0] = vtop[-3]; + vtop[-3] = tmp; + tmp = vtop[-2]; + vtop[-2] = vtop[-3]; + vtop[-3] = tmp; + vswap(); + /* stack: H1 H2 L1 L2 */ + if (op == '*') { + vpushv(vtop - 1); + vpushv(vtop - 1); + gen_op(TOK_UMULL); + lexpand(); + /* stack: H1 H2 L1 L2 ML MH */ + for(i=0;i<4;i++) + vrotb(6); + /* stack: ML MH H1 H2 L1 L2 */ + tmp = vtop[0]; + vtop[0] = vtop[-2]; + vtop[-2] = tmp; + /* stack: ML MH H1 L2 H2 L1 */ + gen_op('*'); + vrotb(3); + vrotb(3); + gen_op('*'); + /* stack: ML MH M1 M2 */ + gen_op('+'); + gen_op('+'); + } else if (op == '+' || op == '-') { + /* XXX: add non carry method too (for MIPS ?) */ + if (op == '+') + op1 = TOK_ADDC1; + else + op1 = TOK_SUBC1; + gen_op(op1); + /* stack: H1 H2 (L1 op L2) */ + vrotb(3); + vrotb(3); + gen_op(op1 + 1); /* TOK_xxxC2 */ + } else { + gen_op(op); + /* stack: H1 H2 (L1 op L2) */ + vrotb(3); + vrotb(3); + /* stack: (L1 op L2) H1 H2 */ + gen_op(op); + /* stack: (L1 op L2) (H1 op H2) */ + } + /* stack: L H */ + lbuild(t); + break; + case TOK_SAR: + case TOK_SHR: + case TOK_SHL: + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) { + t = vtop[-1].t; + vswap(); + lexpand(); + vrotb(3); + /* stack: L H shift */ + c = (int)vtop->c.i; + /* constant: simpler */ + /* NOTE: all comments are for SHL. the other cases are + done by swaping words */ + vpop(); + if (op != TOK_SHL) + vswap(); + if (c >= 32) { + /* stack: L H */ + vpop(); + if (c > 32) { + vpushi(c - 32); + gen_op(op); + } + if (op != TOK_SAR) { + vpushi(0); + } else { + gv_dup(); + vpushi(31); + gen_op(TOK_SAR); + } + vswap(); + } else { + vswap(); + gv_dup(); + /* stack: H L L */ + vpushi(c); + gen_op(op); + vswap(); + vpushi(32 - c); + if (op == TOK_SHL) + gen_op(TOK_SHR); + else + gen_op(TOK_SHL); + vrotb(3); + /* stack: L L H */ + vpushi(c); + gen_op(op); + gen_op('|'); + } + if (op != TOK_SHL) + vswap(); + lbuild(t); + } else { + /* XXX: should provide a faster fallback on x86 ? */ + switch(op) { + case TOK_SAR: + func = __sardi3; + goto gen_func; + case TOK_SHR: + func = __shrdi3; + goto gen_func; + case TOK_SHL: + func = __shldi3; + goto gen_func; + } + } + break; + default: + /* compare operations */ + t = vtop->t; + vswap(); + lexpand(); + vrotb(3); + lexpand(); + /* stack: L1 H1 L2 H2 */ + tmp = vtop[-1]; + vtop[-1] = vtop[-2]; + vtop[-2] = tmp; + /* stack: L1 L2 H1 H2 */ + /* compare high */ + op1 = op; + /* when values are equal, we need to compare low words. since + the jump is inverted, we invert the test too. */ + if (op1 == TOK_LT) + op1 = TOK_LE; + else if (op1 == TOK_GT) + op1 = TOK_GE; + else if (op1 == TOK_ULT) + op1 = TOK_ULE; + else if (op1 == TOK_UGT) + op1 = TOK_UGE; + a = 0; + b = 0; + gen_op(op1); + if (op1 != TOK_NE) { + a = gtst(1, 0); + } + if (op != TOK_EQ) { + /* generate non equal test */ + /* XXX: NOT PORTABLE yet */ + if (a == 0) + b = gtst(0, 0); + else + b = psym(0x850f, 0); + } + /* compare low */ + gen_op(op); + a = gtst(1, a); + gsym(b); + vset(VT_INT, VT_JMPI, a); + break; + } +} + /* handle constant optimizations and various machine independant opt */ void gen_opc(int op) { @@ -2206,6 +2767,21 @@ int pointed_size(int t) return type_size(pointed_type(t), &t); } +#if 0 +void check_pointer_types(SValue *p1, SValue *p2) +{ + char buf1[256], buf2[256]; + int t1, t2; + t1 = p1->t; + t2 = p2->t; + if (!is_compatible_types(t1, t2)) { + type_to_str(buf1, sizeof(buf1), t1, NULL); + type_to_str(buf2, sizeof(buf2), t2, NULL); + error("incompatible pointers '%s' and '%s'", buf1, buf2); + } +} +#endif + /* generic gen_op: handles types problems */ void gen_op(int op) { @@ -2216,38 +2792,21 @@ void gen_op(int op) bt1 = t1 & VT_BTYPE; bt2 = t2 & VT_BTYPE; - if (is_float(bt1) || is_float(bt2)) { - /* compute bigger type and do implicit casts */ - if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { - t = VT_LDOUBLE; - } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { - t = VT_DOUBLE; - } else { - t = VT_FLOAT; - } - if (op != '+' && op != '-' && op != '*' && op != '/' && - op < TOK_EQ || op > TOK_GT) - error("invalid operands for binary operation"); - if (bt1 != t) { - vswap(); - gen_cast(t); - vswap(); + if (bt1 == VT_PTR || bt2 == VT_PTR) { + /* at least one operand is a pointer */ + /* relationnal op: must be both pointers */ + if (op >= TOK_ULT && op <= TOK_GT) { + // check_pointer_types(vtop, vtop - 1); + /* pointers are handled are unsigned */ + t = VT_INT | VT_UNSIGNED; + goto std_op; } - if (bt2 != t) { - gen_cast(t); - } - gen_opf(op); - if (op >= TOK_EQ && op <= TOK_GT) { - /* the result is an int */ - vtop->t = VT_INT; - } else { - vtop->t = t; - } - } else if (op == '+' || op == '-') { + /* if both pointers, then it must be the '-' op */ if ((t1 & VT_BTYPE) == VT_PTR && (t2 & VT_BTYPE) == VT_PTR) { if (op != '-') - error("invalid type"); + error("cannot use pointers here"); + // check_pointer_types(vtop - 1, vtop); /* XXX: check that types are compatible */ u = pointed_size(t1); gen_opc(op); @@ -2255,26 +2814,55 @@ void gen_op(int op) vtop->t = VT_INT; vpushi(u); gen_op(TOK_PDIV); - } else if ((t1 & VT_BTYPE) == VT_PTR || - (t2 & VT_BTYPE) == VT_PTR) { + } else { + /* exactly one pointer : must be '+' or '-'. */ + if (op != '-' && op != '+') + error("cannot use pointers here"); + /* Put pointer as first operand */ if ((t2 & VT_BTYPE) == VT_PTR) { vswap(); swap(&t1, &t2); } - /* stack-4 contains pointer, stack-2 value to add */ + /* XXX: cast to int ? (long long case) */ vpushi(pointed_size(vtop[-1].t)); gen_op('*'); gen_opc(op); /* put again type if gen_opc() swaped operands */ vtop->t = t1; + } + } else if (is_float(bt1) || is_float(bt2)) { + /* compute bigger type and do implicit casts */ + if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { + t = VT_LDOUBLE; + } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { + t = VT_DOUBLE; } else { - gen_opc(op); + t = VT_FLOAT; } + /* floats can only be used for a few operations */ + if (op != '+' && op != '-' && op != '*' && op != '/' && + (op < TOK_ULT || op > TOK_GT)) + error("invalid operands for binary operation"); + goto std_op; + } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { + /* cast to biggest op */ + t = VT_LLONG; + /* convert to unsigned if it does not fit in a long long */ + if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) || + (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) + t |= VT_UNSIGNED; + goto std_op; } else { - /* XXX: test types and compute returned value */ - if ((t1 | t2) & VT_UNSIGNED || - (t1 & VT_BTYPE) == VT_PTR || - (t2 & VT_BTYPE) == VT_PTR) { + /* integer operations */ + t = VT_INT; + /* convert to unsigned if it does not fit in an integer */ + if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) || + (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) + t |= VT_UNSIGNED; + std_op: + /* XXX: currently, some unsigned operations are explicit, so + we modify them here */ + if (t & VT_UNSIGNED) { if (op == TOK_SAR) op = TOK_SHR; else if (op == '/') @@ -2290,18 +2878,118 @@ void gen_op(int op) else if (op == TOK_GE) op = TOK_UGE; } - gen_opc(op); + vswap(); + gen_cast(t); + vswap(); + /* special case for shifts and long long: we keep the shift as + an integer */ + if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) + gen_cast(VT_INT); + else + gen_cast(t); + if (is_float(t)) + gen_opf(op); + else if ((t & VT_BTYPE) == VT_LLONG) + gen_opl(op); + else + gen_opc(op); + if (op >= TOK_ULT && op <= TOK_GT) { + /* relationnal op: the result is an int */ + vtop->t = VT_INT; + } else { + vtop->t = t; + } + } +} + +/* generic itof for unsigned long long case */ +void gen_cvt_itof1(int t) +{ + GFuncContext gf; + + if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_LLONG | VT_UNSIGNED)) { + + gfunc_start(&gf); + gfunc_param(&gf); + if (t == VT_FLOAT) + vpushi((int)&__ulltof); + else if (t == VT_DOUBLE) + vpushi((int)&__ulltod); + else + vpushi((int)&__ulltold); + gfunc_call(&gf); + vpushi(0); + vtop->r = REG_FRET; + } else { + gen_cvt_itof(t); + } +} + +/* generic ftoi for unsigned long long case */ +void gen_cvt_ftoi1(int t) +{ + GFuncContext gf; + int st; + + if (t == (VT_LLONG | VT_UNSIGNED)) { + /* not handled natively */ + gfunc_start(&gf); + st = vtop->t & VT_BTYPE; + gfunc_param(&gf); + if (st == VT_FLOAT) + vpushi((int)&__ftoull); + else if (st == VT_DOUBLE) + vpushi((int)&__dtoull); + else + vpushi((int)&__ldtoull); + gfunc_call(&gf); + vpushi(0); + vtop->r = REG_IRET; + vtop->r2 = REG_LRET; + } else { + gen_cvt_ftoi(t); + } +} + +/* force char or short cast */ +void force_charshort_cast(int t) +{ + int bits, dbt; + dbt = t & VT_BTYPE; + /* XXX: add optimization if lvalue : just change type and offset */ + if (dbt == VT_BYTE) + bits = 8; + else + bits = 16; + if (t & VT_UNSIGNED) { + vpushi((1 << bits) - 1); + gen_op('&'); + } else { + bits = 32 - bits; + vpushi(bits); + gen_op(TOK_SHL); + vpushi(bits); + gen_op(TOK_SAR); } } /* cast 'vtop' to 't' type */ void gen_cast(int t) { - int bits, sbt, dbt, sf, df, c, st1, dt1; + int sbt, dbt, sf, df, c, st1, dt1; + + /* special delayed cast for char/short */ + /* XXX: in some cases (multiple cascaded casts), it may still + be incorrect */ + if (vtop->r & VT_MUSTCAST) { + vtop->r &= ~VT_MUSTCAST; + force_charshort_cast(vtop->t); + } - /* if not lvalue, then we convert now */ dbt = t & VT_BTYPE; sbt = vtop->t & VT_BTYPE; + if (sbt != dbt) { sf = is_float(sbt); df = is_float(dbt); @@ -2329,13 +3017,12 @@ void gen_cast(int t) } } else if (df) { /* convert int to fp */ - /* XXX: add const cases */ st1 = vtop->t & (VT_BTYPE | VT_UNSIGNED); if (c) { switch(st1) { case VT_LLONG | VT_UNSIGNED: case VT_LLONG: - /* well, currently not needed */ + /* XXX: add const cases for long long */ goto do_itof; case VT_INT | VT_UNSIGNED: switch(dbt) { @@ -2354,21 +3041,21 @@ void gen_cast(int t) } } else { do_itof: - gen_cvt_itof(dbt); + gen_cvt_itof1(dbt); } } else if (sf) { /* convert fp to int */ dt1 = t & (VT_BTYPE | VT_UNSIGNED); /* we handle char/short/etc... with generic code */ - if (dt1 != VT_INT | VT_UNSIGNED && - dt1 != VT_LLONG | VT_UNSIGNED && + if (dt1 != (VT_INT | VT_UNSIGNED) && + dt1 != (VT_LLONG | VT_UNSIGNED) && dt1 != VT_LLONG) dt1 = VT_INT; if (c) { switch(dt1) { case VT_LLONG | VT_UNSIGNED: case VT_LLONG: - /* well, currently not needed */ + /* XXX: add const cases for long long */ goto do_ftoi; case VT_INT | VT_UNSIGNED: switch(sbt) { @@ -2388,37 +3075,57 @@ void gen_cast(int t) } } else { do_ftoi: - gen_cvt_ftoi(dt1); + gen_cvt_ftoi1(dt1); } if (dt1 == VT_INT && (t & (VT_BTYPE | VT_UNSIGNED)) != dt1) { /* additionnal cast for char/short/bool... */ vtop->t = dt1; gen_cast(t); } + } else if (dbt == VT_LLONG) { + /* scalar to long long */ + if (c) { + if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) + vtop->c.ll = vtop->c.ui; + else + vtop->c.ll = vtop->c.i; + } else { + /* machine independant conversion */ + gv(RC_INT); + /* generate high word */ + if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) { + vpushi(0); + gv(RC_INT); + } else { + gv_dup(); + vpushi(31); + gen_op(TOK_SAR); + } + /* patch second register */ + vtop[-1].r2 = vtop->r; + vpop(); + } } else if (dbt == VT_BOOL) { + /* scalar to bool */ vpushi(0); gen_op(TOK_NE); - } else if ((dbt == VT_BYTE || dbt == VT_SHORT) && - !(vtop->r & VT_LVAL)) { - /* no need to apply if lvalue because we do it while - loading the value */ - if (dbt == VT_BYTE) - bits = 8; - else - bits = 16; - if (t & VT_UNSIGNED) { - vpushi((1 << bits) - 1); - gen_op('&'); - } else { - bits = 32 - bits; - vpushi(bits); - gen_op(TOK_SHL); - vpushi(bits); - gen_op(TOK_SAR); + } else if (dbt == VT_BYTE || dbt == VT_SHORT) { + force_charshort_cast(t); + } else if (dbt == VT_INT) { + /* scalar to int */ + if (sbt == VT_LLONG) { + /* from long long: just take low order word */ + lexpand(); + vpop(); + } else if (vtop->r & VT_LVAL) { + /* if lvalue and single word type, nothing to do (XXX: + maybe incorrect for sizeof op) */ + goto no_cast; } } } vtop->t = t; + no_cast: ; } /* return type size. Put alignment at 'a' */ @@ -2544,6 +3251,7 @@ int check_assign_types(int t1, int t2) /* print a type. If 'varstr' is not NULL, then the variable is also printed in the type */ +/* XXX: union */ /* XXX: add array and function pointers */ /* XXX: buffer overflows */ void type_to_str(char *buf, int buf_size, @@ -2633,7 +3341,7 @@ void type_to_str(char *buf, int buf_size, /* verify type compatibility to store vtop in 'st' type, and generate - casts if needed */ + casts if needed. */ void gen_assign_cast(int dt) { int st; @@ -2645,22 +3353,30 @@ void gen_assign_cast(int dt) type_to_str(buf2, sizeof(buf2), dt, NULL); error("cannot cast '%s' to '%s'", buf1, buf2); } - if ((dt & VT_BTYPE) == VT_BOOL || is_float(dt)) { - gen_cast(dt & VT_BTYPE); - } + gen_cast(dt); } /* store vtop in lvalue pushed on stack */ void vstore(void) { - int ft, r, t, size, align, bit_size, bit_pos, rc; + int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast; GFuncContext gf; ft = vtop[-1].t; - gen_assign_cast(ft & VT_TYPE); + sbt = vtop->t & VT_BTYPE; + dbt = ft & VT_BTYPE; + if (((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) || + (sbt == VT_INT && dbt == VT_SHORT)) { + /* optimize char/short casts */ + delayed_cast = VT_MUSTCAST; + vtop->t = ft & VT_TYPE; + } else { + delayed_cast = 0; + gen_assign_cast(ft & VT_TYPE); + } - if ((vtop->t & VT_BTYPE) == VT_STRUCT) { + if (sbt == VT_STRUCT) { /* if structure, only generate pointer */ /* structure assignment : generate memcpy */ /* XXX: optimize if small size */ @@ -2724,39 +3440,34 @@ void vstore(void) vtop[-1].r = t | VT_LVAL; } store(r, vtop - 1); - vtop--; - vtop->r = r; - vtop->c.i = 0; + /* two word case handling : store second register at word + 4 */ + if ((ft & VT_BTYPE) == VT_LLONG) { + vswap(); + /* convert to int to increment easily */ + vtop->t = VT_INT; + vtop->r &= ~VT_LVAL; + vpushi(4); + gen_op('+'); + vtop->r |= VT_LVAL; + vswap(); + /* XXX: it works because r2 is spilled last ! */ + store(vtop->r2, vtop - 1); + } + vswap(); + vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ + vtop->r |= delayed_cast; } } /* post defines POST/PRE add. c is the token ++ or -- */ void inc(int post, int c) { - int r, r1, rc, t; - SValue sv; - test_lvalue(); - if (post) - vdup(); /* room for returned value */ vdup(); /* save lvalue */ if (post) { - /* duplicate value */ - rc = RC_INT; - sv.t = VT_INT; - t = vtop->t & VT_TYPE; - if (is_float(t)) { - rc = RC_FLOAT; - sv.t = t; - } - r = gv(rc); - r1 = get_reg(rc); - sv.r = r; - sv.c.ul = 0; - load(r1, &sv); /* move r to r1 */ - /* duplicates value */ - vtop[-2].r = r1; - vtop[-2].c.i = 0; + gv_dup(); /* duplicate value */ + vrotb(3); + vrotb(3); } /* add constant */ vpushi(c - TOK_MID); @@ -2766,12 +3477,75 @@ void inc(int post, int c) vpop(); /* if post op, return saved value */ } +/* Parse GNUC __attribute__ extension. Currently, the following + extensions are recognized: + - aligned(n) : set data/function alignment. + - section(x) : generate data/code in this section. + - unused : currently ignored, but may be used someday. + */ +void parse_attribute(AttributeDef *ad) +{ + int t, n; + + next(); + skip('('); + skip('('); + while (tok != ')') { + if (tok < TOK_IDENT) + expect("attribute name"); + t = tok; + next(); + switch(t) { + case TOK_SECTION: + case TOK___SECTION__: + skip('('); + if (tok != TOK_STR) + expect("section name"); + ad->section = find_section(tokc.ts->str); + next(); + skip(')'); + break; + case TOK_ALIGNED: + case TOK___ALIGNED__: + skip('('); + n = expr_const(); + if (n <= 0 || (n & (n - 1)) != 0) + error("alignment must be a positive power of two"); + ad->aligned = n; + skip(')'); + break; + case TOK_UNUSED: + case TOK___UNUSED__: + /* currently, no need to handle it because tcc does not + track unused objects */ + break; + default: + warning("'%s' attribute ignored", get_tok_str(t, NULL)); + /* skip parameters */ + /* XXX: skip parenthesis too */ + if (tok == '(') { + next(); + while (tok != ')' && tok != -1) + next(); + next(); + } + break; + } + if (tok != ',') + break; + next(); + } + skip(')'); + skip(')'); +} + /* enum/struct/union declaration */ int struct_decl(int u) { int a, t, b, v, size, align, maxalign, c, offset; int bit_size, bit_pos, bsize, bt, lbit_pos; Sym *s, *ss, **ps; + AttributeDef ad; a = tok; /* save decl type */ next(); @@ -2780,7 +3554,8 @@ int struct_decl(int u) next(); /* struct already defined ? return it */ /* XXX: check consistency */ - if (s = sym_find(v | SYM_STRUCT)) { + s = sym_find(v | SYM_STRUCT); + if (s) { if (s->t != a) error("invalid type"); goto do_decl; @@ -2817,7 +3592,7 @@ int struct_decl(int u) next(); c++; } else { - parse_btype(&b); + parse_btype(&b, &ad); while (1) { bit_size = -1; v = 0; @@ -2925,11 +3700,12 @@ int struct_decl(int u) /* return 0 if no type declaration. otherwise, return the basic type and skip it. */ -int parse_btype(int *type_ptr) +int parse_btype(int *type_ptr, AttributeDef *ad) { int t, u, type_found; Sym *s; + memset(ad, 0, sizeof(AttributeDef)); type_found = 0; t = 0; while(1) { @@ -3015,6 +3791,10 @@ int parse_btype(int *type_ptr) t |= VT_TYPEDEF; next(); break; + /* GNUC attribute */ + case TOK___ATTRIBUTE__: + parse_attribute(ad); + break; default: s = sym_find(tok); if (!s || !(s->t & VT_TYPEDEF)) @@ -3037,6 +3817,7 @@ int post_type(int t) { int p, n, pt, l, t1; Sym **plast, *s, *first; + AttributeDef ad; if (tok == '(') { /* function declaration */ @@ -3047,7 +3828,7 @@ int post_type(int t) while (tok != ')') { /* read param name and compute offset */ if (l != FUNC_OLD) { - if (!parse_btype(&pt)) { + if (!parse_btype(&pt, &ad)) { if (l) { error("invalid type"); } else { @@ -3208,13 +3989,23 @@ void gfunc_param_typed(GFuncContext *gf, Sym *func, Sym *arg) void unary(void) { - int n, t, ft, fc, p, align, size, r; + int n, t, ft, fc, p, align, size, r, data_offset; Sym *s; GFuncContext gf; + AttributeDef ad; - if (tok == TOK_NUM || tok == TOK_CCHAR || tok == TOK_LCHAR) { + if (tok == TOK_CINT || tok == TOK_CCHAR || tok == TOK_LCHAR) { vpushi(tokc.i); next(); + } else if (tok == TOK_CUINT) { + vsetc(VT_INT | VT_UNSIGNED, VT_CONST, &tokc); + next(); + } else if (tok == TOK_CLLONG) { + vsetc(VT_LLONG, VT_CONST, &tokc); + next(); + } else if (tok == TOK_CULLONG) { + vsetc(VT_LLONG | VT_UNSIGNED, VT_CONST, &tokc); + next(); } else if (tok == TOK_CFLOAT) { vsetc(VT_FLOAT, VT_CONST, &tokc); next(); @@ -3227,9 +4018,11 @@ void unary(void) } else if (tok == TOK___FUNC__) { /* special function name identifier */ /* generate (char *) type */ - vset(mk_pointer(VT_BYTE), VT_CONST, glo); - strcpy((void *)glo, funcname); - glo += strlen(funcname) + 1; + data_offset = (int)data_section->data_ptr; + vset(mk_pointer(VT_BYTE), VT_CONST, data_offset); + strcpy((void *)data_offset, funcname); + data_offset += strlen(funcname) + 1; + data_section->data_ptr = (unsigned char *)data_offset; next(); } else if (tok == TOK_LSTR) { t = VT_INT; @@ -3239,20 +4032,22 @@ void unary(void) t = VT_BYTE; str_init: type_size(t, &align); - glo = (glo + align - 1) & -align; - fc = glo; + data_offset = (int)data_section->data_ptr; + data_offset = (data_offset + align - 1) & -align; + fc = data_offset; /* we must declare it as an array first to use initializer parser */ t = VT_ARRAY | mk_pointer(t); - decl_initializer(t, VT_CONST, glo, 1, 0); - glo += type_size(t, &align); + decl_initializer(t, VT_CONST, data_offset, 1, 0); + data_offset += type_size(t, &align); /* put it as pointer */ vset(t & ~VT_ARRAY, VT_CONST, fc); + data_section->data_ptr = (unsigned char *)data_offset; } else { t = tok; next(); if (t == '(') { /* cast ? */ - if (parse_btype(&t)) { + if (parse_btype(&t, &ad)) { ft = type_decl(&n, t, TYPE_ABSTRACT); skip(')'); /* check ISOC99 compound literal */ @@ -3265,7 +4060,8 @@ void unary(void) /* all except arrays are lvalues */ if (!(ft & VT_ARRAY)) r |= VT_LVAL; - fc = decl_initializer_alloc(ft, r, 1); + memset(&ad, 0, sizeof(AttributeDef)); + fc = decl_initializer_alloc(ft, &ad, r, 1); vset(ft, r, fc); } else { unary(); @@ -3309,7 +4105,7 @@ void unary(void) if (t == TOK_SIZEOF) { if (tok == '(') { next(); - if (parse_btype(&t)) { + if (parse_btype(&t, &ad)) { t = type_decl(&n, t, TYPE_ABSTRACT); } else { /* XXX: some code could be generated: add eval @@ -3355,10 +4151,10 @@ void unary(void) /* post operations */ while (1) { - if (tok == TOK_INC | tok == TOK_DEC) { + if (tok == TOK_INC || tok == TOK_DEC) { inc(1, tok); next(); - } else if (tok == '.' | tok == TOK_ARROW) { + } else if (tok == '.' || tok == TOK_ARROW) { /* field */ if (tok == TOK_ARROW) indir(); @@ -3371,7 +4167,7 @@ void unary(void) s = sym_find(((unsigned)vtop->t >> VT_STRUCT_SHIFT) | SYM_STRUCT); /* find field */ tok |= SYM_FIELD; - while (s = s->next) { + while ((s = s->next) != NULL) { if (s->v == tok) break; } @@ -3484,11 +4280,15 @@ void unary(void) gfunc_param(&gf); } else { ret.t = s->t; + ret.r2 = VT_CONST; /* return in register */ - if (is_float(ret.t)) + if (is_float(ret.t)) { ret.r = REG_FRET; - else + } else { + if ((ret.t & VT_BTYPE) == VT_LLONG) + ret.r2 = REG_LRET; ret.r = REG_IRET; + } ret.c.i = 0; } #ifndef INVERT_FUNC_PARAMS @@ -3507,6 +4307,7 @@ void unary(void) gfunc_call(&gf); /* return value */ vsetc(ret.t, ret.r, &ret.c); + vtop->r2 = ret.r2; } else { break; } @@ -3544,17 +4345,17 @@ void sum(int l) uneq(); else { sum(--l); - while ((l == 0 & (tok == '*' | tok == '/' | tok == '%')) | - (l == 1 & (tok == '+' | tok == '-')) | - (l == 2 & (tok == TOK_SHL | tok == TOK_SAR)) | - (l == 3 & ((tok >= TOK_ULE & tok <= TOK_GT) | - tok == TOK_ULT | tok == TOK_UGE)) | - (l == 4 & (tok == TOK_EQ | tok == TOK_NE)) | - (l == 5 & tok == '&') | - (l == 6 & tok == '^') | - (l == 7 & tok == '|') | - (l == 8 & tok == TOK_LAND) | - (l == 9 & tok == TOK_LOR)) { + while ((l == 0 && (tok == '*' || tok == '/' || tok == '%')) || + (l == 1 && (tok == '+' || tok == '-')) || + (l == 2 && (tok == TOK_SHL || tok == TOK_SAR)) || + (l == 3 && ((tok >= TOK_ULE && tok <= TOK_GT) || + tok == TOK_ULT || tok == TOK_UGE)) || + (l == 4 && (tok == TOK_EQ || tok == TOK_NE)) || + (l == 5 && tok == '&') || + (l == 6 && tok == '^') || + (l == 7 && tok == '|') || + (l == 8 && tok == TOK_LAND) || + (l == 9 && tok == TOK_LOR)) { t = tok; next(); sum(l); @@ -3629,7 +4430,7 @@ void expr_eq(void) next(); t = gtst(1, 0); gexpr(); - /* XXX: float handling ? */ + /* XXX: long long handling ? */ rc = RC_INT; if (is_float(vtop->t)) rc = RC_FLOAT; @@ -3758,6 +4559,7 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg) next(); if (tok != ';') { gexpr(); + gen_assign_cast(func_vt); if ((func_vt & VT_BTYPE) == VT_STRUCT) { /* if returning structure, must copy it to implicit first pointer arg location */ @@ -3841,6 +4643,7 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg) next(); skip('('); gexpr(); + /* XXX: other types than integer */ case_reg = gv(RC_INT); vpop(); skip(')'); @@ -3857,17 +4660,34 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg) gsym(a); } else if (tok == TOK_CASE) { - next(); - a = expr_const(); + int v1, v2; if (!case_sym) expect("switch"); + next(); + v1 = expr_const(); + v2 = v1; + if (gnu_ext && tok == TOK_DOTS) { + next(); + v2 = expr_const(); + if (v2 < v1) + warning("empty case range"); + } /* since a case is like a label, we must skip it with a jmp */ b = gjmp(0); gsym(*case_sym); vset(VT_INT, case_reg, 0); - vpushi(a); - gen_op(TOK_EQ); - *case_sym = gtst(1, 0); + vpushi(v1); + if (v1 == v2) { + gen_op(TOK_EQ); + *case_sym = gtst(1, 0); + } else { + gen_op(TOK_GE); + *case_sym = gtst(1, 0); + vset(VT_INT, case_reg, 0); + vpushi(v2); + gen_op(TOK_LE); + *case_sym = gtst(1, *case_sym); + } gsym(b); skip(':'); block(bsym, csym, case_sym, def_sym, case_reg); @@ -4232,10 +5052,11 @@ void decl_initializer(int t, int r, int c, int first, int size_only) /* parse an initializer for type 't' if 'has_init' is true, and allocate space in local or global data space ('r' is either VT_LOCAL or VT_CONST). The allocated address in returned */ -int decl_initializer_alloc(int t, int r, int has_init) +int decl_initializer_alloc(int t, AttributeDef *ad, int r, int has_init) { - int size, align, addr, tok1; + int size, align, addr, tok1, data_offset; int *init_str, init_len, level, *saved_macro_ptr; + Section *sec; size = type_size(t, &align); /* If unknown size, we must evaluate it before @@ -4284,17 +5105,30 @@ int decl_initializer_alloc(int t, int r, int has_init) if (size < 0) error("unknown type size"); } + /* take into account specified alignment if bigger */ + if (ad->aligned > align) + align = ad->aligned; if ((r & VT_VALMASK) == VT_LOCAL) { loc = (loc - size) & -align; addr = loc; } else { - glo = (glo + align - 1) & -align; - addr = glo; + /* compute section */ + sec = ad->section; + if (!sec) { + if (has_init) + sec = data_section; + else + sec = bss_section; + } + data_offset = (int)sec->data_ptr; + data_offset = (data_offset + align - 1) & -align; + addr = data_offset; /* very important to increment global pointer at this time because initializers themselves can create new initializers */ - glo += size; + data_offset += size; + sec->data_ptr = (unsigned char *)data_offset; } if (has_init) { decl_initializer(t, r, addr, 1, 0); @@ -4308,15 +5142,15 @@ int decl_initializer_alloc(int t, int r, int has_init) return addr; } - /* 'l' is VT_LOCAL or VT_CONST to define default storage type */ void decl(int l) { int *a, t, b, v, addr, has_init, size, align, r, u; Sym *sym; - + AttributeDef ad; + while (1) { - if (!parse_btype(&b)) { + if (!parse_btype(&b, &ad)) { /* skip redundant ';' */ /* XXX: find more elegant solution */ if (tok == ';') { @@ -4338,6 +5172,10 @@ void decl(int l) } while (1) { /* iterate thru each declaration */ t = type_decl(&v, b, TYPE_DIRECT); + /* currently, we do not parse attribute in + type_decl(). May change if needed */ + if (tok == TOK___ATTRIBUTE__) + parse_attribute(&ad); #if 0 { char buf[500]; @@ -4350,6 +5188,11 @@ void decl(int l) error("cannot use local functions"); if (!(t & VT_FUNC)) expect("function definition"); + /* compute text section */ + cur_text_section = ad.section; + if (!cur_text_section) + cur_text_section = text_section; + ind = (int)cur_text_section->data_ptr; /* patch forward references */ if ((sym = sym_find(v)) && (sym->r & VT_FORWARD)) { greloc_patch(sym, ind); @@ -4374,7 +5217,7 @@ void decl(int l) func_vc = addr; addr += 4; } - while (sym = sym->next) { + while ((sym = sym->next) != NULL) { u = sym->t; sym_push(sym->v & ~SYM_FIELD, u, VT_LOCAL | VT_LVAL, addr); @@ -4397,6 +5240,7 @@ void decl(int l) o(0xc3c9); /* leave, ret */ *a = (-loc + 3) & -4; /* align local size to word & save local variables */ + cur_text_section->data_ptr = (unsigned char *)ind; sym_pop(&label_stack, NULL); /* reset label stack */ sym_pop(&local_stack, NULL); /* reset local stack */ funcname = ""; /* for safety */ @@ -4426,7 +5270,7 @@ void decl(int l) has_init = (tok == '='); if (has_init) next(); - addr = decl_initializer_alloc(t, r, + addr = decl_initializer_alloc(t, &ad, r, has_init); if (l == VT_CONST) { /* global scope: see if already defined */ @@ -4506,10 +5350,10 @@ int tcc_compile_file(const char *filename1) { Sym *define_start; - filename = (char *)filename1; - line_num = 1; funcname = ""; + filename = (char *)filename1; + file = fopen(filename, "r"); if (!file) error("file '%s' not found", filename); @@ -4539,6 +5383,59 @@ int tcc_compile_file(const char *filename1) return 0; } +/* define a symbol. A value can also be provided with the '=' operator */ +/* XXX: currently only handles integers and string defines. should use + tcc parser, but would need a custom 'FILE *' */ +void define_symbol(const char *sym) +{ + TokenSym *ts; + int *str, len; + CValue cval; + const char *p; + char buf[256]; + + p = strchr(sym, '='); + if (!p) { + /* XXX: buffer overflow */ + strcpy(buf, sym); + p = "1"; + } else { + len = p - sym; + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + memcpy(buf, sym, len); + buf[len] = '\0'; + p++; + } + + ts = tok_alloc(buf, 0); + str = NULL; + len = 0; + if (isnum(*p)) { + /* integer case */ + cval.i = atoi(p); + tok_add2(&str, &len, TOK_CINT, &cval); + } else { + /* string case */ + cval.ts = tok_alloc(p, 0); + tok_add2(&str, &len, TOK_STR, &cval); + } + tok_add(&str, &len, 0); + sym_push1(&define_stack, ts->tok, MACRO_OBJ, (int)str); +} + +void undef_symbol(const char *sym) +{ + TokenSym *ts; + Sym *s; + printf("undef %s\n", sym); + ts = tok_alloc(sym, 0); + s = sym_find1(&define_stack, tok); + /* undefine symbol by putting an invalid name */ + if (s) + sym_undef(&define_stack, s); +} + /* open a dynamic library so that its symbol are available for compiled programs */ void open_dll(char *libname) @@ -4581,10 +5478,16 @@ void build_exe(char *filename) { FILE *f; f = fopen(filename, "w"); - fwrite((void *)prog, 1, ind - prog, f); + fwrite(text_section->data, 1, text_section->data_ptr - text_section->data, f); fclose(f); } +void help(void) +{ + printf("tcc version 0.9.3 - Tiny C Compiler - Copyright (C) 2001, 2002 Fabrice Bellard\n" + "usage: tcc [-Idir] [-Dsym[=val]] [-Usym] [-llib] [-i infile] infile [infile_args...]\n"); +} + int main(int argc, char **argv) { Sym *s; @@ -4599,7 +5502,7 @@ int main(int argc, char **argv) /* add all tokens */ tok_ident = TOK_IDENT; - p = "int\0void\0char\0if\0else\0while\0break\0return\0for\0extern\0static\0unsigned\0goto\0do\0continue\0switch\0case\0const\0volatile\0long\0register\0signed\0auto\0inline\0restrict\0float\0double\0_Bool\0short\0struct\0union\0typedef\0default\0enum\0sizeof\0define\0include\0ifdef\0ifndef\0elif\0endif\0defined\0undef\0error\0line\0__LINE__\0__FILE__\0__DATE__\0__TIME__\0__VA_ARGS__\0__func__\0main\0"; + p = "int\0void\0char\0if\0else\0while\0break\0return\0for\0extern\0static\0unsigned\0goto\0do\0continue\0switch\0case\0const\0volatile\0long\0register\0signed\0auto\0inline\0restrict\0float\0double\0_Bool\0short\0struct\0union\0typedef\0default\0enum\0sizeof\0__attribute__\0define\0include\0ifdef\0ifndef\0elif\0endif\0defined\0undef\0error\0line\0__LINE__\0__FILE__\0__DATE__\0__TIME__\0__VA_ARGS__\0__func__\0main\0section\0__section__\0aligned\0__aligned__\0unused\0__unused__\0"; while (*p) { r = p; while (*r++); @@ -4615,18 +5518,17 @@ int main(int argc, char **argv) /* tiny C specific defines */ define_symbol("__TINYC__"); - glo = (int)malloc(DATA_SIZE); - memset((void *)glo, 0, DATA_SIZE); - prog = (int)malloc(TEXT_SIZE); - ind = prog; + /* create standard sections */ + text_section = new_section(".text"); + data_section = new_section(".data"); + bss_section = new_section(".bss"); optind = 1; outfile = NULL; while (1) { if (optind >= argc) { show_help: - printf("tcc version 0.9.2 - Tiny C Compiler - Copyright (C) 2001 Fabrice Bellard\n" - "usage: tcc [-Idir] [-Dsym] [-llib] [-i infile] infile [infile_args...]\n"); + help(); return 1; } r = argv[optind]; @@ -4639,6 +5541,8 @@ int main(int argc, char **argv) include_paths[nb_include_paths++] = r + 2; } else if (r[1] == 'D') { define_symbol(r + 2); + } else if (r[1] == 'U') { + undef_symbol(r + 2); } else if (r[1] == 'l') { open_dll(r + 2); } else if (r[1] == 'i') { -- 2.11.4.GIT