[qfcc] Rewrite init_elements

The end goal was to fix erroneous non-constant initializer errors for
the following (ie, nested initializer blocks):

    typedef struct { int x; int y; } Point;
    typedef struct { int width; int height; } Extent;
    typedef struct Rect_s { Point offset; Extent extent; } Rect;
    Rect makeRect (int xpos, int ypos, int xlen, int ylen)
    {
	Rect rect = {{xpos, ypos}, {xlen, ylen}};
	return rect;
    }

However, it turned out that nested initializer blocks for local
variables did not work at all in that the relocations were lost because
fake defs were being created for the generated instructions.

Thus, instead of creating fake defs, simply record the offset relative
to the base def, the type, and the basic type initializer expression,
then generate instructions that all refer to the correct def but with a
relative offset.

Other than using the new element system, static initializers are largely
unaffected.
This commit is contained in:
Bill Currie 2020-03-05 11:05:13 +09:00
parent 1b2a806f28
commit 9ccfe8aefc

View file

@ -62,7 +62,37 @@
#include "type.h" #include "type.h"
#include "value.h" #include "value.h"
typedef struct element_s {
struct element_s *next;
int offset;
type_t *type;
expr_t *expr;
} element_t;
typedef struct element_chain_s {
element_t *head;
element_t **tail;
} element_chain_t;
static def_t *defs_freelist; static def_t *defs_freelist;
static element_t *elements_freelist;
static element_t *
new_element (void)
{
element_t *element;
ALLOC (256, element_t, elements, element);
return element;
}
static element_t *
append_element (element_chain_t *element_chain, element_t *element)
{
element->next = 0;
*element_chain->tail = element;
element_chain->tail = &element->next;
return element;
}
static void static void
set_storage_bits (def_t *def, storage_class_t storage) set_storage_bits (def_t *def, storage_class_t storage)
@ -303,17 +333,8 @@ zero_memory (expr_t *local_expr, def_t *def, type_t *zero_type,
} }
static void static void
init_elements (struct def_s *def, expr_t *eles) init_elements_nil (def_t *def)
{ {
expr_t *e, *c;
int count, i, num_elements, base_offset;
pr_type_t *g;
def_t *elements;
base_offset = def->offset;
if (def->local && local_expr)
base_offset = 0;
if (eles->type == ex_nil) {
if (def->local && local_expr) { if (def->local && local_expr) {
// memset to 0 // memset to 0
int init_size = type_size (def->type); int init_size = type_size (def->type);
@ -344,100 +365,126 @@ init_elements (struct def_s *def, expr_t *eles)
} }
} }
// it's a global, so already initialized to 0 // it's a global, so already initialized to 0
return;
} }
if (is_array (def->type)) {
type_t *array_type = def->type->t.array.type; static void
int array_size = def->type->t.array.size; build_element_chain (element_chain_t *element_chain, type_t *type,
elements = calloc (array_size, sizeof (def_t)); expr_t *eles, int base_offset)
{
expr_t *e = eles->e.block.head;
if (is_array (type)) {
type_t *array_type = type->t.array.type;
int array_size = type->t.array.size;
int i;
for (i = 0; i < array_size; i++) { for (i = 0; i < array_size; i++) {
elements[i].type = array_type; int offset = base_offset + i * type_size (array_type);
elements[i].space = def->space; if (e && e->type == ex_block) {
elements[i].offset = base_offset + i * type_size (array_type); build_element_chain (element_chain, array_type, e, offset);
} else {
element_t *element = new_element ();
element->type = array_type;
element->offset = offset;
element->expr = e; // null will be treated as nil
append_element (element_chain, element);
} }
num_elements = i; if (e) {
} else if (is_struct (def->type) || is_vector (def->type) e = e->next;
|| is_quaternion (def->type)) { }
symtab_t *symtab = def->type->t.symtab; }
} else if (is_struct (type) || is_vector (type) || is_quaternion (type)) {
symtab_t *symtab = type->t.symtab;
symbol_t *field; symbol_t *field;
for (i = 0, field = symtab->symbols; field; field = field->next) { for (field = symtab->symbols; field; field = field->next) {
if (field->sy_type != sy_var) int offset = base_offset + field->s.offset;
if (field->sy_type != sy_var
|| field->visibility == vis_anonymous) {
continue; continue;
i++;
} }
elements = calloc (i, sizeof (def_t)); if (e && e->type == ex_block) {
for (i = 0, field = symtab->symbols; field; field = field->next) { build_element_chain (element_chain, field->type, e, offset);
if (field->sy_type != sy_var) } else {
continue; element_t *element = new_element ();
elements[i].type = field->type; element->type = field->type;
elements[i].space = def->space; element->offset = offset;
elements[i].offset = base_offset + field->s.offset; element->expr = e; // null will be treated as nil
i++; append_element (element_chain, element);
}
if (e) {
e = e->next;
}
} }
num_elements = i;
} else { } else {
error (eles, "invalid initializer"); error (eles, "invalid initializer");
return;
} }
for (count = 0, e = eles->e.block.head; e; count++, e = e->next) { if (e && e->next && options.warnings.initializer) {
convert_name (e);
if (e->type == ex_nil && count < num_elements
&& !(is_array (elements[count].type)
|| is_struct (elements[count].type))) {
convert_nil (e, elements[count].type);
}
if (e->type == ex_error) {
free (elements);
return;
}
}
if (count > num_elements) {
if (options.warnings.initializer)
warning (eles, "excessive elements in initializer"); warning (eles, "excessive elements in initializer");
count = num_elements;
} }
for (i = 0, e = eles->e.block.head; i < count; i++, e = e->next) {
g = D_POINTER (pr_type_t, &elements[i]);
c = constant_expr (e);
// nil will not survive as nil to this point if array or struct
if (c->type == ex_block || c->type == ex_nil) {
if (!is_array (elements[i].type)
&& !is_struct (elements[i].type)) {
error (e, "type mismatch in initializer");
continue;
} }
init_elements (&elements[i], c);
continue; static void
} else if (c->type == ex_labelref) { init_elements (struct def_s *def, expr_t *eles)
def_t loc; {
loc.space = elements[i].space; expr_t *c;
loc.offset = elements[i].offset; pr_type_t *g;
reloc_def_op (c->e.labelref.label, &loc); element_chain_t element_chain;
element_t *element;
if (eles->type == ex_nil) {
init_elements_nil (def);
return;
}
element_chain.head = 0;
element_chain.tail = &element_chain.head;
build_element_chain (&element_chain, def->type, eles, 0);
if (def->local && local_expr) {
for (element = element_chain.head; element; element = element->next) {
int offset = element->offset;
type_t *type = element->type;
expr_t *ptr = new_pointer_expr (offset, type, def);
if (element->expr) {
c = constant_expr (element->expr);
} else {
c = convert_nil (new_nil_expr (), type);
}
append_expr (local_expr, assign_expr (unary_expr ('.', ptr), c));
}
} else {
def_t dummy = *def;
for (element = element_chain.head; element; element = element->next) {
if (element->expr) {
c = constant_expr (element->expr);
} else {
c = convert_nil (new_nil_expr (), element->type);
}
dummy.offset = def->offset + element->offset;
g = D_POINTER (pr_type_t, &dummy);
if (c->type == ex_labelref) {
reloc_def_op (c->e.labelref.label, &dummy);
continue; continue;
} else if (c->type == ex_value) { } else if (c->type == ex_value) {
if (c->e.value->lltype == ev_integer if (c->e.value->lltype == ev_integer
&& elements[i].type->type == ev_float) && is_float (element->type)) {
convert_int (c); convert_int (c);
if (get_type (c) != elements[i].type) { }
error (e, "type mismatch in initializer"); if (get_type (c) != element->type) {
error (c, "type mismatch in initializer");
continue; continue;
} }
} else { } else {
if (!def->local || !local_expr) { if (!def->local || !local_expr) {
error (e, "non-constant initializer"); error (c, "non-constant initializer");
continue; continue;
} }
} }
if (def->local && local_expr) { if (c->type != ex_value) {
int offset = elements[i].offset;
type_t *type = elements[i].type;
expr_t *ptr = new_pointer_expr (offset, type, def);
append_expr (local_expr, assign_expr (unary_expr ('.', ptr), c));
} else {
if (c->type != ex_value)
internal_error (c, "bogus expression type in init_elements()"); internal_error (c, "bogus expression type in init_elements()");
}
if (c->e.value->lltype == ev_string) { if (c->e.value->lltype == ev_string) {
EMIT_STRING (def->space, g->string_var, EMIT_STRING (def->space, g->string_var,
c->e.value->v.string_val); c->e.value->v.string_val);
@ -446,7 +493,9 @@ init_elements (struct def_s *def, expr_t *eles)
} }
} }
} }
free (elements);
*element_chain.tail = elements_freelist;
elements_freelist = element_chain.head;
} }
static void static void