struct-init: Fix zero initialization with multi-level designators

See the added testcase.  When one used designators like .a.x to initialize
sub-members of members, and didn't then initialize all of them the
required zero-initialization of the other sub-members wasn't done.
The fix also enables tiny code cleanups.
master
Michael Matz 2017-05-06 05:28:13 +02:00
parent 0757234560
commit ff998900b1
3 changed files with 96 additions and 67 deletions

126
tccgen.c
View File

@ -5915,17 +5915,37 @@ static void parse_init_elem(int expr_type)
}
}
/* put zeros for variable based init */
static void init_putz(Section *sec, unsigned long c, int size)
{
if (sec) {
/* nothing to do because globals are already set to zero */
} else {
vpush_global_sym(&func_old_type, TOK_memset);
vseti(VT_LOCAL, c);
#ifdef TCC_TARGET_ARM
vpushs(size);
vpushi(0);
#else
vpushi(0);
vpushs(size);
#endif
gfunc_call(3);
}
}
/* t is the array or struct type. c is the array or struct
address. cur_field is the pointer to the current
value, for arrays the 'c' member contains the current start
index and the 'r' contains the end index (in case of range init).
'size_only' is true if only size info is needed (only used
in arrays) */
static void decl_designator(CType *type, Section *sec, unsigned long c,
Sym **cur_field, int size_only)
field, for arrays the 'c' member contains the current start
index. 'size_only' is true if only size info is needed (only used
in arrays). al contains the already initialized length of the
current container (starting at c). This returns the new length of that. */
static int decl_designator(CType *type, Section *sec, unsigned long c,
Sym **cur_field, int size_only, int al)
{
Sym *s, *f;
int index, index_last, align, l, nb_elems, elem_size;
unsigned long corig = c;
elem_size = 0;
nb_elems = 1;
@ -5947,10 +5967,8 @@ static void decl_designator(CType *type, Section *sec, unsigned long c,
if (index < 0 || (s->c >= 0 && index_last >= s->c) ||
index_last < index)
tcc_error("invalid index");
if (cur_field) {
(*cur_field)->c = index;
(*cur_field)->r = index_last;
}
if (cur_field)
(*cur_field)->c = index_last;
type = pointed_type(type);
elem_size = type_size(type, &align);
c += index * elem_size;
@ -5995,6 +6013,10 @@ static void decl_designator(CType *type, Section *sec, unsigned long c,
c += f->c;
}
}
/* must put zero in holes (note that doing it that way
ensures that it even works with designators) */
if (!size_only && c - corig > al)
init_putz(sec, corig + al, c - corig - al);
decl_initializer(type, sec, c, 0, size_only);
/* XXX: make it more general */
@ -6023,6 +6045,10 @@ static void decl_designator(CType *type, Section *sec, unsigned long c,
}
}
}
c += nb_elems * type_size(type, &align);
if (c - corig > al)
al = c - corig;
return al;
}
/* store a value or an expression directly in global data or in local array */
@ -6195,25 +6221,6 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
}
}
/* put zeros for variable based init */
static void init_putz(Section *sec, unsigned long c, int size)
{
if (sec) {
/* nothing to do because globals are already set to zero */
} else {
vpush_global_sym(&func_old_type, TOK_memset);
vseti(VT_LOCAL, c);
#ifdef TCC_TARGET_ARM
vpushs(size);
vpushi(0);
#else
vpushi(0);
vpushs(size);
#endif
gfunc_call(3);
}
}
/* '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
@ -6222,7 +6229,7 @@ static void init_putz(Section *sec, unsigned long c, int size)
static void decl_initializer(CType *type, Section *sec, unsigned long c,
int first, int size_only)
{
int index, array_length, n, no_oblock, nb, i;
int len, n, no_oblock, nb, i;
int size1, align1;
int have_elem;
Sym *s, *f;
@ -6252,7 +6259,6 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
} else if (type->t & VT_ARRAY) {
s = type->ref;
n = s->c;
array_length = 0;
t1 = pointed_type(type);
size1 = type_size(t1, &align1);
@ -6275,6 +6281,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
(t1->t & VT_BTYPE) == VT_INT
#endif
) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
len = 0;
while (tok == TOK_STR || tok == TOK_LSTR) {
int cstr_len, ch;
@ -6285,8 +6292,8 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
cstr_len = tokc.str.size / sizeof(nwchar_t);
cstr_len--;
nb = cstr_len;
if (n >= 0 && nb > (n - array_length))
nb = n - array_length;
if (n >= 0 && nb > (n - len))
nb = n - len;
if (!size_only) {
if (cstr_len > nb)
tcc_warning("initializer-string for array is too long");
@ -6294,7 +6301,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
string in global variable, we handle it
specifically */
if (sec && tok == TOK_STR && size1 == 1) {
memcpy(sec->data + c + array_length, tokc.str.data, nb);
memcpy(sec->data + c + len, tokc.str.data, nb);
} else {
for(i=0;i<nb;i++) {
if (tok == TOK_STR)
@ -6302,75 +6309,61 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
else
ch = ((nwchar_t *)tokc.str.data)[i];
vpushi(ch);
init_putv(t1, sec, c + (array_length + i) * size1);
init_putv(t1, sec, c + (len + i) * size1);
}
}
}
array_length += nb;
len += 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 (n < 0 || len < n) {
if (!size_only) {
vpushi(0);
init_putv(t1, sec, c + (array_length * size1));
init_putv(t1, sec, c + (len * size1));
}
array_length++;
len++;
}
len *= size1;
} else {
indexsym.c = 0;
indexsym.r = 0;
f = &indexsym;
do_init_list:
len = 0;
while (tok != '}' || have_elem) {
decl_designator(type, sec, c, &f, size_only);
len = decl_designator(type, sec, c, &f, size_only, len);
have_elem = 0;
index = f->c;
/* 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(sec, c + array_length * size1,
(index - array_length) * size1);
}
if (type->t & VT_ARRAY) {
index = indexsym.c = ++indexsym.r;
++indexsym.c;
/* special test for multi dimensional arrays (may not
be strictly correct if designators are used at the
same time) */
if (no_oblock && len >= n*size1)
break;
} else {
index = index + type_size(&f->type, &align1);
if (s->type.t == TOK_UNION)
f = NULL;
else
f = f->next;
}
if (index > array_length)
array_length = index;
if (type->t & VT_ARRAY) {
/* special test for multi dimensional arrays (may not
be strictly correct if designators are used at the
same time) */
if (no_oblock && index >= n)
break;
} else {
if (no_oblock && f == NULL)
break;
}
if (tok == '}')
break;
skip(',');
}
}
/* put zeros at the end */
if (!size_only && array_length < n) {
init_putz(sec, c + array_length * size1,
(n - array_length) * size1);
}
if (!size_only && len < n*size1)
init_putz(sec, c + len, n*size1 - len);
if (!no_oblock)
skip('}');
/* patch type size if needed, which happens only for array types */
if (n < 0)
s->c = array_length;
s->c = size1 == 1 ? len : ((len + size1 - 1)/size1);
} else if ((type->t & VT_BTYPE) == VT_STRUCT) {
size1 = 1;
no_oblock = 1;
@ -6380,7 +6373,6 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
}
s = type->ref;
f = s->next;
array_length = 0;
n = s->c;
goto do_init_list;
} else if (tok == '{') {

View File

@ -217,7 +217,40 @@ void test_multi_relocs(void)
for (i = 0; i < sizeof(table)/sizeof(table[0]); i++)
table[i]();
}
/* Following is from GCC gcc.c-torture/execute/20050613-1.c. */
struct SEA { int i; int j; int k; int l; };
struct SEB { struct SEA a; int r[1]; };
struct SEC { struct SEA a; int r[0]; };
struct SED { struct SEA a; int r[]; };
static void
test_correct_filling (struct SEA *x)
{
static int i;
if (x->i != 0 || x->j != 5 || x->k != 0 || x->l != 0)
printf("sea_fill%d: wrong\n", i);
else
printf("sea_fill%d: okay\n", i);
i++;
}
int
test_zero_init (void)
{
/* The peculiarity here is that only a.j is initialized. That
means that all other members must be zero initialized. TCC
once didn't do that for sub-level designators. */
struct SEB b = { .a.j = 5 };
struct SEC c = { .a.j = 5 };
struct SED d = { .a.j = 5 };
test_correct_filling (&b.a);
test_correct_filling (&c.a);
test_correct_filling (&d.a);
return 0;
}
int main()
{
print(ce);
@ -244,5 +277,6 @@ int main()
//printf("q: %s\n", q);
test_compound_with_relocs();
test_multi_relocs();
test_zero_init();
return 0;
}

View File

@ -38,3 +38,6 @@ flow: 9 8 7 6 0 0 0 0 0 0 0 0 0 0 0 0 6 5 4 3 0 0 0 0 0 0 0 0 0 0 0 0
one
two
three
sea_fill0: okay
sea_fill1: okay
sea_fill2: okay