struct-init: Reimplement

Start reimplementing the whole initializer handling to be
conforming to ISO C.  This patch just reimplements current
functionality to prepare for further changes, all tests pass.
master
Michael Matz 2016-07-24 00:43:49 +02:00
parent 5d0c16a884
commit 968bccdd2a
1 changed files with 106 additions and 78 deletions

184
tccgen.c
View File

@ -5774,19 +5774,10 @@ static void decl_designator(CType *type, Section *sec, unsigned long c,
#define EXPR_CONST 1 #define EXPR_CONST 1
#define EXPR_ANY 2 #define EXPR_ANY 2
/* store a value or an expression directly in global data or in local array */ static void parse_init_elem(int expr_type)
static void init_putv(CType *type, Section *sec, unsigned long c,
int v, int expr_type)
{ {
int saved_global_expr, bt, bit_pos, bit_size; int saved_global_expr;
void *ptr;
unsigned long long bit_mask;
CType dtype;
switch(expr_type) { switch(expr_type) {
case EXPR_VAL:
vpushi(v);
break;
case EXPR_CONST: case EXPR_CONST:
/* compound literals must be allocated globally in this case */ /* compound literals must be allocated globally in this case */
saved_global_expr = global_expr; saved_global_expr = global_expr;
@ -5801,18 +5792,29 @@ static void init_putv(CType *type, Section *sec, unsigned long c,
expr_eq(); expr_eq();
break; break;
} }
}
/* store a value or an expression directly in global data or in local array */
static void init_putv(CType *type, Section *sec, unsigned long c,
int v, int expr_type)
{
int bt, bit_pos, bit_size;
void *ptr;
unsigned long long bit_mask;
CType dtype;
dtype = *type; dtype = *type;
dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */ dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
if (sec) { if (sec) {
int size, align;
/* XXX: not portable */ /* XXX: not portable */
/* XXX: generate error if incorrect relocation */ /* XXX: generate error if incorrect relocation */
gen_assign_cast(&dtype); gen_assign_cast(&dtype);
bt = type->t & VT_BTYPE; bt = type->t & VT_BTYPE;
/* we'll write at most 16 bytes */ size = type_size(type, &align);
if (c + 16 > sec->data_allocated) { if (c + size > sec->data_allocated) {
section_realloc(sec, c + 16); section_realloc(sec, c + size);
} }
ptr = sec->data + c; ptr = sec->data + c;
/* XXX: make code faster ? */ /* XXX: make code faster ? */
@ -5825,79 +5827,102 @@ static void init_putv(CType *type, Section *sec, unsigned long c,
bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f; bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
bit_mask = (1LL << bit_size) - 1; bit_mask = (1LL << bit_size) - 1;
} }
if ((vtop->r & VT_SYM) && if ((vtop->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST) &&
(bt == VT_BYTE || vtop->sym->v >= SYM_FIRST_ANOM &&
bt == VT_SHORT || /* XXX This rejects compount literals like
bt == VT_DOUBLE || '(void *){ptr}'. The problem is that '&sym' is
bt == VT_LDOUBLE || represented the same way, which would be ruled out
by the SYM_FIRST_ANOM check above, but also '"string"'
in 'char *p = "string"' is represented the same
with the type being VT_PTR and the symbol being an
anonymous one. That is, there's no difference in vtop
between '(void *){x}' and '&(void *){x}'. Ignore
pointer typed entities here. Hopefully no real code
will every use compound literals with scalar type. */
(vtop->type.t & VT_BTYPE) != VT_PTR) {
/* These come from compound literals, memcpy stuff over. */
Section *ssec;
ElfW(Sym) *esym;
esym = &((ElfW(Sym) *)symtab_section->data)[vtop->sym->c];
ssec = tcc_state->sections[esym->st_shndx];
memmove (ptr, ssec->data + esym->st_value, size);
} else {
if ((vtop->r & VT_SYM) &&
(bt == VT_BYTE ||
bt == VT_SHORT ||
bt == VT_DOUBLE ||
bt == VT_LDOUBLE ||
#if PTR_SIZE == 8 #if PTR_SIZE == 8
(bt == VT_LLONG && bit_size != 64) || (bt == VT_LLONG && bit_size != 64) ||
bt == VT_INT bt == VT_INT
#else #else
bt == VT_LLONG || bt == VT_LLONG ||
(bt == VT_INT && bit_size != 32) (bt == VT_INT && bit_size != 32)
#endif #endif
)) ))
tcc_error("initializer element is not computable at load time"); tcc_error("initializer element is not computable at load time");
switch(bt) { switch(bt) {
/* XXX: when cross-compiling we assume that each type has the /* XXX: when cross-compiling we assume that each type has the
same representation on host and target, which is likely to same representation on host and target, which is likely to
be wrong in the case of long double */ be wrong in the case of long double */
case VT_BOOL: case VT_BOOL:
vtop->c.i = (vtop->c.i != 0); vtop->c.i = (vtop->c.i != 0);
case VT_BYTE: case VT_BYTE:
*(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos; *(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
break; break;
case VT_SHORT: case VT_SHORT:
*(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos; *(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
break; break;
case VT_DOUBLE: case VT_DOUBLE:
*(double *)ptr = vtop->c.d; *(double *)ptr = vtop->c.d;
break; break;
case VT_LDOUBLE: case VT_LDOUBLE:
if (sizeof(long double) == LDOUBLE_SIZE) if (sizeof(long double) == LDOUBLE_SIZE)
*(long double *)ptr = vtop->c.ld; *(long double *)ptr = vtop->c.ld;
else if (sizeof(double) == LDOUBLE_SIZE) else if (sizeof(double) == LDOUBLE_SIZE)
*(double *)ptr = vtop->c.ld; *(double *)ptr = vtop->c.ld;
else else
tcc_error("can't cross compile long double constants"); tcc_error("can't cross compile long double constants");
break; break;
#if PTR_SIZE != 8 #if PTR_SIZE != 8
case VT_LLONG: case VT_LLONG:
*(long long *)ptr |= (vtop->c.i & bit_mask) << bit_pos; *(long long *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
break; break;
#else #else
case VT_LLONG: case VT_LLONG:
#endif #endif
case VT_PTR: { case VT_PTR:
addr_t val = (vtop->c.i & bit_mask) << bit_pos; {
addr_t val = (vtop->c.i & bit_mask) << bit_pos;
#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) #if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
if (vtop->r & VT_SYM) if (vtop->r & VT_SYM)
greloca(sec, vtop->sym, c, R_DATA_PTR, val); greloca(sec, vtop->sym, c, R_DATA_PTR, val);
else else
*(addr_t *)ptr |= val; *(addr_t *)ptr |= val;
#else #else
if (vtop->r & VT_SYM) if (vtop->r & VT_SYM)
greloc(sec, vtop->sym, c, R_DATA_PTR); greloc(sec, vtop->sym, c, R_DATA_PTR);
*(addr_t *)ptr |= val; *(addr_t *)ptr |= val;
#endif #endif
break; break;
} }
default: { default:
int val = (vtop->c.i & bit_mask) << bit_pos; {
int val = (vtop->c.i & bit_mask) << bit_pos;
#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) #if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
if (vtop->r & VT_SYM) if (vtop->r & VT_SYM)
greloca(sec, vtop->sym, c, R_DATA_PTR, val); greloca(sec, vtop->sym, c, R_DATA_PTR, val);
else else
*(int *)ptr |= val; *(int *)ptr |= val;
#else #else
if (vtop->r & VT_SYM) if (vtop->r & VT_SYM)
greloc(sec, vtop->sym, c, R_DATA_PTR); greloc(sec, vtop->sym, c, R_DATA_PTR);
*(int *)ptr |= val; *(int *)ptr |= val;
#endif #endif
break; break;
} }
} }
}
vtop--; vtop--;
} else { } else {
vset(&dtype, VT_LOCAL|VT_LVAL, c); vset(&dtype, VT_LOCAL|VT_LVAL, c);
@ -6006,6 +6031,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
ch = ((unsigned char *)tokc.str.data)[i]; ch = ((unsigned char *)tokc.str.data)[i];
else else
ch = ((nwchar_t *)tokc.str.data)[i]; ch = ((nwchar_t *)tokc.str.data)[i];
vpushi(ch);
init_putv(t1, sec, c + (array_length + i) * size1, init_putv(t1, sec, c + (array_length + i) * size1,
ch, EXPR_VAL); ch, EXPR_VAL);
} }
@ -6018,6 +6044,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
warning in this case since it is standard) */ warning in this case since it is standard) */
if (n < 0 || array_length < n) { if (n < 0 || array_length < n) {
if (!size_only) { if (!size_only) {
vpushi(0);
init_putv(t1, sec, c + (array_length * size1), 0, EXPR_VAL); init_putv(t1, sec, c + (array_length * size1), 0, EXPR_VAL);
} }
array_length++; array_length++;
@ -6058,14 +6085,14 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
if (n < 0) if (n < 0)
s->c = array_length; s->c = array_length;
} else if ((type->t & VT_BTYPE) == VT_STRUCT && } else if ((type->t & VT_BTYPE) == VT_STRUCT &&
(sec || !first || tok == '{')) { (!first || tok == '{')) {
/* NOTE: the previous test is a specific case for automatic /* NOTE: the previous test is a specific case for automatic
struct/union init */ struct/union init */
/* XXX: union needs only one init */ /* XXX: union needs only one init */
int par_count = 0; int par_count = 0;
if (tok == '(') { if (0 && tok == '(') {
AttributeDef ad1; AttributeDef ad1;
CType type1; CType type1;
next(); next();
@ -6210,6 +6237,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
expr_type = EXPR_CONST; expr_type = EXPR_CONST;
if (!sec) if (!sec)
expr_type = EXPR_ANY; expr_type = EXPR_ANY;
parse_init_elem(expr_type);
init_putv(type, sec, c, 0, expr_type); init_putv(type, sec, c, 0, expr_type);
} }
} }