aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--init.c35
-rw-r--r--init.h2
-rw-r--r--qbe.c11
-rw-r--r--tests/initializer-replace.c10
-rw-r--r--tests/initializer-replace.qbe22
5 files changed, 56 insertions, 24 deletions
diff --git a/init.c b/init.c
index 590fbc9..1b0c5e0 100644
--- a/init.c
+++ b/init.c
@@ -46,29 +46,24 @@ mkinit(uint64_t start, uint64_t end, struct expr *expr)
static void
initadd(struct init **init, struct init *new)
{
- struct init *next, *sub, *last;
- uint64_t offset;
+ struct init *old;
- while (*init && new->start >= (*init)->end)
- init = &(*init)->next;
- next = *init;
- if (next && next->start <= new->start && new->end < next->end) {
- initadd(&next->subinit, new);
- last = NULL; /* silence gcc, we know that next->subinit has at least one member */
- for (offset = next->start, sub = next->subinit; sub; offset = sub->end, last = sub, sub = sub->next) {
- if (sub->start != offset)
- return;
- }
- if (offset == next->end) {
- *init = next->subinit;
- last->next = next->next;
+ for (; old = *init; init = &old->next) {
+ if (new->start >= old->end)
+ continue;
+ /* no overlap, insert before `old` */
+ if (new->end <= old->start)
+ break;
+ /* replace any initializers that `new` covers */
+ if (new->end >= old->end) {
+ do old = old->next;
+ while (old && new->end >= old->end);
+ break;
}
- } else {
- *init = new;
- while (next && next->start < (*init)->end)
- next = next->next;
- (*init)->next = next;
+ /* `old` covers `new`, keep looking */
}
+ new->next = old;
+ *init = new;
}
static void
diff --git a/init.h b/init.h
index 9e50515..410b4d7 100644
--- a/init.h
+++ b/init.h
@@ -1,7 +1,7 @@
struct init {
uint64_t start, end;
struct expr *expr;
- struct init *next, *subinit;
+ struct init *next;
};
struct scope;
diff --git a/qbe.c b/qbe.c
index 63e0039..a40540a 100644
--- a/qbe.c
+++ b/qbe.c
@@ -892,7 +892,7 @@ void
funcinit(struct func *func, struct decl *d, struct init *init)
{
struct value *src, *dst;
- uint64_t offset = 0;
+ uint64_t offset = 0, max = 0;
size_t i;
funcalloc(func, d);
@@ -912,8 +912,10 @@ funcinit(struct func *func, struct decl *d, struct init *init)
funcstore(func, init->expr->type, dst, src);
offset = init->end;
}
+ if (max < offset)
+ max = offset;
}
- zero(func, d->value, d->type->align, offset, d->type->size);
+ zero(func, d->value, d->type->align, max, d->type->size);
}
static void
@@ -1203,12 +1205,15 @@ emitdata(struct decl *d, struct init *init)
emitvalue(d->value);
printf(" = align %d { ", d->align);
- for (; init; offset = init->end, init = init->next) {
+ for (; init; init = init->next) {
+ if (init->start < offset) /* XXX: sub-initializer may overlap */
+ continue;
if (offset < init->start)
printf("z %" PRIu64 ", ", init->start - offset);
printf("%c ", init->expr->type->kind == TYPEARRAY ? init->expr->type->base->repr->ext : init->expr->type->repr->ext);
dataitem(init->expr, init->end - init->start);
fputs(", ", stdout);
+ offset = init->end;
}
assert(offset <= d->type->size);
if (offset < d->type->size)
diff --git a/tests/initializer-replace.c b/tests/initializer-replace.c
new file mode 100644
index 0000000..8b93ef2
--- /dev/null
+++ b/tests/initializer-replace.c
@@ -0,0 +1,10 @@
+void f(void) {
+ struct {
+ char s[6];
+ } x = {
+ .s[0] = 'x',
+ .s[4] = 'y',
+ .s = "hello",
+ .s[1] = 'a',
+ };
+}
diff --git a/tests/initializer-replace.qbe b/tests/initializer-replace.qbe
new file mode 100644
index 0000000..72ad90a
--- /dev/null
+++ b/tests/initializer-replace.qbe
@@ -0,0 +1,22 @@
+export
+function $f() {
+@start.1
+ %.1 =l alloc4 6
+@body.2
+ %.2 =l add %.1, 0
+ storeb 104, %.2
+ %.3 =l add %.1, 1
+ storeb 101, %.3
+ %.4 =l add %.1, 2
+ storeb 108, %.4
+ %.5 =l add %.1, 3
+ storeb 108, %.5
+ %.6 =l add %.1, 4
+ storeb 111, %.6
+ %.7 =l add %.1, 1
+ %.8 =w copy 97
+ storeb %.8, %.7
+ %.9 =l add %.1, 5
+ storeb 0, %.9
+ ret
+}