radare2/shlr/tcc/tccgen.c
2020-04-03 13:07:00 +02:00

3221 lines
73 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
*/
#include "tcc.h"
#define TCC_ERR(...) do { \
tcc_error (__VA_ARGS__); \
return; \
} while (0)
/* callback pointer */
ST_DATA char **tcc_cb_ptr;
/********************************************************/
/* global variables */
/* loc : local variable index
ind : output code index
rsym: return symbol
anon_sym: anonymous symbol index
*/
ST_DATA int rsym, anon_sym = SYM_FIRST_ANOM, ind, loc;
ST_DATA Sym *sym_free_first;
ST_DATA void **sym_pools;
ST_DATA int nb_sym_pools;
static int arraysize = 0;
static const char *global_symname = NULL;
static const char *global_type = NULL;
ST_DATA Sym *global_stack;
ST_DATA Sym *local_stack;
ST_DATA Sym *scope_stack_bottom;
ST_DATA Sym *define_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *local_label_stack;
ST_DATA int vla_sp_loc_tmp; /* vla_sp_loc is set to this when the value won't be needed later */
ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */
ST_DATA int *vla_sp_loc;/* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */
ST_DATA int vla_flags; /* VLA_* flags */
ST_DATA SValue __vstack[1 + VSTACK_SIZE], *vtop;
ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_vc;
ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
ST_DATA char *funcname;
ST_DATA char *dirname;
ST_DATA CType char_pointer_type, func_old_type;
ST_DATA CType int8_type, int16_type, int32_type, int64_type, size_type;
/* ------------------------------------------------------------------------- */
static inline CType *pointed_type(CType *type);
static int is_compatible_types(CType *type1, CType *type2);
static int parse_btype(CType *type, AttributeDef *ad);
static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
static void parse_expr_type(CType *type);
static void decl_initializer(CType *type, unsigned long c, int first, int size_only);
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, char *asm_label, int scope);
static int decl0(int l, int is_for_loop_init);
static void expr_eq(void);
static void unary_type(CType *type);
static int is_compatible_parameter_types(CType *type1, CType *type2);
static void expr_type(CType *type);
/* ------------------------------------------------------------------------- */
ST_INLN bool is_structured(CType *t) {
return (t->t & VT_BTYPE) == VT_STRUCT || (t->t & VT_BTYPE) == VT_UNION;
}
ST_INLN bool is_struct(CType *t) {
return (t->t & VT_BTYPE) == VT_STRUCT;
}
ST_INLN bool is_union(CType *t) {
return (t->t & VT_BTYPE) == VT_UNION;
}
ST_INLN bool is_enum(CType *t) {
return (t->t & VT_BTYPE) == VT_ENUM;
}
ST_INLN bool is_float(int t) {
int bt;
bt = t & VT_BTYPE;
return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT || bt == VT_QFLOAT;
}
ST_INLN bool not_structured(CType *t) {
return (t->t & VT_BTYPE) != VT_STRUCT && (t->t & VT_BTYPE) != VT_UNION;
}
/* ------------------------------------------------------------------------- */
/* we use our own 'finite' function to avoid potential problems with
non standard math libs */
/* XXX: endianness dependent */
ST_FUNC int ieee_finite(double d) {
int *p = (int *) &d;
return ((unsigned) ((p[1] | 0x800fffff) + 1)) >> 31;
}
ST_FUNC void test_lvalue(void) {
if (!(vtop->r & VT_LVAL)) {
expect ("lvalue");
}
}
/* ------------------------------------------------------------------------- */
/* symbol allocator */
static Sym *__sym_malloc(void) {
Sym *sym_pool, *sym, *last_sym;
int i;
int sym_pool_size = SYM_POOL_NB * sizeof(Sym);
sym_pool = malloc (sym_pool_size);
memset (sym_pool, 0, sym_pool_size);
dynarray_add (&sym_pools, &nb_sym_pools, sym_pool);
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;
}
ST_INLN void sym_free(Sym *sym) {
sym->next = sym_free_first;
free (sym->asm_label);
sym_free_first = sym;
}
/* push, without hashing */
ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long long c) {
Sym *s;
if (ps == &local_stack) {
for (s = *ps; s && s != scope_stack_bottom; s = s->prev) {
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM && s->v == v) {
tcc_error ("incompatible types for redefinition of '%s'",
get_tok_str (v, NULL));
return NULL;
}
}
}
// printf (" %d %ld set symbol '%s'\n", t, c, get_tok_str(v, NULL));
// s = *ps;
s = sym_malloc ();
s->asm_label = NULL;
s->v = v;
s->type.t = t;
s->type.ref = NULL;
#ifdef _WIN64
s->d = NULL;
#endif
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 */
ST_FUNC Sym *sym_find2(Sym *s, int v) {
while (s) {
if (s->v == v) {
return s;
}
s = s->prev;
}
return NULL;
}
/* structure lookup */
ST_INLN 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 */
ST_INLN Sym *sym_find(int v) {
v -= TOK_IDENT;
if ((unsigned) v >= (unsigned) (tok_ident - TOK_IDENT)) {
return NULL;
}
return table_ident[v]->sym_identifier;
}
// TODO: Add better way to store the meta information
// about the pushed type
int tcc_sym_push(char *typename, int typesize, int meta) {
CType *new_type = (CType *) malloc (sizeof(CType));
if (!new_type) {
return 0;
}
new_type->ref = sym_malloc ();
new_type->t = meta;
if (!sym_push (0, new_type, 0, 0)) {
return 0;
}
free (new_type);
return 1;
}
void dump_type(CType *type, int depth) {
if (depth <= 0) {
return;
}
eprintf ("------------------------\n");
int bt = type->t & VT_BTYPE;
eprintf ("BTYPE = %d ", bt);
switch (bt) {
case VT_UNION: eprintf ("[UNION]\n");
break;
case VT_STRUCT: eprintf ("[STRUCT]\n");
break;
case VT_PTR: eprintf ("[PTR]\n");
break;
case VT_ENUM: eprintf ("[ENUM]\n");
break;
case VT_INT64: eprintf ("[INT64_T]\n");
break;
case VT_INT32: eprintf ("[INT32_T]\n");
break;
case VT_INT16: eprintf ("[INT16_T]\n");
break;
case VT_INT8: eprintf ("[INT8_T]\n");
break;
default:
eprintf ("\n");
break;
}
if (type->ref) {
eprintf ("v = %d\n", type->ref->v);
char *varstr = NULL;
varstr = get_tok_str (type->ref->v, NULL);
if (varstr) {
eprintf ("var = %s\n", varstr);
}
if (type->ref->asm_label) {
eprintf ("asm_label = %s\n", type->ref->asm_label);
}
eprintf ("r = %d\n", type->ref->r);
eprintf ("associated type:\n");
// dump_type(&(type->ref->type), --depth);
}
}
/* push a given symbol on the symbol stack */
ST_FUNC Sym *sym_push(int v, CType *type, int r, long long c) {
Sym *s, **ps;
TokenSym *ts;
if (local_stack) {
ps = &local_stack;
} else {
ps = &global_stack;
}
// dump_type(type, 5);
s = sym_push2 (ps, v, type->t, c);
if (!s) {
return NULL;
}
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) {
int i = (v & ~SYM_STRUCT);
if (i < TOK_IDENT) {
eprintf ("Not found\n");
return NULL;
}
// ts = table_ident[i - TOK_IDENT];
/* record symbol in token array */
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 */
ST_FUNC Sym *global_identifier_push(int v, int t, long long c) {
Sym *s, **ps;
s = sym_push2 (&global_stack, v, t, c);
/* don't record anonymous symbol */
if (s && v < SYM_FIRST_ANOM) {
int i = (v & ~SYM_STRUCT);
if (i < TOK_IDENT) {
eprintf ("Not found\n");
return NULL;
}
ps = &table_ident[i - TOK_IDENT]->sym_identifier;
/* modify the top most local identifier, so that
sym_identifier will point to 's' when popped */
while (*ps) {
ps = &(*ps)->prev_tok;
}
s->prev_tok = NULL;
*ps = s;
}
return s;
}
/* pop symbols until top reaches 'b' */
ST_FUNC void sym_pop(Sym **ptop, Sym *b) {
Sym *s, *ss, **ps;
TokenSym *ts;
int v;
if (!b) {
return;
}
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) {
int i = (v & ~SYM_STRUCT);
if (i < TOK_IDENT) {
eprintf ("Not found\n");
return;
}
ts = table_ident[i - TOK_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;
}
static void weaken_symbol(Sym *sym) {
sym->type.t |= VT_WEAK;
}
/* ------------------------------------------------------------------------- */
ST_FUNC void swap(int *p, int *q) {
int t;
t = *p;
*p = *q;
*q = t;
}
static void vsetc(CType *type, int r, CValue *vc) {
if (vtop >= vstack + (VSTACK_SIZE - 1)) {
TCC_ERR ("memory full");
}
vtop++;
vtop->type = *type;
vtop->r = r;
vtop->r2 = VT_CONST;
vtop->c = *vc;
}
/* push constant of type "type" with useless value */
void vpush(CType *type) {
CValue cval = { 0 };
vsetc (type, VT_CONST, &cval);
}
/* push integer constant */
ST_FUNC void vpushi(int v) {
CValue cval = { 0 };
cval.i = v;
vsetc (&int32_type, VT_CONST, &cval);
}
/* push a pointer sized constant */
static void vpushs(long long v) {
CValue cval;
if (PTR_SIZE == 4) {
cval.i = (int) v;
} else {
cval.ull = v;
}
vsetc (&size_type, VT_CONST, &cval);
}
/* push arbitrary 64 bit constant */
void vpush64(int ty, unsigned long long v) {
CValue cval;
CType ctype;
ctype.t = ty;
ctype.ref = NULL;
cval.ull = v;
vsetc (&ctype, VT_CONST, &cval);
}
/* push long long constant */
ST_FUNC void vpushll(long long v) {
CValue cval;
cval.ll = v;
vsetc (&int64_type, VT_CONST, &cval);
}
ST_FUNC void vset(CType *type, int r, int v) {
CValue cval;
cval.i = v;
vsetc (type, r, &cval);
}
static void vseti(int r, int v) {
CType type = { 0 };
type.t = VT_INT32;
type.ref = NULL;
vset (&type, r, v);
}
ST_FUNC void vswap(void) {
SValue tmp;
/* cannot let cpu flags if other instruction are generated. Also
avoid leaving VT_JMP anywhere except on the top of the stack
because it would complicate the code generator. */
tmp = vtop[0];
vtop[0] = vtop[-1];
vtop[-1] = tmp;
/* XXX: +2% overall speed possible with optimized memswap
*
* memswap(&vtop[0], &vtop[1], sizeof *vtop);
*/
}
ST_FUNC void vpushv(SValue *v) {
if (vtop >= vstack + (VSTACK_SIZE - 1)) {
TCC_ERR ("memory full");
}
vtop++;
*vtop = *v;
}
static void vdup(void) {
vpushv (vtop);
}
/* get address of vtop (vtop MUST BE an lvalue) */
static void gaddrof(void) {
vtop->r &= ~VT_LVAL;
/* tricky: if saved lvalue, then we can go back to lvalue */
if ((vtop->r & VT_VALMASK) == VT_LLOCAL) {
vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL;
}
}
static int pointed_size(CType *type) {
int align;
return type_size (pointed_type (type), &align);
}
static inline int is_integer_btype(int bt) {
return bt == VT_INT8 || bt == VT_INT16 || bt == VT_INT32 || bt == VT_INT64;
}
/* return type size as known at compile time. Put alignment at 'a' */
ST_FUNC int type_size(CType *type, int *a) {
Sym *s;
int bt;
bt = type->t & VT_BTYPE;
if (is_structured(type)) {
/* struct/union */
s = type->ref;
*a = s->r;
return s->c;
} else if (bt == VT_PTR) {
if (type->t & VT_ARRAY) {
int ts;
s = type->ref;
ts = type_size (&s->type, a);
if (ts < 0 && s->c < 0) {
ts = -ts;
}
return ts * s->c;
} else {
*a = PTR_SIZE;
return PTR_SIZE;
}
} else if (bt == VT_LDOUBLE) {
*a = LDOUBLE_ALIGN;
return LDOUBLE_SIZE;
} else if (bt == VT_DOUBLE || bt == VT_INT64) {
if (!strncmp (tcc_state->arch, "x86", 3) && tcc_state->bits == 32) {
if (!strncmp (tcc_state->os, "windows", 7)) {
*a = 8;
} else {
*a = 4;
}
} else if (!strncmp (tcc_state->arch, "arm", 3)) {
/* It was like originally:
#ifdef TCC_ARM_EABI
*a = 8;
#else
*a = 4;
#endif
FIXME: Determine EABI then too
*/
*a = 8;
} else {
*a = 8;
}
return 8;
} else if (bt == VT_ENUM) {
/* Non standard, but still widely used
* and implemented in GCC, MSVC */
*a = 8;
return 8;
} else if (bt == VT_INT32 || bt == VT_FLOAT) {
*a = 4;
return 4;
} else if (bt == VT_INT16) {
*a = 2;
return 2;
} else if (bt == VT_QLONG || bt == VT_QFLOAT) {
*a = 8;
return 16;
} else {
/* char, void, function, _Bool */
*a = 1;
return 1;
}
}
/* return the pointed type of t */
static inline CType *pointed_type(CType *type) {
return &type->ref->type;
}
/* modify type so that its it is a pointer to type. */
ST_FUNC void mk_pointer(CType *type) {
Sym *s;
s = sym_push (SYM_FIELD, type, 0, -1);
if (!s) {
return;
}
type->t = VT_PTR | (type->t & ~VT_TYPE);
type->ref = s;
}
/* compare function types. OLD functions match any new functions */
static int is_compatible_func(CType *type1, CType *type2) {
Sym *s1, *s2;
s1 = type1->ref;
s2 = type2->ref;
if (!is_compatible_types (&s1->type, &s2->type)) {
return 0;
}
/* check func_call */
if (FUNC_CALL (s1->r) != FUNC_CALL (s2->r)) {
return 0;
}
/* XXX: not complete */
if (s1->c == FUNC_OLD || s2->c == FUNC_OLD) {
return 1;
}
if (s1->c != s2->c) {
return 0;
}
while (s1 != NULL) {
if (s2 == NULL) {
return 0;
}
if (!is_compatible_parameter_types (&s1->type, &s2->type)) {
return 0;
}
s1 = s1->next;
s2 = s2->next;
}
if (s2) {
return 0;
}
return 1;
}
/* return true if type1 and type2 are the same. If unqualified is
true, qualifiers on the types are ignored.
- enums are not checked as gcc __builtin_types_compatible_p ()
*/
static int compare_types(CType *type1, CType *type2, int unqualified) {
int t1 = type1->t & VT_TYPE;
int t2 = type2->t & VT_TYPE;
if (unqualified) {
/* strip qualifiers before comparing */
t1 &= ~(VT_CONSTANT | VT_VOLATILE);
t2 &= ~(VT_CONSTANT | VT_VOLATILE);
}
/* XXX: bitfields ? */
if (t1 != t2) {
return 0;
}
/* test more complicated cases */
int bt1 = t1 & VT_BTYPE;
if (bt1 == VT_PTR) {
type1 = pointed_type (type1);
type2 = pointed_type (type2);
return is_compatible_types (type1, type2);
} else if (bt1 == VT_STRUCT || bt1 == VT_UNION) {
return type1->ref == type2->ref;
} else if (bt1 == VT_FUNC) {
return is_compatible_func (type1, type2);
} else {
return 1;
}
}
/* return true if type1 and type2 are exactly the same (including
qualifiers).
*/
static int is_compatible_types(CType *type1, CType *type2) {
return compare_types (type1, type2, 0);
}
/* return true if type1 and type2 are the same (ignoring qualifiers).
*/
static int is_compatible_parameter_types(CType *type1, CType *type2) {
return compare_types (type1, type2, 1);
}
/* 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 */
static void type_to_str(char *buf, int buf_size, CType *type, const char *varstr) {
int bt, v, t;
Sym *s, *sa;
char buf1[256];
const char *tstr;
t = type->t & VT_TYPE;
bt = t & VT_BTYPE;
buf[0] = '\0';
if (t & VT_CONSTANT) {
pstrcat (buf, buf_size, "const ");
}
if (t & VT_VOLATILE) {
pstrcat (buf, buf_size, "volatile ");
}
switch (bt) {
case VT_VOID:
tstr = "void";
goto add_tstr;
case VT_BOOL:
tstr = "bool";
goto add_tstr;
case VT_INT8:
if (t & VT_UNSIGNED) {
tstr = "uint8_t";
} else {
if (t & VT_CHAR) {
tstr = "char";
} else {
tstr = "int8_t";
}
}
goto add_tstr;
case VT_INT16:
if (t & VT_UNSIGNED) {
tstr = "uint16_t";
} else {
tstr = "int16_t";
}
goto add_tstr;
case VT_INT32:
if (t & VT_UNSIGNED) {
tstr = "uint32_t";
} else {
tstr = "int32_t";
}
goto add_tstr;
case VT_LONG:
tstr = "long";
goto add_tstr;
case VT_INT64:
if (t & VT_UNSIGNED) {
tstr = "uint64_t";
} else {
tstr = "int64_t";
}
goto add_tstr;
case VT_FLOAT:
tstr = "float";
goto add_tstr;
case VT_DOUBLE:
tstr = "double";
goto add_tstr;
case VT_LDOUBLE:
tstr = "long double";
add_tstr:
pstrcat (buf, buf_size, tstr);
if ((t & VT_UNSIGNED) && (bt != VT_INT8) &&
(bt != VT_INT16) && (bt != VT_INT32) &&
(bt != VT_INT64)) {
pstrcat (buf, buf_size, "unsigned ");
}
break;
case VT_ENUM:
case VT_STRUCT:
case VT_UNION:
if (bt == VT_STRUCT) {
tstr = "struct ";
} else if (bt == VT_UNION) {
tstr = "union ";
} else {
tstr = "enum ";
}
pstrcat (buf, buf_size, tstr);
v = type->ref->v & ~SYM_STRUCT;
if (v >= SYM_FIRST_ANOM) {
strcat_printf (buf, buf_size, "%u", v - SYM_FIRST_ANOM);
} else {
pstrcat (buf, buf_size, get_tok_str (v, NULL));
}
break;
case VT_FUNC:
s = type->ref;
type_to_str (buf, buf_size, &s->type, varstr);
pstrcat (buf, buf_size, "(");
sa = s->next;
while (sa != NULL) {
type_to_str (buf1, sizeof(buf1), &sa->type, NULL);
pstrcat (buf, buf_size, buf1);
sa = sa->next;
if (sa) {
pstrcat (buf, buf_size, ", ");
}
}
pstrcat (buf, buf_size, ")");
goto no_var;
case VT_PTR:
s = type->ref;
if (t & VT_ARRAY) {
type_to_str (buf, buf_size, &s->type, NULL);
} else {
pstrcpy (buf1, sizeof(buf1), "*");
if (varstr) {
pstrcat (buf1, sizeof(buf1), varstr);
}
type_to_str (buf, buf_size, &s->type, buf1);
}
goto no_var;
}
if (varstr) {
pstrcat (buf, buf_size, " ");
pstrcat (buf, buf_size, varstr);
}
no_var:
;
}
/* Parse GNUC __attribute__ extension. Currently, the following
extensions are recognized:
- aligned(n) : set data/function alignment.
- packed : force data alignment to 1
- unused : currently ignored, but may be used someday.
- regparm(n) : pass function parameters in registers (i386 only)
*/
static void parse_attribute(AttributeDef *ad) {
int t;
long long n;
while (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) {
next ();
skip ('(');
skip ('(');
while (tok != ')') {
if (tok < TOK_IDENT) {
expect ("attribute name");
}
t = tok;
next ();
switch (t) {
case TOK_ALIAS1:
case TOK_ALIAS2:
skip ('(');
if (tok != TOK_STR) {
expect ("alias(\"target\")");
}
ad->alias_target = /* save string as token, for later */
tok_alloc ((char *) tokc.cstr->data, tokc.cstr->size - 1)->tok;
next ();
skip (')');
break;
case TOK_ALIGNED1:
case TOK_ALIGNED2:
if (tok == '(') {
next ();
n = expr_const ();
if (n <= 0 || (n & (n - 1)) != 0) {
TCC_ERR ("alignment must be a positive power of two");
}
skip (')');
} else {
n = MAX_ALIGN;
}
ad->aligned = n;
break;
case TOK_PACKED1:
case TOK_PACKED2:
ad->packed = 1;
break;
case TOK_WEAK1:
case TOK_WEAK2:
ad->weak = 1;
break;
case TOK_UNUSED1:
case TOK_UNUSED2:
/* currently, no need to handle it because tcc does not
track unused objects */
break;
case TOK_NORETURN1:
case TOK_NORETURN2:
/* currently, no need to handle it because tcc does not
track unused objects */
break;
case TOK_CDECL1:
case TOK_CDECL2:
case TOK_CDECL3:
ad->func_call = FUNC_CDECL;
break;
case TOK_STDCALL1:
case TOK_STDCALL2:
case TOK_STDCALL3:
ad->func_call = FUNC_STDCALL;
break;
#ifdef TCC_TARGET_I386
case TOK_REGPARM1:
case TOK_REGPARM2:
skip ('(');
n = expr_const ();
if (n > 3) {
n = 3;
} else if (n < 0) {
n = 0;
}
if (n > 0) {
ad->func_call = FUNC_FASTCALL1 + n - 1;
}
skip (')');
break;
case TOK_FASTCALL1:
case TOK_FASTCALL2:
case TOK_FASTCALL3:
ad->func_call = FUNC_FASTCALLW;
break;
#endif
case TOK_MODE:
skip ('(');
switch (tok) {
case TOK_MODE_DI:
ad->mode = VT_INT64 + 1;
break;
case TOK_MODE_HI:
ad->mode = VT_INT16 + 1;
break;
case TOK_MODE_SI:
ad->mode = VT_INT32 + 1;
break;
default:
tcc_warning ("__mode__(%s) not supported\n", get_tok_str (tok, NULL));
break;
}
next ();
skip (')');
break;
case TOK_DLLEXPORT:
ad->func_export = 1;
break;
case TOK_DLLIMPORT:
ad->func_import = 1;
break;
default:
if (tcc_state->warn_unsupported) {
tcc_warning ("'%s' attribute ignored", get_tok_str (t, NULL));
}
/* skip parameters */
if (tok == '(') {
int parenthesis = 0;
do {
if (tok == '(') {
parenthesis++;
} else if (tok == ')') {
parenthesis--;
}
next ();
} while (parenthesis && tok != -1);
}
break;
}
if (tok != ',') {
break;
}
next ();
}
skip (')');
skip (')');
}
}
/* enum/struct/union declaration. u is either VT_ENUM, VT_STRUCT or VT_UNION */
static void struct_decl(CType *type, int u) {
int a, v, size, align, maxalign, offset;
long long c = 0;
int bit_size, bit_pos, bsize, bt, lbit_pos, prevbt;
char buf[STRING_MAX_SIZE + 1];
Sym *s, *ss, *ass, **ps;
AttributeDef ad;
const char *name = NULL;
STACK_NEW0 (CType, type1);
STACK_NEW0 (CType, btype);
a = tok;/* save decl type */
next ();
name = get_tok_str (tok, NULL);
if (tok != '{') {
v = tok;
next ();
/* struct already defined ? return it */
if (v < TOK_IDENT) {
expect ("struct/union/enum name");
}
s = struct_find (v);
if (s) {
if (s->type.t != a) {
TCC_ERR ("invalid type");
}
goto do_decl;
}
} else {
v = anon_sym++;
snprintf (buf, sizeof(buf), "%u", v - SYM_FIRST_ANOM);
name = buf;
}
type1.t = a;
/* we put an undefined size for struct/union/enum */
s = sym_push (v | SYM_STRUCT, &type1, 0, -1);
if (!s) {
return;
}
s->r = 0; /* default alignment is zero as gcc */
/* put struct/union/enum name in type */
/* TODO: Extract this part into the separate functions per type */
do_decl:
type->t = u;
type->ref = s;
if (tok == '{') {
next ();
if (s->c != -1) {
TCC_ERR ("struct/union/enum already defined");
}
/* cannot be empty */
c = 0LL;
/* non empty enums are not allowed */
if (a == TOK_ENUM) {
if (!strcmp (name, "{")) {
// UNNAMED
fprintf (stderr, "anonymous enums are ignored\n");
}
while (tcc_nerr () == 0) {
v = tok;
if (v < TOK_UIDENT) {
expect ("identifier");
}
next ();
if (tok == '=') {
next ();
c = expr_const ();
}
if (strcmp (name, "{")) {
char *varstr = get_tok_str (v, NULL);
tcc_appendf ("%s=enum\n", name);
tcc_appendf ("[+]enum.%s=%s\n",name, varstr);
tcc_appendf ("enum.%s.%s=0x%"PFMT64x "\n", name, varstr, c);
tcc_appendf ("enum.%s.0x%"PFMT64x "=%s\n", name, c, varstr);
// TODO: if token already defined throw an error
// if (varstr isInside (arrayOfvars)) { erprintf ("ERROR: DUP VAR IN ENUM\n"); }
}
/* enum symbols have static storage */
ss = sym_push (v, &int64_type, VT_CONST, c);
if (!ss) {
return;
}
ss->type.t |= VT_STATIC;
if (tok != ',') {
break;
}
next ();
c++;
/* NOTE: we accept a trailing comma */
if (tok == '}') {
break;
}
}
skip ('}');
} else {
maxalign = 1;
ps = &s->next;
prevbt = VT_INT32;
bit_pos = 0;
offset = 0;
const char *ctype = (a == TOK_UNION)? "union": "struct";
tcc_appendf ("%s=%s\n", name, ctype);
while (tok != '}') {
if (!parse_btype (&btype, &ad)) {
expect ("bracket");
break;
}
while (tcc_nerr () == 0) {
bit_size = -1;
v = 0;
memcpy (&type1, &btype, sizeof(type1));
if (tok != ':') {
type_decl (&type1, &ad, &v, TYPE_DIRECT | TYPE_ABSTRACT);
if (v == 0 && not_structured(&type1)) {
expect ("identifier");
}
if ((type1.t & VT_BTYPE) == VT_FUNC ||
(type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE))) {
TCC_ERR ("invalid type for '%s'",
get_tok_str (v, NULL));
}
}
if (tok == ':') {
next ();
bit_size = (int) expr_const ();
/* XXX: handle v = 0 case for messages */
if (bit_size < 0) {
TCC_ERR ("negative width in bit-field '%s'",
get_tok_str (v, NULL));
}
if (v && bit_size == 0) {
TCC_ERR ("zero width for bit-field '%s'",
get_tok_str (v, NULL));
}
}
size = type_size (&type1, &align);
if (ad.aligned) {
if (align < ad.aligned) {
align = ad.aligned;
}
} else if (ad.packed) {
align = 1;
} else if (*tcc_state->pack_stack_ptr) {
if (align > *tcc_state->pack_stack_ptr) {
align = *tcc_state->pack_stack_ptr;
}
}
lbit_pos = 0;
// FIXME: Here it handles bitfields only in a way
// of the same endianess as the host system (this code was compiled for)
// It should depend on the endianess of the `asm.arch` instead.
if (bit_size >= 0) {
bt = type1.t & VT_BTYPE;
if (bt != VT_INT8 &&
bt != VT_INT16 &&
bt != VT_INT32 &&
bt != VT_INT64 &&
bt != VT_ENUM &&
bt != VT_BOOL) {
TCC_ERR ("bitfields must have scalar type");
}
bsize = size * 8;
if (bit_size > bsize) {
TCC_ERR ("width of '%s' exceeds its type",
get_tok_str (v, NULL));
} else if (bit_size == bsize) {
/* no need for bit fields */
bit_pos = 0;
} else if (bit_size == 0) {
/* XXX: what to do if only padding in a
structure ? */
/* zero size: means to pad */
bit_pos = 0;
} else {
/* we do not have enough room ?
did the type change?
is it a union? */
if ((bit_pos + bit_size) > bsize ||
bt != prevbt || a == TOK_UNION) {
bit_pos = 0;
}
lbit_pos = bit_pos;
/* XXX: handle LSB first */
type1.t |= VT_BITFIELD |
(bit_pos << VT_STRUCT_SHIFT) |
(bit_size << (VT_STRUCT_SHIFT + 6));
bit_pos += bit_size;
}
prevbt = bt;
} else {
bit_pos = 0;
}
if (v != 0 || is_structured(&type1)) {
/* add new memory data only if starting
bit field */
if (lbit_pos == 0) {
if (a == TOK_STRUCT) {
c = (c + align - 1) & - align;
offset = c;
if (size > 0) {
c += size;
}
} else {
offset = 0;
if (size > c) {
c = size;
}
}
if (align > maxalign) {
maxalign = align;
}
}
#if 1
// TODO: Don't use such a small limit?
char b[1024];
char *varstr = get_tok_str (v, NULL);
type_to_str (b, sizeof(b), &type1, NULL);
{
int type_bt = type1.t & VT_BTYPE;
//eprintf("2: %s.%s = %s\n", ctype, name, varstr);
tcc_appendf ("[+]%s.%s=%s\n",
ctype, name, varstr);
tcc_appendf ("%s.%s.%s.meta=%d\n",
ctype, name, varstr, type_bt);
/* compact form */
tcc_appendf ("%s.%s.%s=%s,%d,%d\n",
ctype, name, varstr, b, offset, arraysize);
#if 0
eprintf ("%s.%s.%s.type=%s\n", ctype, name, varstr, b);
eprintf ("%s.%s.%s.offset=%d\n", ctype, name, varstr, offset);
eprintf ("%s.%s.%s.array=%d\n", ctype, name, varstr, arraysize);
#endif
// (%s) field (%s) offset=%d array=%d", name, b, get_tok_str(v, NULL), offset, arraysize);
arraysize = 0;
if (type1.t & VT_BITFIELD) {
tcc_appendf ("%s.%s.%s.bitfield.pos=%d\n",
ctype, name, varstr, (type1.t >> VT_STRUCT_SHIFT) & 0x3f);
tcc_appendf ("%s.%s.%s.bitfield.size=%d\n",
ctype, name, varstr, (type1.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f);
}
// printf("\n");
}
#endif
}
if (v == 0 && is_structured(&type1)) {
ass = type1.ref;
while ((ass = ass->next) != NULL) {
ss = sym_push (ass->v, &ass->type, 0, offset + ass->c);
if (!ss) {
return;
}
*ps = ss;
ps = &ss->next;
}
} else if (v) {
ss = sym_push (v | SYM_FIELD, &type1, 0, offset);
if (!ss) {
return;
}
*ps = ss;
ps = &ss->next;
}
if (tok == ';' || tok == TOK_EOF) {
break;
}
skip (',');
}
skip (';');
}
skip ('}');
/* store size and alignment */
s->c = (c + maxalign - 1) & - maxalign;
s->r = maxalign;
}
}
}
/* return 0 if no type declaration. otherwise, return the basic type
and skip it.
*/
static int parse_btype(CType *type, AttributeDef *ad) {
int t, u, type_found, typespec_found, typedef_found;
Sym *s;
STACK_NEW0 (CType, type1);
memset (ad, 0, sizeof(AttributeDef));
type_found = 0;
typespec_found = 0;
typedef_found = 0;
/* FIXME: Make this dependent on the target */
t = 0; /* default for 'int' */
while (tcc_nerr () == 0) {
switch (tok) {
case TOK_EXTENSION:
/* currently, we really ignore extension */
next ();
continue;
/* ------------------------------------------------------------------ */
/* basic types */
/* ------------------------------------------------------------------ */
/* int8_t, uint8_t, char */
case TOK_UINT8:
t |= VT_UNSIGNED;
/* fall through */
case TOK_INT8:
u = VT_INT8;
goto basic_type;
case TOK_CHAR:
u = VT_INT8;
/* Mark as character type, for strings */
t |= VT_CHAR;
basic_type:
next ();
basic_type1:
if ((t & VT_BTYPE) != 0) {
tcc_error ("too many basic types");
return 0;
}
t |= u;
typespec_found = 1;
break;
/* void* */
case TOK_VOID:
u = VT_VOID;
goto basic_type;
/* int16_t, uint16_t, short */
case TOK_UINT16:
t |= VT_UNSIGNED;
/* fall through */
case TOK_INT16:
case TOK_SHORT:
u = VT_INT16;
goto basic_type;
/* int32_t, uint32_t, int */
case TOK_UINT32:
t |= VT_UNSIGNED;
/* fall through */
case TOK_INT32:
u = VT_INT32;
goto basic_type;
case TOK_INT:
next ();
typespec_found = 1;
break;
/* int64_t, uint64_t, long, long long */
case TOK_UINT64:
t |= VT_UNSIGNED;
/* fall through */
case TOK_INT64:
u = VT_INT64;
goto basic_type;
case TOK_LONG:
next ();
// FIXME: Better handling long and long long types
if ((t & VT_BTYPE) == VT_DOUBLE) {
if (strncmp (tcc_state->os, "windows", 7)) {
t = (t & ~VT_BTYPE) | VT_LDOUBLE;
}
} else if ((t & VT_BTYPE) == VT_LONG) {
t = (t & ~VT_BTYPE) | VT_INT64;
} else {
u = VT_LONG;
goto basic_type1;
}
break;
case TOK_BOOL:
case TOK_STDBOOL:
u = VT_BOOL;
goto basic_type;
case TOK_FLOAT:
u = VT_FLOAT;
goto basic_type;
case TOK_DOUBLE:
next ();
if ((t & VT_BTYPE) == VT_LONG) {
if (!strncmp (tcc_state->os, "windows", 7)) {
t = (t & ~VT_BTYPE) | VT_DOUBLE;
} else {
t = (t & ~VT_BTYPE) | VT_LDOUBLE;
}
} else {
u = VT_DOUBLE;
goto basic_type1;
}
break;
case TOK_ENUM:
struct_decl (&type1, VT_ENUM);
basic_type2:
u = type1.t;
type->ref = type1.ref;
goto basic_type1;
case TOK_STRUCT:
struct_decl (&type1, VT_STRUCT);
goto basic_type2;
case TOK_UNION:
struct_decl (&type1, VT_UNION);
goto basic_type2;
/* type modifiers */
case TOK_CONST1:
case TOK_CONST2:
case TOK_CONST3:
t |= VT_CONSTANT;
next ();
break;
case TOK_VOLATILE1:
case TOK_VOLATILE2:
case TOK_VOLATILE3:
t |= VT_VOLATILE;
next ();
break;
case TOK_SIGNED1:
case TOK_SIGNED2:
case TOK_SIGNED3:
typespec_found = 1;
t |= VT_SIGNED;
next ();
break;
case TOK_REGISTER:
case TOK_AUTO:
case TOK_RESTRICT1:
case TOK_RESTRICT2:
case TOK_RESTRICT3:
next ();
break;
case TOK_UNSIGNED:
t |= VT_UNSIGNED;
next ();
typespec_found = 1;
break;
/* storage */
case TOK_EXTERN:
t |= VT_EXTERN;
next ();
break;
case TOK_STATIC:
t |= VT_STATIC;
next ();
break;
case TOK_TYPEDEF:
t |= VT_TYPEDEF;
next ();
break;
case TOK_INLINE1:
case TOK_INLINE2:
case TOK_INLINE3:
t |= VT_INLINE;
next ();
break;
/* GNUC attribute */
case TOK_ATTRIBUTE1:
case TOK_ATTRIBUTE2:
parse_attribute (ad);
if (ad->mode) {
u = ad->mode - 1;
t = (t & ~VT_BTYPE) | u;
}
break;
/* GNUC typeof */
case TOK_TYPEOF1:
case TOK_TYPEOF2:
case TOK_TYPEOF3:
next ();
parse_expr_type (&type1);
/* remove all storage modifiers except typedef */
type1.t &= ~(VT_STORAGE & ~VT_TYPEDEF);
goto basic_type2;
default:
if (typespec_found || typedef_found) {
goto the_end;
}
s = sym_find (tok);
if (!s || !(s->type.t & VT_TYPEDEF)) {
goto the_end;
}
typedef_found = 1;
t |= (s->type.t & ~VT_TYPEDEF);
type->ref = s->type.ref;
if (s->r) {
/* get attributes from typedef */
if (0 == ad->aligned) {
ad->aligned = FUNC_ALIGN (s->r);
}
if (0 == ad->func_call) {
ad->func_call = FUNC_CALL (s->r);
}
ad->packed |= FUNC_PACKED (s->r);
}
next ();
typespec_found = 1;
break;
}
type_found = 1;
}
the_end:
if ((t & (VT_SIGNED | VT_UNSIGNED)) == (VT_SIGNED | VT_UNSIGNED)) {
tcc_error ("signed and unsigned modifier");
return 0;
}
if (tcc_state->char_is_unsigned) {
if ((t & (VT_SIGNED | VT_UNSIGNED | VT_BTYPE)) == VT_INT8) {
t |= VT_UNSIGNED;
}
}
t &= ~VT_SIGNED;
/* long is never used as type */
if ((t & VT_BTYPE) == VT_LONG) {
if (!strncmp (tcc_state->os, "windows", 7) ||
(!strncmp (tcc_state->arch, "x86", 3) && tcc_state->bits == 32)) {
t = (t & ~VT_BTYPE) | VT_INT32;
} else {
t = (t & ~VT_BTYPE) | VT_INT64;
}
}
type->t = t;
return type_found;
}
/* convert a function parameter type (array to pointer and function to
function pointer) */
static inline void convert_parameter_type(CType *pt) {
/* remove const and volatile qualifiers (XXX: const could be used
to indicate a const function parameter */
pt->t &= ~(VT_CONSTANT | VT_VOLATILE);
/* array must be transformed to pointer according to ANSI C */
pt->t &= ~VT_ARRAY;
if ((pt->t & VT_BTYPE) == VT_FUNC) {
mk_pointer (pt);
}
}
static void post_type(CType *type, AttributeDef *ad) {
int n, l, t1, arg_size, align;
Sym **plast, *s, *first;
AttributeDef ad1;
CType pt;
char *symname = NULL;
int narg = 0;
if (tok == '(') {
/* function declaration */
next ();
l = 0;
first = NULL;
plast = &first;
{
const char *ret_type = global_type;
free (symname);
symname = strdup (global_symname);
tcc_appendf ("func.%s.ret=%s\n", symname, ret_type);
tcc_appendf ("func.%s.cc=%s\n", symname, "cdecl"); // TODO
tcc_appendf ("%s=func\n", symname);
}
arg_size = 0;
if (tok != ')') {
while (tcc_nerr () == 0) {
/* read param name and compute offset */
if (l != FUNC_OLD) {
if (!parse_btype (&pt, &ad1)) {
if (l) {
TCC_ERR ("invalid type");
} else {
l = FUNC_OLD;
goto old_proto;
}
}
l = FUNC_NEW;
if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')') {
break;
}
type_decl (&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
if ((pt.t & VT_BTYPE) == VT_VOID) {
TCC_ERR ("parameter declared as void");
}
arg_size += (type_size (&pt, &align) + PTR_SIZE - 1) / PTR_SIZE;
} else {
old_proto:
n = tok;
if (n < TOK_UIDENT) {
expect ("identifier");
}
pt.t = VT_INT32;
next ();
}
convert_parameter_type (&pt);
s = sym_push (n | SYM_FIELD, &pt, 0, 0);
if (!s) {
return;
} else {
char kind[1024];
type_to_str (kind, sizeof (kind), &pt, NULL);
tcc_appendf ("func.%s.arg.%d=%s,%s\n",
symname, narg, kind, global_symname);
narg++;
}
*plast = s;
plast = &s->next;
if (tok == ')') {
break;
}
skip (',');
if (l == FUNC_NEW && tok == TOK_DOTS) {
l = FUNC_ELLIPSIS;
next ();
break;
}
}
}
tcc_appendf ("func.%s.args=%d\n", symname, narg);
/* if no parameters, then old type prototype */
if (l == 0) {
l = FUNC_OLD;
}
skip (')');
/* NOTE: const is ignored in returned type as it has a special
meaning in gcc / C++ */
type->t &= ~VT_CONSTANT;
/* some ancient pre-K&R C allows a function to return an array
and the array brackets to be put after the arguments, such
that "int c()[]" means something like "int[] c()" */
if (tok == '[') {
next ();
skip (']'); /* only handle simple "[]" */
type->t |= VT_PTR;
}
/* we push a anonymous symbol which will contain the function prototype */
ad->func_args = arg_size;
s = sym_push (SYM_FIELD, type, INT_ATTR (ad), l);
if (!s) {
return;
}
s->next = first;
type->t = VT_FUNC;
type->ref = s;
R_FREE (symname);
} else if (tok == '[') {
/* array definition */
next ();
if (tok == TOK_RESTRICT1) {
next ();
}
n = -1;
t1 = 0;
if (tok != ']') {
if (!local_stack || nocode_wanted) {
vpushll (expr_const ());
} else {
gexpr ();
}
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
n = vtop->c.i;
if (n < 0) {
TCC_ERR ("invalid array size");
}
} else {
if (!is_integer_btype (vtop->type.t & VT_BTYPE)) {
TCC_ERR ("size of variable length array should be an integer");
}
t1 = VT_VLA;
}
}
skip (']');
/* parse next post type */
post_type (type, ad);
/* we push an anonymous symbol which will contain the array
element type */
arraysize = n;
#if 0
if (n < 0) {
printf ("array with no size []\n");
} else {
printf ("PUSH SIZE %d\n", n);
}
#endif
s = sym_push (SYM_FIELD, type, 0, n);
if (!s) {
return;
}
type->t = (t1? VT_VLA: VT_ARRAY) | VT_PTR;
type->ref = s;
}
}
/* Parse a type declaration (except basic type), and return the type
in 'type'. 'td' is a bitmask indicating which kind of type decl is
expected. 'type' should contain the basic type. 'ad' is the
attribute definition of the basic type. It can be modified by
type_decl().
*/
static void type_decl(CType *type, AttributeDef *ad, int *v, int td) {
Sym *s;
int qualifiers, storage;
CType *type1 = R_NEW0 (CType);
CType *type2 = R_NEW0 (CType);
if (!type1 || !type2) {
free (type1);
free (type2);
return;
}
while (tok == '*') {
qualifiers = 0;
redo:
next ();
switch (tok) {
case TOK_CONST1:
case TOK_CONST2:
case TOK_CONST3:
qualifiers |= VT_CONSTANT;
goto redo;
case TOK_VOLATILE1:
case TOK_VOLATILE2:
case TOK_VOLATILE3:
qualifiers |= VT_VOLATILE;
goto redo;
case TOK_RESTRICT1:
case TOK_RESTRICT2:
case TOK_RESTRICT3:
goto redo;
}
mk_pointer (type);
type->t |= qualifiers;
}
/* XXX: clarify attribute handling */
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) {
parse_attribute (ad);
}
/* recursive type */
/* XXX: incorrect if abstract type for functions (e.g. 'int ()') */
type1->t = 0; /* XXX: same as int */
if (tok == '(') {
next ();
/* XXX: this is not correct to modify 'ad' at this point, but
the syntax is not clear */
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) {
parse_attribute (ad);
}
type_decl (type1, ad, v, td);
skip (')');
} else {
/* type identifier */
if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) {
*v = tok;
next ();
} else {
if (!(td & TYPE_ABSTRACT)) {
expect ("identifier");
}
*v = 0;
}
}
storage = type->t & VT_STORAGE;
type->t &= ~VT_STORAGE;
if (storage & VT_STATIC) {
int saved_nocode_wanted = nocode_wanted;
nocode_wanted = 1;
// eprintf ("STATIC %s\n", get_tok_str(*v, NULL));
post_type (type, ad);
nocode_wanted = saved_nocode_wanted;
} else {
static char kind[1024];
char *name = get_tok_str (*v, NULL);
type_to_str (kind, sizeof(kind), type, NULL);
// eprintf ("---%d %s STATIC %s\n", td, kind, name);
global_symname = name;
global_type = kind;
post_type (type, ad);
}
type->t |= storage;
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) {
parse_attribute (ad);
}
if (!type1->t) {
free (type1);
free (type2);
return;
}
/* append type at the end of type1 */
type2 = type1;
for (;;) {
s = type2->ref;
type2 = &s->type;
if (!type2->t) {
*type2 = *type;
break;
}
}
memcpy (type, type1, sizeof(*type));
}
/* compute the lvalue VT_LVAL_xxx needed to match type t. */
ST_FUNC int lvalue_type(int t) {
int bt, r;
r = VT_LVAL;
bt = t & VT_BTYPE;
if (bt == VT_INT8 || bt == VT_BOOL) {
r |= VT_LVAL_BYTE;
} else if (bt == VT_INT16) {
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 */
ST_FUNC void indir(void) {
if ((vtop->type.t & VT_BTYPE) != VT_PTR) {
if ((vtop->type.t & VT_BTYPE) == VT_FUNC) {
return;
}
expect ("pointer");
}
vtop->type = *pointed_type (&vtop->type);
/* Arrays and functions are never lvalues */
if (!(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_VLA)
&& (vtop->type.t & VT_BTYPE) != VT_FUNC) {
vtop->r |= lvalue_type (vtop->type.t);
/* if bound checking, the referenced pointer must be checked */
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check) {
vtop->r |= VT_MUSTBOUND;
}
#endif
}
}
/* parse an expression of the form '(type)' or '(expr)' and return its
type */
static void parse_expr_type(CType *type) {
int n;
AttributeDef ad;
skip ('(');
if (parse_btype (type, &ad)) {
type_decl (type, &ad, &n, TYPE_ABSTRACT);
} else {
expr_type (type);
}
skip (')');
}
static void parse_type(CType *type) {
AttributeDef ad;
int n;
if (!parse_btype (type, &ad)) {
expect ("type");
}
type_decl (type, &ad, &n, TYPE_ABSTRACT);
}
static void vpush_tokc(int t) {
CType type = { 0 };
type.t = t;
type.ref = NULL;
vsetc (&type, VT_CONST, &tokc);
}
ST_FUNC void unary(void) {
int n, t, align, size, r, sizeof_caller;
CType type = { 0 };
Sym *s;
AttributeDef ad;
static int in_sizeof = 0;
sizeof_caller = in_sizeof;
in_sizeof = 0;
/* XXX: GCC 2.95.3 does not generate a table although it should be
better here */
tok_next:
switch (tok) {
case TOK_EXTENSION:
next ();
goto tok_next;
case TOK_CINT:
case TOK_CCHAR:
case TOK_LCHAR:
vpushi (tokc.i);
next ();
break;
case TOK_CUINT:
vpush_tokc (VT_INT32 | VT_UNSIGNED);
next ();
break;
case TOK_CLLONG:
vpush_tokc (VT_INT64);
next ();
break;
case TOK_CULLONG:
vpush_tokc (VT_INT64 | VT_UNSIGNED);
next ();
break;
case TOK_CFLOAT:
vpush_tokc (VT_FLOAT);
next ();
break;
case TOK_CDOUBLE:
vpush_tokc (VT_DOUBLE);
next ();
break;
case TOK_CLDOUBLE:
vpush_tokc (VT_LDOUBLE);
next ();
break;
case TOK___FUNCTION__:
if (!gnu_ext) {
goto tok_identifier;
}
/* fall thru */
case TOK___FUNC__:
{
// void *ptr = NULL;
int len;
/* special function name identifier */
len = strlen (funcname) + 1;
/* generate char[len] type */
type.t = VT_INT8;
mk_pointer (&type);
type.t |= VT_ARRAY;
if (type.ref) {
type.ref->c = len;
}
// XXX ptr is NULL HERE WTF
// memcpy(ptr, funcname, len);
next ();
}
break;
case TOK_LSTR:
if (!strncmp (tcc_state->os, "windows", 7)) {
t = VT_INT16 | VT_UNSIGNED;
} else {
t = VT_INT32;
}
goto str_init;
case TOK_STR:
/* string parsing */
t = VT_INT8;
str_init:
if (tcc_state->warn_write_strings) {
t |= VT_CONSTANT;
}
type.t = t;
mk_pointer (&type);
type.t |= VT_ARRAY;
memset (&ad, 0, sizeof(AttributeDef));
decl_initializer_alloc (&type, &ad, VT_CONST, 2, 0, NULL, 0);
break;
case '(':
next ();
/* cast ? */
if (parse_btype (&type, &ad)) {
type_decl (&type, &ad, &n, TYPE_ABSTRACT);
skip (')');
/* check ISOC99 compound literal */
if (tok == '{') {
/* data is allocated locally by default */
if (global_expr) {
r = VT_CONST;
} else {
r = VT_LOCAL;
}
/* all except arrays are lvalues */
if (!(type.t & VT_ARRAY)) {
r |= lvalue_type (type.t);
}
memset (&ad, 0, sizeof(AttributeDef));
decl_initializer_alloc (&type, &ad, r, 1, 0, NULL, 0);
} else {
if (sizeof_caller) {
vpush (&type);
return;
}
unary ();
}
} else if (tok == '{') {
/* statement expression : we do not accept break/continue
inside as GCC does */
skip (')');
} else {
gexpr ();
skip (')');
}
break;
case '*':
next ();
unary ();
indir ();
break;
case '!':
next ();
unary ();
if ((vtop->r & VT_VALMASK) == VT_CMP) {
vtop->c.i = vtop->c.i ^ 1;
}
break;
case TOK_SIZEOF:
case TOK_ALIGNOF1:
case TOK_ALIGNOF2:
t = tok;
next ();
in_sizeof++;
unary_type (&type); // Perform a in_sizeof = 0;
size = type_size (&type, &align);
if (t == TOK_SIZEOF) {
if (!(type.t & VT_VLA)) {
if (size < 0) {
TCC_ERR ("sizeof applied to an incomplete type");
}
vpushs (size);
}
} else {
vpushs (align);
}
vtop->type.t |= VT_UNSIGNED;
break;
case TOK_builtin_types_compatible_p:
{
STACK_NEW0 (CType, type1);
STACK_NEW0 (CType, type2);
next ();
skip ('(');
parse_type (&type1);
skip (',');
parse_type (&type2);
skip (')');
type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
vpushi (is_compatible_types (&type1, &type2));
}
break;
case TOK_builtin_constant_p:
{
int saved_nocode_wanted;
long long res;
next ();
skip ('(');
saved_nocode_wanted = nocode_wanted;
nocode_wanted = 1;
gexpr ();
res = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
nocode_wanted = saved_nocode_wanted;
skip (')');
vpushll (res);
}
break;
case TOK_builtin_frame_address:
{
int level;
CType type = { 0 };
next ();
skip ('(');
if (tok != TOK_CINT || tokc.i < 0) {
TCC_ERR ("__builtin_frame_address only takes positive integers");
}
level = tokc.i;
next ();
skip (')');
type.t = VT_VOID;
mk_pointer (&type);
vset (&type, VT_LOCAL, 0); /* local frame */
while (level--) {
mk_pointer (&vtop->type);
indir (); /* -> parent frame */
}
}
break;
case TOK_builtin_va_start:
if (!strncmp (tcc_state->arch, "x86", 3) && tcc_state->bits == 64 &&
!strncmp (tcc_state->os, "windows", 7)) {
next ();
skip ('(');
expr_eq ();
skip (',');
expr_eq ();
skip (')');
if ((vtop->r & VT_VALMASK) != VT_LOCAL) {
TCC_ERR ("__builtin_va_start expects a local variable");
}
vtop->r &= ~(VT_LVAL | VT_REF);
vtop->type = char_pointer_type;
}
break;
case TOK_builtin_va_arg_types:
if (!(!strncmp (tcc_state->arch, "x86", 3) && tcc_state->bits == 64 &&
!strncmp (tcc_state->os, "windows", 7))) {
CType type = { 0 };
next ();
skip ('(');
parse_type (&type);
skip (')');
// FIXME: Handle this too
// vpushll(classify_x86_64_va_arg(&type));
}
break;
// special qnan , snan and infinity values
case TOK___NAN__:
vpush64 (VT_DOUBLE, 0x7ff8000000000000ULL);
next ();
break;
case TOK___SNAN__:
vpush64 (VT_DOUBLE, 0x7ff0000000000001ULL);
next ();
break;
case TOK___INF__:
vpush64 (VT_DOUBLE, 0x7ff0000000000000ULL);
next ();
break;
default:
tok_identifier:
t = tok;
next ();
if (t < TOK_UIDENT) {
expect ("identifier");
}
s = sym_find (t);
if (!s) {
if (tok != '(') {
TCC_ERR ("'%s' undeclared", get_tok_str (t, NULL));
}
}
if (!s) {
TCC_ERR ("invalid declaration '%s'", get_tok_str (t, NULL));
} else {
if ((s->type.t & (VT_STATIC | VT_INLINE | VT_BTYPE)) ==
(VT_STATIC | VT_INLINE | VT_FUNC)) {
/* if referencing an inline function, then we generate a
symbol to it if not already done. It will have the
effect to generate code for it at the end of the
compilation unit. */
r = VT_SYM | VT_CONST;
} else {
r = s->r;
}
vset (&s->type, r, s->c);
/* if forward reference, we must point to s */
if (vtop->r & VT_SYM) {
vtop->sym = s;
vtop->c.ul = 0;
}
}
break;
}
/* post operations */
while (1) {
if (tok == '.' || tok == TOK_ARROW) {
int qualifiers;
/* field */
if (tok == TOK_ARROW) {
indir ();
}
qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE);
test_lvalue ();
gaddrof ();
next ();
/* expect pointer on structure */
if (not_structured(&vtop->type)) {
expect ("struct or union");
}
s = vtop->type.ref;
/* find field */
tok |= SYM_FIELD;
while ((s = s->next) != NULL) {
if (s->v == tok) {
break;
}
}
if (!s) {
TCC_ERR ("field not found: %s", get_tok_str (tok & ~SYM_FIELD, NULL));
}
/* add field offset to pointer */
vtop->type = char_pointer_type; /* change type to 'char *' */
vpushi (s->c);
/* change type to field type, and set to lvalue */
vtop->type = s->type;
vtop->type.t |= qualifiers;
/* an array is never an lvalue */
if (!(vtop->type.t & VT_ARRAY)) {
vtop->r |= lvalue_type (vtop->type.t);
#ifdef CONFIG_TCC_BCHECK
/* if bound checking, the referenced pointer must be checked */
if (tcc_state->do_bounds_check) {
vtop->r |= VT_MUSTBOUND;
}
#endif
}
next ();
} else if (tok == '[') {
next ();
gexpr ();
indir ();
skip (']');
/*
} else if (tok == '(') {
SValue ret;
Sym *sa;
int nb_args, sret;
*/
} else {
break;
}
}
}
ST_FUNC void expr_prod(void) {
unary ();
while (tok == '*' || tok == '/' || tok == '%') {
next ();
unary ();
}
}
ST_FUNC void expr_sum(void) {
expr_prod ();
while (tok == '+' || tok == '-') {
next ();
expr_prod ();
}
}
static void expr_shift(void) {
expr_sum ();
while (tok == TOK_SHL || tok == TOK_SAR) {
next ();
expr_sum ();
}
}
static void expr_cmp(void) {
expr_shift ();
while ((tok >= TOK_ULE && tok <= TOK_GT) ||
tok == TOK_ULT || tok == TOK_UGE) {
next ();
expr_shift ();
}
}
static void expr_cmpeq(void) {
expr_cmp ();
while (tok == TOK_EQ || tok == TOK_NE) {
next ();
expr_cmp ();
}
}
static void expr_and(void) {
expr_cmpeq ();
while (tok == '&') {
next ();
expr_cmpeq ();
}
}
static void expr_xor(void) {
expr_and ();
while (tok == '^') {
next ();
expr_and ();
}
}
static void expr_or(void) {
expr_xor ();
while (tok == '|') {
next ();
expr_xor ();
}
}
/* XXX: fix this mess */
static void expr_land_const(void) {
expr_or ();
while (tok == TOK_LAND) {
next ();
expr_or ();
}
}
/* XXX: fix this mess */
static void expr_lor_const(void) {
expr_land_const ();
while (tok == TOK_LOR) {
next ();
expr_land_const ();
}
}
/* only used if non constant */
static void expr_land(void) {
expr_or ();
if (tok == TOK_LAND) {
while (tcc_nerr () == 0) {
if (tok != TOK_LAND) {
break;
}
next ();
expr_or ();
}
}
}
static void expr_lor(void) {
expr_land ();
if (tok == TOK_LOR) {
while (tcc_nerr () == 0) {
if (tok != TOK_LOR) {
break;
}
next ();
expr_land ();
}
}
}
/* XXX: better constant handling */
static void expr_cond(void) {
if (const_wanted) {
expr_lor_const ();
if (tok == '?') {
vdup ();
next ();
if (tok != ':' || !gnu_ext) {
gexpr ();
}
skip (':');
expr_cond ();
}
} else {
expr_lor ();
}
}
static void expr_eq(void) {
int t;
expr_cond ();
if (tok == '=' ||
(tok >= TOK_A_MOD && tok <= TOK_A_DIV) ||
tok == TOK_A_XOR || tok == TOK_A_OR ||
tok == TOK_A_SHL || tok == TOK_A_SAR) {
test_lvalue ();
t = tok;
next ();
if (t == '=') {
expr_eq ();
} else {
vdup ();
expr_eq ();
}
}
}
ST_FUNC void gexpr(void) {
while (tcc_nerr () == 0) {
expr_eq ();
if (tok != ',') {
break;
}
next ();
}
}
/* parse an expression and return its type without any side effect. */
static void expr_type(CType *type) {
int saved_nocode_wanted;
saved_nocode_wanted = nocode_wanted;
nocode_wanted = 1;
gexpr ();
*type = vtop->type;
nocode_wanted = saved_nocode_wanted;
}
/* parse a unary expression and return its type without any side
effect. */
static void unary_type(CType *type) {
int a = nocode_wanted;
nocode_wanted = 1;
unary ();
*type = vtop->type;
nocode_wanted = a;
}
/* parse a constant expression and return value in vtop. */
static void expr_const1(void) {
int a;
a = const_wanted;
const_wanted = 1;
expr_cond ();
const_wanted = a;
}
/* parse an integer constant and return its value. */
ST_FUNC long long expr_const(void) {
long long c = 0LL;
expr_const1 ();
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) {
expect ("constant expression");
}
c = vtop->c.ll;
return c;
}
/* return the label token if current token is a label, otherwise
return zero */
static int is_label(void) {
int last_tok;
/* fast test first */
if (tok < TOK_UIDENT) {
return 0;
}
/* no need to save tokc because tok is an identifier */
last_tok = tok;
next ();
if (tok == ':') {
next ();
return last_tok;
} else {
unget_tok (last_tok);
return 0;
}
}
/* t is the array or struct type. c is the array or struct
address. cur_index/cur_field is the pointer to the current
value. 'size_only' is true if only size info is needed (only used
in arrays) */
static void decl_designator(CType *type, unsigned long c,
long long *cur_index, Sym **cur_field,
int size_only)
{
Sym *s, *f = NULL;
long long index, index_last;
int notfirst, align, l, nb_elems, elem_size;
STACK_NEW0 (CType, type1);
notfirst = 0;
if (gnu_ext && (l = is_label ()) != 0) {
goto struct_field;
}
while (tok == '[' || tok == '.') {
if (tok == '[') {
if (!(type->t & VT_ARRAY)) {
expect ("array type");
}
s = type->ref;
next ();
index = expr_const ();
if (index < 0 || (s->c >= 0 && index >= s->c)) {
expect ("invalid index");
}
if (tok == TOK_DOTS && gnu_ext) {
next ();
index_last = expr_const ();
if (index_last < 0 ||
(s->c >= 0 && index_last >= s->c) ||
index_last < index) {
expect ("invalid index");
}
} else {
index_last = index;
}
skip (']');
if (!notfirst && cur_index) {
*cur_index = index_last;
}
type = pointed_type (type);
elem_size = type_size (type, &align);
c += index * elem_size;
/* NOTE: we only support ranges for last designator */
nb_elems = index_last - index + 1;
if (nb_elems != 1) {
notfirst = 1;
break;
}
} else {
next ();
l = tok;
next ();
struct_field:
if (not_structured(type)) {
expect ("struct/union type");
}
s = type->ref;
l |= SYM_FIELD;
f = s->next;
while (f) {
if (f->v == l) {
break;
}
f = f->next;
}
if (!f) {
expect ("field");
}
if (!notfirst && cur_field) {
*cur_field = f;
}
/* XXX: fix this mess by using explicit storage field */
if (f) {
type1 = f->type;
type1.t |= (type->t & ~VT_TYPE);
type = &type1;
c += f->c;
}
}
notfirst = 1;
}
if (notfirst) {
if (tok == '=') {
next ();
} else {
if (!gnu_ext) {
expect ("=");
}
}
} else {
if (type->t & VT_ARRAY) {
index = cur_index ? *cur_index : 0;
type = pointed_type (type);
c += index * type_size (type, &align);
} else {
f = cur_field ? *cur_field : NULL;
if (!f) {
TCC_ERR ("too many field init");
}
/* XXX: fix this mess by using explicit storage field */
if (f) {
type1 = f->type;
type1.t |= (type->t & ~VT_TYPE);
type = &type1;
c += f->c;
}
}
}
decl_initializer (type, c, 0, size_only);
}
#define EXPR_VAL 0
#define EXPR_CONST 1
#define EXPR_ANY 2
/* store a value or an expression directly in global data or in local array */
static void init_putv(CType *type, unsigned long c, long long v, int expr_type) {
int saved_global_expr;
CType dtype;
switch (expr_type) {
case EXPR_VAL:
vpushll (v);
break;
case EXPR_CONST:
/* compound literals must be allocated globally in this case */
saved_global_expr = global_expr;
global_expr = 1;
expr_const1 ();
global_expr = saved_global_expr;
/* NOTE: symbols are accepted */
if ((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST) {
TCC_ERR ("initializer element is not constant");
}
break;
case EXPR_ANY:
expr_eq ();
break;
}
dtype = *type;
dtype.t &= ~VT_CONSTANT;/* need to do that to avoid false warning */
vset (&dtype, VT_LOCAL | VT_LVAL, c);
vswap ();
}
/* put zeros for variable based init */
static void init_putz(CType *t, unsigned long c, int size) {
vseti (VT_LOCAL, c);
vpushi (0);
vpushs (size);
}
/* 't' contains the type and storage info. 'c' is the offset of the
object in section 'sec'. If 'sec' is NULL, it means stack based
allocation. 'first' is true if array '{' must be read (multi
dimension implicit array init handling). 'size_only' is true if
size only evaluation is wanted (only for arrays). */
static void decl_initializer(CType *type, unsigned long c, int first, int size_only) {
long long index;
int array_length, n, no_oblock, nb, parlevel, parlevel1, i;
int size1, align1, expr_type;
Sym *s, *f;
CType *t1;
if (type->t & VT_ARRAY) {
s = type->ref;
n = s->c;
array_length = 0;
t1 = pointed_type (type);
size1 = type_size (t1, &align1);
no_oblock = 1;
if ((first && tok != TOK_LSTR && tok != TOK_STR) ||
tok == '{') {
if (tok != '{') {
TCC_ERR ("character array initializer must be a literal,"
" optionally enclosed in braces");
}
skip ('{');
no_oblock = 0;
}
/* only parse strings here if correct type (otherwise: handle
them as ((w)char *) expressions */
if ((tok == TOK_LSTR &&
/* FIXME: Handle platform here ! */
#ifdef TCC_TARGET_PE
(t1->t & VT_BTYPE) == VT_INT16 && (t1->t & VT_UNSIGNED)
#else
(t1->t & VT_BTYPE) == VT_INT32
#endif
) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_INT8)) {
while (tcc_nerr() == 0 && (tok == TOK_STR || tok == TOK_LSTR)) {
int cstr_len, ch;
CString *cstr;
cstr = tokc.cstr;
/* compute maximum number of chars wanted */
if (tok == TOK_STR) {
cstr_len = cstr->size;
} else {
cstr_len = cstr->size / sizeof(nwchar_t);
}
cstr_len--;
nb = cstr_len;
if (n >= 0 && nb > (n - array_length)) {
nb = n - array_length;
}
if (!size_only) {
if (cstr_len > nb) {
tcc_warning ("initializer-string for array is too long");
}
/* in order to go faster for common case (char
string in global variable, we handle it
specifically */
for (i = 0; i < nb; i++) {
if (tok == TOK_STR) {
ch = ((unsigned char *) cstr->data)[i];
} else {
ch = ((nwchar_t *) cstr->data)[i];
}
init_putv (t1, c + (array_length + i) * size1,
ch, EXPR_VAL);
}
}
array_length += nb;
next ();
}
/* only add trailing zero if enough storage (no
warning in this case since it is standard) */
if (n < 0 || array_length < n) {
if (!size_only) {
init_putv (t1, c + (array_length * size1), 0, EXPR_VAL);
}
array_length++;
}
} else {
index = 0;
while (tok != '}') {
decl_designator (type, c, &index, NULL, size_only);
if (n >= 0 && index >= n) {
TCC_ERR ("index too large");
}
/* must put zero in holes (note that doing it that way
ensures that it even works with designators) */
if (!size_only && array_length < index) {
init_putz (t1, c + array_length * size1,
(index - array_length) * size1);
}
index++;
if (index > array_length) {
array_length = index;
}
/* special test for multi dimensional arrays (may not
be strictly correct if designators are used at the
same time) */
if (index >= n && no_oblock) {
break;
}
if (tok == '}') {
break;
}
skip (',');
}
}
if (!no_oblock) {
skip ('}');
}
/* put zeros at the end */
if (!size_only && n >= 0 && array_length < n) {
init_putz (t1, c + array_length * size1,
(n - array_length) * size1);
}
/* patch type size if needed */
if (n < 0) {
s->c = array_length;
}
} else if (is_structured(type) && (!first || tok == '{')) {
int par_count;
/* NOTE: the previous test is a specific case for automatic
struct/union init */
/* XXX: union needs only one init */
/* XXX: this test is incorrect for local initializers
beginning with ( without {. It would be much more difficult
to do it correctly (ideally, the expression parser should
be used in all cases) */
par_count = 0;
if (tok == '(') {
AttributeDef ad1;
STACK_NEW0 (CType, type1);
next ();
while (tok == '(') {
par_count++;
next ();
}
if (!parse_btype (&type1, &ad1)) {
expect ("cast");
}
type_decl (&type1, &ad1, &n, TYPE_ABSTRACT);
#if 0
if (!is_assignable_types (type, &type1)) {
tcc_error ("invalid type for cast");
}
#endif
skip (')');
}
no_oblock = 1;
if (first || tok == '{') {
skip ('{');
no_oblock = 0;
}
s = type->ref;
f = s->next;
array_length = 0;
index = 0;
n = s->c;
while (tok != '}') {
decl_designator (type, c, NULL, &f, size_only);
index = f->c;
if (!size_only && array_length < index) {
init_putz (type, c + array_length,
index - array_length);
}
index = index + type_size (&f->type, &align1);
if (index > array_length) {
array_length = index;
}
/* gr: skip fields from same union - ugly. */
while (f->next) {
///printf("index: %2d %08x -- %2d %08x\n", f->c, f->type.t, f->next->c, f->next->type.t);
/* test for same offset */
if (f->next->c != f->c) {
break;
}
/* if yes, test for bitfield shift */
if ((f->type.t & VT_BITFIELD) && (f->next->type.t & VT_BITFIELD)) {
int bit_pos_1 = (f->type.t >> VT_STRUCT_SHIFT) & 0x3f;
int bit_pos_2 = (f->next->type.t >> VT_STRUCT_SHIFT) & 0x3f;
// printf("bitfield %d %d\n", bit_pos_1, bit_pos_2);
if (bit_pos_1 != bit_pos_2) {
break;
}
}
f = f->next;
}
f = f->next;
if (no_oblock && f == NULL) {
break;
}
if (tok == '}') {
break;
}
skip (',');
}
/* put zeros at the end */
if (!size_only && array_length < n) {
init_putz (type, c + array_length,
n - array_length);
}
if (!no_oblock) {
skip ('}');
}
while (par_count) {
skip (')');
par_count--;
}
} else if (tok == '{') {
next ();
decl_initializer (type, c, first, size_only);
skip ('}');
} else if (size_only) {
/* just skip expression */
parlevel = parlevel1 = 0;
while ((parlevel > 0 || parlevel1 > 0 ||
(tok != '}' && tok != ',')) && tok != -1) {
if (tok == '(') {
parlevel++;
} else if (tok == ')') {
parlevel--;
} else if (tok == '{') {
parlevel1++;
} else if (tok == '}') {
parlevel1--;
}
next ();
}
} else {
/* currently, we always use constant expression for globals
(may change for scripting case) */
expr_type = EXPR_CONST;
init_putv (type, c, 0, expr_type);
}
}
/* parse an initializer for type 't' if 'has_init' is non zero, and
allocate space in local or global data space ('r' is either
VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated
variable 'v' with an associated name represented by 'asm_label' of
scope 'scope' is declared before initializers are parsed. If 'v' is
zero, then a reference to the new object is put in the value stack.
If 'has_init' is 2, a special parsing is done to handle string
constants. */
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, char *asm_label, int scope) {
int size, align, addr;
int level;
ParseState saved_parse_state = {
0
};
TokenString init_str;
Sym *flexible_array;
flexible_array = NULL;
if (is_struct(type)) {
Sym *field;
field = type->ref;
while (field && field->next)
field = field->next;
if (field && (field->type.t & VT_ARRAY) && (field->type.ref->c < 0)) {
flexible_array = field;
}
}
size = type_size (type, &align);
/* If unknown size, we must evaluate it before
evaluating initializers because
initializers can generate global data too
(e.g. string pointers or ISOC99 compound
literals). It also simplifies local
initializers handling */
tok_str_new (&init_str);
if (size < 0 || (flexible_array && has_init)) {
if (!has_init) {
TCC_ERR ("unknown type size");
}
/* get all init string */
if (has_init == 2) {
/* only get strings */
while (tok == TOK_STR || tok == TOK_LSTR) {
tok_str_add_tok (&init_str);
next ();
}
} else {
level = 0;
while (tcc_nerr() == 0 && (level > 0 || (tok != ',' && tok != ';'))) {
if (tok < 0) {
TCC_ERR ("unexpected end of file in initializer");
}
tok_str_add_tok (&init_str);
if (tok == '{') {
level++;
} else if (tok == '}') {
level--;
if (level <= 0) {
next ();
break;
}
}
next ();
}
}
tok_str_add (&init_str, -1);
tok_str_add (&init_str, 0);
/* compute size */
save_parse_state (&saved_parse_state);
macro_ptr = init_str.str;
next ();
decl_initializer (type, 0, 1, 1);
/* prepare second initializer parsing */
macro_ptr = init_str.str;
next ();
/* if still unknown size, error */
size = type_size (type, &align);
if (size < 0) {
TCC_ERR ("unknown type size");
}
}
if (flexible_array) {
size += flexible_array->type.ref->c * pointed_size (&flexible_array->type);
}
/* take into account specified alignment if bigger */
if (ad->aligned) {
if (ad->aligned > align) {
align = ad->aligned;
}
} else if (ad->packed) {
align = 1;
}
if ((r & VT_VALMASK) == VT_LOCAL) {
loc = (loc - size) & - align;
addr = loc;
if (v) {
/* local variable */
sym_push (v, type, r, addr);
} else {
/* push local reference */
vset (type, r, addr);
}
} else {
Sym *sym;
sym = NULL;
if (v && scope == VT_CONST) {
/* see if the symbol was already defined */
sym = sym_find (v);
if (sym) {
if (!is_compatible_types (&sym->type, type)) {
TCC_ERR ("incompatible types for redefinition of '%s'",
get_tok_str (v, NULL));
}
if (sym->type.t & VT_EXTERN) {
/* if the variable is extern, it was not allocated */
sym->type.t &= ~VT_EXTERN;
/* set array size if it was ommited in extern
declaration */
if ((sym->type.t & VT_ARRAY) &&
sym->type.ref->c < 0 &&
type->ref->c >= 0) {
sym->type.ref->c = type->ref->c;
}
} else {
/* we accept several definitions of the same
global variable. this is tricky, because we
must play with the SHN_COMMON type of the symbol */
/* XXX: should check if the variable was already
initialized. It is incorrect to initialized it
twice */
/* no init data, we won't add more to the symbol */
if (!has_init) {
goto no_alloc;
}
}
}
}
if (v) {
if (scope != VT_CONST || !sym) {
sym = sym_push (v, type, r | VT_SYM, 0);
sym->asm_label = asm_label;
}
} else {
CValue cval = { 0 };
vsetc (type, VT_CONST | VT_SYM, &cval);
vtop->sym = sym;
}
/* patch symbol weakness */
if ((type->t & VT_WEAK) && sym) {
weaken_symbol (sym);
}
}
no_alloc:
;
}
/* parse an old style function declaration list */
/* XXX: check multiple parameter */
static void func_decl_list(Sym *func_sym) {
AttributeDef ad;
int v;
Sym *s = NULL;
CType btype, type;
/* parse each declaration */
while (tcc_nerr () == 0 && tok != '{' && tok != ';' && tok != ',' && tok != TOK_EOF &&
tok != TOK_ASM1 && tok != TOK_ASM2 && tok != TOK_ASM3) {
if (!parse_btype (&btype, &ad)) {
expect ("declaration list");
}
if ((is_enum(&btype) || is_structured(&btype)) && tok == ';') {
/* we accept no variable after */
} else {
while (tcc_nerr () == 0) {
int found;
type = btype;
type_decl (&type, &ad, &v, TYPE_DIRECT);
/* find parameter in function parameter list */
s = func_sym;
found = 0;
while ((s = s->next) != NULL) {
if ((s->v & ~SYM_FIELD) == v) {
found = 1;
break;
}
}
if (found == 0) {
TCC_ERR ("declaration for parameter '%s' but no such parameter",
get_tok_str (v, NULL));
}
/* check that no storage specifier except 'register' was given */
if (type.t & VT_STORAGE) {
TCC_ERR ("storage class specified for '%s'", get_tok_str (v, NULL));
}
convert_parameter_type (&type);
/* we can add the type (NOTE: it could be local to the function) */
if (s) {
s->type = type;
}
/* accept other parameters */
if (tok == ',') {
next ();
} else {
break;
}
}
}
skip (';');
}
}
/* 'l' is VT_LOCAL or VT_CONST to define default storage type */
static int decl0(int l, int is_for_loop_init) {
int v, has_init, r;
CType type = {.t = 0, .ref = NULL}, btype = {.t = 0, .ref = NULL};
Sym *sym = NULL;
AttributeDef ad;
while (tcc_nerr () == 0) {
if (!parse_btype (&btype, &ad)) {
if (is_for_loop_init) {
return 0;
}
/* skip redundant ';' */
/* XXX: find more elegant solution */
if (tok == ';') {
next ();
continue;
}
if (l == VT_CONST &&
(tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
/* global asm block */
#if 1
eprintf ("global asm not supported\n");
return 1;
#endif
// asm_global_instr();
continue;
}
/* special test for old K&R protos without explicit int
type. Only accepted when defining global data */
if (l == VT_LOCAL || tok < TOK_DEFINE) {
break;
}
btype.t = VT_INT32;
}
if ((is_enum(&btype) || is_structured(&btype)) && tok == ';') {
/* we accept no variable after */
next ();
continue;
}
/* iterate thru each declaration */
while (tcc_nerr () == 0) {
type = btype;
type_decl (&type, &ad, &v, TYPE_DIRECT);
#if 0
{
char buf[500];
type_to_str (buf, sizeof(buf), t, get_tok_str (v, NULL));
printf ("type = '%s'\n", buf);
}
#endif
if ((type.t & VT_BTYPE) == VT_FUNC) {
if ((type.t & VT_STATIC) && (l == VT_LOCAL)) {
tcc_error ("function without file scope cannot be static");
return 1;
}
/* if old style function prototype, we accept a
declaration list */
sym = type.ref;
if (sym->c == FUNC_OLD) {
func_decl_list (sym);
}
}
if (ad.weak) {
type.t |= VT_WEAK;
}
#ifdef TCC_TARGET_PE
if (ad.func_import) {
type.t |= VT_IMPORT;
}
if (ad.func_export) {
type.t |= VT_EXPORT;
}
#endif
if (tok == '{') {
if (l == VT_LOCAL) {
tcc_error ("cannot use local functions");
return 1;
}
if ((type.t & VT_BTYPE) != VT_FUNC) {
expect ("function definition");
}
/* reject abstract declarators in function definition */
sym = type.ref;
if (sym) {
while ((sym = sym->next) != NULL)
if (!(sym->v & ~SYM_FIELD)) {
expect ("identifier");
}
} else {
return 0; // XXX unmatching braces in typedef?
}
/* XXX: cannot do better now: convert extern line to static inline */
if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE)) {
type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
}
sym = sym_find (v);
if (sym) {
if ((sym->type.t & VT_BTYPE) != VT_FUNC) {
goto func_error1;
}
r = sym->type.ref->r;
/* use func_call from prototype if not defined */
if (FUNC_CALL (r) != FUNC_CDECL
&& FUNC_CALL (type.ref->r) == FUNC_CDECL) {
FUNC_CALL (type.ref->r) = FUNC_CALL (r);
}
/* use export from prototype */
if (FUNC_EXPORT (r)) {
FUNC_EXPORT (type.ref->r) = 1;
}
/* use static from prototype */
if (sym->type.t & VT_STATIC) {
type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
}
if (!is_compatible_types (&sym->type, &type)) {
func_error1:
tcc_error ("incompatible types for redefinition of '%s'",
get_tok_str (v, NULL));
return 1;
}
/* if symbol is already defined, then put complete type */
sym->type = type;
} else {
/* put function symbol */
sym = global_identifier_push (v, type.t, 0);
if (!sym) {
return 1;
}
sym->type.ref = type.ref;
}
break;
} else {
if (btype.t & VT_TYPEDEF) {
/* save typedefed type */
/* XXX: test storage specifiers ? */
if (tok != ';') {
v = tok;
next();
}
sym = sym_push (v, &type, INT_ATTR (&ad), 0);
if (!sym) {
return 1;
}
sym->type.t |= VT_TYPEDEF;
/* Provide SDB with typedefs' info */
const char *alias = NULL;
char buf[500];
alias = get_tok_str(v, NULL);
type_to_str(buf, sizeof(buf), &sym->type, NULL);
tcc_appendf("%s=typedef\n",alias);
tcc_appendf("typedef.%s=%s\n",alias, buf);
} else {
r = 0;
if ((type.t & VT_BTYPE) == VT_FUNC) {
/* external function definition */
/* specific case for func_call attribute */
type.ref->r = INT_ATTR (&ad);
} else if (!(type.t & VT_ARRAY)) {
/* not lvalue if array */
r |= lvalue_type (type.t);
}
has_init = (tok == '=');
if (has_init && (type.t & VT_VLA)) {
tcc_error ("Variable length array cannot be initialized");
return 1;
}
}
if (tok != ',') {
if (is_for_loop_init) {
return 1;
}
skip (';');
break;
}
next ();
}
ad.aligned = 0;
}
}
return 0;
}
ST_FUNC void decl(int l) {
decl0 (l, 0);
}