aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cc.h4
-rw-r--r--pp.c1
-rw-r--r--qbe.c57
-rw-r--r--stmt.c14
-rw-r--r--token.c1
5 files changed, 70 insertions, 7 deletions
diff --git a/cc.h b/cc.h
index cb53b0d..fabc460 100644
--- a/cc.h
+++ b/cc.h
@@ -26,6 +26,7 @@ enum tokenkind {
TCONSTEXPR,
TCONTINUE,
TDEFAULT,
+ TDEFER,
TDO,
TDOUBLE,
TELSE,
@@ -558,6 +559,9 @@ struct value *mkintconst(unsigned long long);
struct func *mkfunc(struct decl *, char *, struct type *, struct scope *);
void delfunc(struct func *);
struct type *functype(struct func *);
+void funcstartdefer(struct func *);
+void funcenddefer(struct func *);
+void funcdefer(struct func *);
void funclabel(struct func *, struct block *);
struct value *funcexpr(struct func *, struct expr *);
void funcjmp(struct func *, struct block *);
diff --git a/pp.c b/pp.c
index 99e56e8..7040017 100644
--- a/pp.c
+++ b/pp.c
@@ -571,6 +571,7 @@ keyword(struct token *tok)
{"constexpr", TCONSTEXPR},
{"continue", TCONTINUE},
{"default", TDEFAULT},
+ {"defer", TDEFER},
{"do", TDO},
{"double", TDOUBLE},
{"else", TELSE},
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;
diff --git a/stmt.c b/stmt.c
index ff163bf..358f232 100644
--- a/stmt.c
+++ b/stmt.c
@@ -81,6 +81,7 @@ stmt(struct func *f, struct scope *s)
stmt(f, s);
}
s = delscope(s);
+ funcdefer(f);
next();
break;
@@ -95,6 +96,15 @@ stmt(struct func *f, struct scope *s)
expect(TSEMICOLON, "after expression statement");
break;
+ case TDEFER:
+ next();
+ funcstartdefer(f);
+ s = mkscope(s);
+ stmt(f, s);
+ s = delscope(s);
+ funcenddefer(f);
+ break;
+
/* 6.8.4 Selection statement */
case TIF:
next();
@@ -276,6 +286,7 @@ stmt(struct func *f, struct scope *s)
case TGOTO:
next();
name = expect(TIDENT, "after 'goto'");
+ //funcdefer(f);
funcjmp(f, funcgoto(f, name)->label);
expect(TSEMICOLON, "after 'goto' statement");
break;
@@ -283,6 +294,7 @@ stmt(struct func *f, struct scope *s)
next();
if (!s->continuelabel)
error(&tok.loc, "'continue' statement must be in loop");
+ funcdefer(f);
funcjmp(f, s->continuelabel);
expect(TSEMICOLON, "after 'continue' statement");
break;
@@ -290,6 +302,7 @@ stmt(struct func *f, struct scope *s)
next();
if (!s->breaklabel)
error(&tok.loc, "'break' statement must be in loop or switch");
+ funcdefer(f);
funcjmp(f, s->breaklabel);
expect(TSEMICOLON, "after 'break' statement");
break;
@@ -303,6 +316,7 @@ stmt(struct func *f, struct scope *s)
} else {
v = NULL;
}
+ funcdefer(f);
funcret(f, v);
expect(TSEMICOLON, "after 'return' statement");
break;
diff --git a/token.c b/token.c
index b67baa6..0a70662 100644
--- a/token.c
+++ b/token.c
@@ -22,6 +22,7 @@ const char *tokstr[] = {
[TCONSTEXPR] = "constexpr",
[TCONTINUE] = "continue",
[TDEFAULT] = "default",
+ [TDEFER] = "defer",
[TDO] = "do",
[TDOUBLE] = "double",
[TELSE] = "else",