added char/short types in lvalues to fix cast problems - added gcc style variadic macros - fixed zero arg macros - refuse extra comma at end of function call - fixed unused logical operation result case

tcc-xref
bellard 2002-07-13 11:17:19 +00:00
parent 4e5a85292b
commit 3d3e2372c5
1 changed files with 177 additions and 101 deletions

278
tcc.c
View File

@ -135,7 +135,6 @@ typedef struct Reloc {
/* section definition */ /* section definition */
typedef struct Section { typedef struct Section {
char name[64]; /* section name */
unsigned char *data; /* section data */ unsigned char *data; /* section data */
unsigned char *data_ptr; /* current data pointer */ unsigned char *data_ptr; /* current data pointer */
int sh_num; /* elf section number */ int sh_num; /* elf section number */
@ -144,6 +143,7 @@ typedef struct Section {
int sh_entsize; /* elf entry size */ int sh_entsize; /* elf entry size */
struct Section *link; /* link to another section */ struct Section *link; /* link to another section */
struct Section *next; struct Section *next;
char name[64]; /* section name */
} Section; } Section;
/* GNUC attribute definition */ /* GNUC attribute definition */
@ -283,12 +283,13 @@ struct TCCState {
#define VT_LVAL 0x0100 /* var is an lvalue */ #define VT_LVAL 0x0100 /* var is an lvalue */
#define VT_FORWARD 0x0200 /* value is forward reference */ #define VT_FORWARD 0x0200 /* value is forward reference */
#define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for #define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for
char/short stored in integer registers) */ char/short stored in integer registers) */
#define VT_MUSTBOUND 0x0800 /* bound checking must be done before #define VT_MUSTBOUND 0x0800 /* bound checking must be done before
dereferencing value */ dereferencing value */
#define VT_LVAL_BYTE 0x1000 /* lvalue is a byte */ #define VT_LVAL_BYTE 0x1000 /* lvalue is a byte */
#define VT_LVAL_SHORT 0x2000 /* lvalue is a short */ #define VT_LVAL_SHORT 0x2000 /* lvalue is a short */
#define VT_LVAL_UNSIGNED 0x4000 /* lvalue is unsigned */ #define VT_LVAL_UNSIGNED 0x4000 /* lvalue is unsigned */
#define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)
/* types */ /* types */
#define VT_STRUCT_SHIFT 16 /* structure/enum name shift (16 bits left) */ #define VT_STRUCT_SHIFT 16 /* structure/enum name shift (16 bits left) */
@ -1466,7 +1467,7 @@ void tok_print(int *str)
void parse_define(void) void parse_define(void)
{ {
Sym *s, *first, **ps; Sym *s, *first, **ps;
int v, t; int v, t, varg, is_vaargs;
TokenString str; TokenString str;
v = tok; v = tok;
@ -1479,12 +1480,21 @@ void parse_define(void)
next_nomacro(); next_nomacro();
ps = &first; ps = &first;
while (tok != ')') { while (tok != ')') {
if (tok == TOK_DOTS) varg = tok;
tok = TOK___VA_ARGS__; next_nomacro();
s = sym_push1(&define_stack, tok | SYM_FIELD, 0, 0); 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_push1(&define_stack, varg | SYM_FIELD, is_vaargs, 0);
*ps = s; *ps = s;
ps = &s->next; ps = &s->next;
next_nomacro();
if (tok != ',') if (tok != ',')
break; break;
next_nomacro(); next_nomacro();
@ -2197,10 +2207,22 @@ int *macro_arg_subst(Sym **nested_list, int *macro_str, Sym *args)
s = sym_find2(args, t); s = sym_find2(args, t);
if (s) { if (s) {
st = (int *)s->c; st = (int *)s->c;
/* if '##' is present before or after , no arg substitution */ /* if '##' is present before or after, no arg substitution */
if (*macro_str == TOK_TWOSHARPS || last_tok == TOK_TWOSHARPS) { if (*macro_str == TOK_TWOSHARPS || last_tok == TOK_TWOSHARPS) {
while (*st) /* special case for var arg macros : ## eats the
tok_str_add(&str, *st++); ',' if empty VA_ARGS riable. */
/* XXX: test of the ',' is not 100%
reliable. should fix it to avoid security
problems */
if (gnu_ext && s->t && *st == 0 &&
last_tok == TOK_TWOSHARPS &&
str.len >= 2&& str.str[str.len - 2] == ',') {
/* suppress ',' '##' */
str.len -= 2;
} else {
while (*st)
tok_str_add(&str, *st++);
}
} else { } else {
macro_subst(&str, nested_list, st); macro_subst(&str, nested_list, st);
} }
@ -2335,10 +2357,10 @@ void macro_subst(TokenString *tok_str,
get_tok_str(s->v, 0)); get_tok_str(s->v, 0));
tok_str_new(&str); tok_str_new(&str);
parlevel = 0; parlevel = 0;
/* NOTE: non zero sa->t indicates VA_ARGS */
while ((parlevel > 0 || while ((parlevel > 0 ||
(tok != ')' && (tok != ')' &&
(tok != ',' || (tok != ',' || sa->t))) &&
sa->v == (TOK___VA_ARGS__ | SYM_FIELD)))) &&
tok != -1) { tok != -1) {
if (tok == '(') if (tok == '(')
parlevel++; parlevel++;
@ -2348,17 +2370,24 @@ void macro_subst(TokenString *tok_str,
next_nomacro(); next_nomacro();
} }
tok_str_add(&str, 0); tok_str_add(&str, 0);
sym_push2(&args, sa->v & ~SYM_FIELD, 0, (int)str.str); sym_push2(&args, sa->v & ~SYM_FIELD, sa->t, (int)str.str);
if (tok == ')') sa = sa->next;
break; if (tok == ')') {
/* special case for gcc var args: add an empty
var arg argument if it is omitted */
if (sa && sa->t && gnu_ext)
continue;
else
break;
}
if (tok != ',') if (tok != ',')
expect(","); expect(",");
next_nomacro(); next_nomacro();
sa = sa->next;
} }
if (sa->next) if (sa) {
error("macro '%s' used with too few args", error("macro '%s' used with too few args",
get_tok_str(s->v, 0)); get_tok_str(s->v, 0));
}
/* now subst each arg */ /* now subst each arg */
mstr = macro_arg_subst(nested_list, mstr, args); mstr = macro_arg_subst(nested_list, mstr, args);
@ -2626,14 +2655,17 @@ void gaddrof(void)
/* generate lvalue bound code */ /* generate lvalue bound code */
void gbound(void) void gbound(void)
{ {
int lval_type;
vtop->r &= ~VT_MUSTBOUND; vtop->r &= ~VT_MUSTBOUND;
/* if lvalue, then use checking code before dereferencing */ /* if lvalue, then use checking code before dereferencing */
if (vtop->r & VT_LVAL) { if (vtop->r & VT_LVAL) {
lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL);
gaddrof(); gaddrof();
vpushi(0); vpushi(0);
gen_bounded_ptr_add1(); gen_bounded_ptr_add1();
gen_bounded_ptr_add2(1); gen_bounded_ptr_add2(1);
vtop->r |= VT_LVAL; vtop->r |= lval_type;
} }
} }
#endif #endif
@ -2730,6 +2762,23 @@ int gv(int rc)
vpop(); vpop();
/* write second register */ /* write second register */
vtop->r2 = r2; vtop->r2 = r2;
} else if ((vtop->r & VT_LVAL) && !is_float(vtop->t)) {
int t1, t;
/* lvalue of scalar type : need to use lvalue type
because of possible cast */
t = vtop->t;
t1 = t;
/* compute memory access type */
if (vtop->r & VT_LVAL_BYTE)
t = VT_BYTE;
else if (vtop->r & VT_LVAL_SHORT)
t = VT_SHORT;
if (vtop->r & VT_LVAL_UNSIGNED)
t |= VT_UNSIGNED;
vtop->t = t;
load(r, vtop);
/* restore wanted type */
vtop->t = t1;
} else { } else {
/* one register type load */ /* one register type load */
load(r, vtop); load(r, vtop);
@ -2806,12 +2855,18 @@ void vrotb(int n)
/* pop stack value */ /* pop stack value */
void vpop(void) void vpop(void)
{ {
int v;
v = vtop->r & VT_VALMASK;
#ifdef TCC_TARGET_I386 #ifdef TCC_TARGET_I386
/* for x86, we need to pop the FP stack */ /* for x86, we need to pop the FP stack */
if ((vtop->r & VT_VALMASK) == REG_ST0) { if (v == REG_ST0) {
o(0xd9dd); /* fstp %st(1) */ o(0xd9dd); /* fstp %st(1) */
} } else
#endif #endif
if (v == VT_JMP || v == VT_JMPI) {
/* need to put correct jump if && or || without test */
gsym(vtop->c.ul);
}
vtop--; vtop--;
} }
@ -3467,7 +3522,7 @@ void force_charshort_cast(int t)
/* cast 'vtop' to 't' type */ /* cast 'vtop' to 't' type */
void gen_cast(int t) void gen_cast(int t)
{ {
int sbt, dbt, sf, df, c, st1, dt1; int sbt, dbt, sf, df, c;
/* special delayed cast for char/short */ /* special delayed cast for char/short */
/* XXX: in some cases (multiple cascaded casts), it may still /* XXX: in some cases (multiple cascaded casts), it may still
@ -3477,8 +3532,8 @@ void gen_cast(int t)
force_charshort_cast(vtop->t); force_charshort_cast(vtop->t);
} }
dbt = t & VT_BTYPE; dbt = t & (VT_BTYPE | VT_UNSIGNED);
sbt = vtop->t & VT_BTYPE; sbt = vtop->t & (VT_BTYPE | VT_UNSIGNED);
if (sbt != dbt) { if (sbt != dbt) {
sf = is_float(sbt); sf = is_float(sbt);
@ -3507,9 +3562,8 @@ void gen_cast(int t)
} }
} else if (df) { } else if (df) {
/* convert int to fp */ /* convert int to fp */
st1 = vtop->t & (VT_BTYPE | VT_UNSIGNED);
if (c) { if (c) {
switch(st1) { switch(sbt) {
case VT_LLONG | VT_UNSIGNED: case VT_LLONG | VT_UNSIGNED:
case VT_LLONG: case VT_LLONG:
/* XXX: add const cases for long long */ /* XXX: add const cases for long long */
@ -3535,14 +3589,13 @@ void gen_cast(int t)
} }
} else if (sf) { } else if (sf) {
/* convert fp to int */ /* convert fp to int */
dt1 = t & (VT_BTYPE | VT_UNSIGNED);
/* we handle char/short/etc... with generic code */ /* we handle char/short/etc... with generic code */
if (dt1 != (VT_INT | VT_UNSIGNED) && if (dbt != (VT_INT | VT_UNSIGNED) &&
dt1 != (VT_LLONG | VT_UNSIGNED) && dbt != (VT_LLONG | VT_UNSIGNED) &&
dt1 != VT_LLONG) dbt != VT_LLONG)
dt1 = VT_INT; dbt = VT_INT;
if (c) { if (c) {
switch(dt1) { switch(dbt) {
case VT_LLONG | VT_UNSIGNED: case VT_LLONG | VT_UNSIGNED:
case VT_LLONG: case VT_LLONG:
/* XXX: add const cases for long long */ /* XXX: add const cases for long long */
@ -3565,59 +3618,58 @@ void gen_cast(int t)
} }
} else { } else {
do_ftoi: do_ftoi:
gen_cvt_ftoi1(dt1); gen_cvt_ftoi1(dbt);
} }
if (dt1 == VT_INT && (t & (VT_BTYPE | VT_UNSIGNED)) != dt1) { if (dbt == VT_INT && (t & (VT_BTYPE | VT_UNSIGNED)) != dbt) {
/* additionnal cast for char/short/bool... */ /* additionnal cast for char/short/bool... */
vtop->t = dt1; vtop->t = dbt;
gen_cast(t); gen_cast(t);
} }
} else if (dbt == VT_LLONG) { } else if ((dbt & VT_BTYPE) == VT_LLONG) {
/* scalar to long long */ if ((sbt & VT_BTYPE) != VT_LLONG) {
if (c) { /* scalar to long long */
if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) if (c) {
vtop->c.ll = vtop->c.ui; if (sbt == (VT_INT | VT_UNSIGNED))
else vtop->c.ll = vtop->c.ui;
vtop->c.ll = vtop->c.i; else
} else { vtop->c.ll = vtop->c.i;
/* 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 { } else {
gv_dup(); /* machine independant conversion */
vpushi(31); gv(RC_INT);
gen_op(TOK_SAR); /* generate high word */
if (sbt == (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();
} }
/* patch second register */
vtop[-1].r2 = vtop->r;
vpop();
} }
} else if (dbt == VT_BOOL) { } else if (dbt == VT_BOOL) {
/* scalar to bool */ /* scalar to bool */
vpushi(0); vpushi(0);
gen_op(TOK_NE); gen_op(TOK_NE);
} else if (dbt == VT_BYTE || dbt == VT_SHORT) { } else if ((dbt & VT_BTYPE) == VT_BYTE ||
(dbt & VT_BTYPE) == VT_SHORT) {
force_charshort_cast(t); force_charshort_cast(t);
} else if (dbt == VT_INT) { } else if ((dbt & VT_BTYPE) == VT_INT) {
/* scalar to int */ /* scalar to int */
if (sbt == VT_LLONG) { if (sbt == VT_LLONG) {
/* from long long: just take low order word */ /* from long long: just take low order word */
lexpand(); lexpand();
vpop(); vpop();
} else if (sbt == VT_PTR) { }
/* ok to cast */ /* if lvalue and single word type, nothing to do because
} else if (vtop->r & VT_LVAL) { the lvalue already contains the real type size (see
/* if lvalue and single word type, nothing to do (XXX: VT_LVAL_xxx constants) */
maybe incorrect for sizeof op) */
goto no_cast;
}
} }
} }
vtop->t = t; vtop->t = t;
no_cast: ;
} }
/* return type size. Put alignment at 'a' */ /* return type size. Put alignment at 'a' */
@ -3698,7 +3750,7 @@ int is_compatible_types(int t1, int t2)
if (t1 == VT_VOID || t2 == VT_VOID) if (t1 == VT_VOID || t2 == VT_VOID)
return 1; return 1;
return is_compatible_types(t1, t2); return is_compatible_types(t1, t2);
} else if (bt1 == VT_STRUCT) { } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
return (t2 == t1); return (t2 == t1);
} else if (bt1 == VT_FUNC) { } else if (bt1 == VT_FUNC) {
if (bt2 != VT_FUNC) if (bt2 != VT_FUNC)
@ -3877,7 +3929,6 @@ void vstore(void)
/* if structure, only generate pointer */ /* if structure, only generate pointer */
/* structure assignment : generate memcpy */ /* structure assignment : generate memcpy */
/* XXX: optimize if small size */ /* XXX: optimize if small size */
vdup(); vdup();
gfunc_start(&gf, FUNC_CDECL); gfunc_start(&gf, FUNC_CDECL);
/* type size */ /* type size */
@ -4488,7 +4539,25 @@ Sym *external_sym(int v, int u, int r)
return s; return s;
} }
void indir(void) /* compute the lvalue VT_LVAL_xxx needed to match type t. */
static int lvalue_type(int t)
{
int bt, r;
r = VT_LVAL;
bt = t & VT_BTYPE;
if (bt == VT_BYTE)
r |= VT_LVAL_BYTE;
else if (bt == VT_SHORT)
r |= VT_LVAL_SHORT;
else
return r;
if (t & VT_UNSIGNED)
r |= VT_LVAL_UNSIGNED;
return r;
}
/* indirection with full error checking and bound check */
static void indir(void)
{ {
if ((vtop->t & VT_BTYPE) != VT_PTR) if ((vtop->t & VT_BTYPE) != VT_PTR)
expect("pointer"); expect("pointer");
@ -4497,7 +4566,7 @@ void indir(void)
vtop->t = pointed_type(vtop->t); vtop->t = pointed_type(vtop->t);
/* an array is never an lvalue */ /* an array is never an lvalue */
if (!(vtop->t & VT_ARRAY)) { if (!(vtop->t & VT_ARRAY)) {
vtop->r |= VT_LVAL; vtop->r |= lvalue_type(vtop->t);
/* if bound checking, the referenced pointer must be checked */ /* if bound checking, the referenced pointer must be checked */
if (do_bounds_check) if (do_bounds_check)
vtop->r |= VT_MUSTBOUND; vtop->r |= VT_MUSTBOUND;
@ -4594,7 +4663,7 @@ void unary(void)
r = VT_LOCAL; r = VT_LOCAL;
/* all except arrays are lvalues */ /* all except arrays are lvalues */
if (!(ft & VT_ARRAY)) if (!(ft & VT_ARRAY))
r |= VT_LVAL; r |= lvalue_type(ft);
memset(&ad, 0, sizeof(AttributeDef)); memset(&ad, 0, sizeof(AttributeDef));
fc = decl_initializer_alloc(ft, &ad, r, 1); fc = decl_initializer_alloc(ft, &ad, r, 1);
vset(ft, r, fc); vset(ft, r, fc);
@ -4615,7 +4684,9 @@ void unary(void)
except for unary '&' and sizeof. Since we consider that except for unary '&' and sizeof. Since we consider that
functions are not lvalues, we only have to handle it functions are not lvalues, we only have to handle it
there and in function calls. */ there and in function calls. */
if ((vtop->t & VT_BTYPE) != VT_FUNC) /* arrays can also be used although they are not lvalues */
if ((vtop->t & VT_BTYPE) != VT_FUNC &&
!(vtop->t & VT_ARRAY))
test_lvalue(); test_lvalue();
vtop->t = mk_pointer(vtop->t); vtop->t = mk_pointer(vtop->t);
gaddrof(); gaddrof();
@ -4666,6 +4737,8 @@ void unary(void)
gen_op('-'); gen_op('-');
} else } else
{ {
if (t < TOK_UIDENT)
expect("identifier");
s = sym_find(t); s = sym_find(t);
if (!s) { if (!s) {
if (tok != '(') if (tok != '(')
@ -4716,7 +4789,7 @@ void unary(void)
vtop->t = s->t; vtop->t = s->t;
/* an array is never an lvalue */ /* an array is never an lvalue */
if (!(vtop->t & VT_ARRAY)) if (!(vtop->t & VT_ARRAY))
vtop->r |= VT_LVAL; vtop->r |= lvalue_type(vtop->t);
next(); next();
} else if (tok == '[') { } else if (tok == '[') {
next(); next();
@ -4758,30 +4831,30 @@ void unary(void)
/* read each argument and store it on a stack */ /* read each argument and store it on a stack */
/* XXX: merge it with macro args ? */ /* XXX: merge it with macro args ? */
args = NULL; args = NULL;
while (tok != ')') { if (tok != ')') {
tok_str_new(&str); for(;;) {
parlevel = 0; tok_str_new(&str);
while ((parlevel > 0 || (tok != ')' && tok != ',')) && parlevel = 0;
tok != -1) { while ((parlevel > 0 || (tok != ')' && tok != ',')) &&
if (tok == '(') tok != -1) {
parlevel++; if (tok == '(')
else if (tok == ')') parlevel++;
parlevel--; else if (tok == ')')
tok_str_add_tok(&str); parlevel--;
next(); tok_str_add_tok(&str);
next();
}
tok_str_add(&str, -1); /* end of file added */
tok_str_add(&str, 0);
s1 = sym_push2(&args, 0, 0, (int)str.str);
s1->next = sa; /* add reference to argument */
if (sa)
sa = sa->next;
if (tok == ')')
break;
skip(',');
} }
tok_str_add(&str, -1); /* end of file added */
tok_str_add(&str, 0);
s1 = sym_push2(&args, 0, 0, (int)str.str);
s1->next = sa; /* add reference to argument */
if (sa)
sa = sa->next;
if (tok != ',')
break;
next();
} }
if (tok != ')')
expect(")");
/* now generate code in reverse order by reading the stack */ /* now generate code in reverse order by reading the stack */
save_parse_state(&saved_parse_state); save_parse_state(&saved_parse_state);
@ -4826,13 +4899,16 @@ void unary(void)
ret.c.i = 0; ret.c.i = 0;
} }
#ifndef INVERT_FUNC_PARAMS #ifndef INVERT_FUNC_PARAMS
while (tok != ')') { if (tok != ')') {
expr_eq(); for(;;) {
gfunc_param_typed(&gf, s, sa); expr_eq();
if (sa) gfunc_param_typed(&gf, s, sa);
sa = sa->next; if (sa)
if (tok == ',') sa = sa->next;
next(); if (tok == ')')
break;
skip(',');
}
} }
#endif #endif
if (sa) if (sa)
@ -5934,7 +6010,7 @@ void decl(int l)
/* not lvalue if array */ /* not lvalue if array */
r = 0; r = 0;
if (!(t & VT_ARRAY)) if (!(t & VT_ARRAY))
r |= VT_LVAL; r |= lvalue_type(t);
if (b & VT_EXTERN) { if (b & VT_EXTERN) {
/* external variable */ /* external variable */
external_sym(v, t, r); external_sym(v, t, r);