aboutsummaryrefslogtreecommitdiff
path: root/qbe.c
diff options
context:
space:
mode:
Diffstat (limited to 'qbe.c')
-rw-r--r--qbe.c57
1 files changed, 50 insertions, 7 deletions
diff --git a/qbe.c b/qbe.c
index 33d6b71..5457e21 100644
--- a/qbe.c
+++ b/qbe.c
@@ -95,6 +95,10 @@ struct func {
struct block *start, *end;
struct map gotos;
unsigned lastid;
+ bool deferring;
+ struct {
+ struct block *start, *end, *deferred;
+ } defer;
};
static const int ptrclass = 'l';
@@ -244,14 +248,14 @@ static struct value *
funcinst(struct func *f, int op, int class, struct value *arg0, struct value *arg1)
{
struct inst *inst;
- struct block *b;
+ struct block *b, *end = f->deferring ? f->defer.end : f->end;
- if (f->end->jump.kind) {
+ if (end->jump.kind) {
b = mkblock("dead");
funclabel(f, b);
}
inst = mkinst(f, op, class, arg0, arg1);
- arrayaddptr(&f->end->insts, inst);
+ arrayaddptr(&end->insts, inst);
return &inst->res;
}
@@ -512,6 +516,9 @@ mkfunc(struct decl *decl, char *name, struct type *t, struct scope *s)
f->type = t;
f->start = f->end = mkblock("start");
f->lastid = 0;
+ f->defer.start = f->defer.end = mkblock("defer");
+ f->defer.deferred = NULL;
+ f->deferring = false;
mapinit(&f->gotos, 8);
emittype(t->base);
@@ -566,16 +573,48 @@ functype(struct func *f)
}
void
+funcstartdefer(struct func *f)
+{
+ f->deferring = true;
+}
+
+void
+funcenddefer(struct func *f)
+{
+ f->deferring = false;
+ f->defer.end->next = f->defer.deferred;
+ f->defer.deferred = f->defer.start;
+ f->defer.start = f->defer.end = mkblock("defer");
+}
+
+void
+funcdefer(struct func *f)
+{
+ if (!f->defer.deferred || f->deferring)
+ return;
+ f->end->next = f->defer.deferred;
+ while (f->defer.deferred->next)
+ f->defer.deferred = f->defer.deferred->next;
+ f->end = f->defer.deferred;
+ f->defer.deferred = NULL;
+}
+
+void
funclabel(struct func *f, struct block *b)
{
- f->end->next = b;
- f->end = b;
+ if (f->deferring) {
+ f->defer.end->next = b;
+ f->defer.end = b;
+ } else {
+ f->end->next = b;
+ f->end = b;
+ }
}
void
funcjmp(struct func *f, struct block *l)
{
- struct block *b = f->end;
+ struct block *b = f->deferring ? f->defer.end : f->end;
if (!b->jump.kind) {
b->jump.kind = JUMP_JMP;
@@ -586,7 +625,7 @@ funcjmp(struct func *f, struct block *l)
void
funcjnz(struct func *f, struct value *v, struct type *t, struct block *l1, struct block *l2)
{
- struct block *b = f->end;
+ struct block *b = f->deferring ? f->defer.end : f->end;
if (b->jump.kind)
return;
@@ -613,6 +652,10 @@ funcret(struct func *f, struct value *v)
{
struct block *b = f->end;
+ if (f->deferring) {
+ error(&tok.loc, "cannot return from defer block.");
+ }
+
if (!b->jump.kind) {
b->jump.kind = JUMP_RET;
b->jump.arg = v;