diff options
author | Anna (navi) Figueiredo Gomes <navi@vlhl.dev> | 2024-11-06 02:46:44 +0100 |
---|---|---|
committer | Anna (navi) Figueiredo Gomes <navi@vlhl.dev> | 2024-11-06 12:17:01 +0100 |
commit | 3e51be9d94e561549281e386ce1b7554018ef4dd (patch) | |
tree | a88f20aba911030f16e692351cf5489746917910 | |
parent | f66a661359a39e10af01508ad02429517b8460e3 (diff) |
cproc: Implement defer statement.defer
implemented by keeping a list of deferred blocks in a function, and
emitting them before scope exits (return, lbracket, break, continue)
TODO: error checking, the compiler should error should a compile time
jump (goto) would go over a defer statement.
-rw-r--r-- | cc.h | 4 | ||||
-rw-r--r-- | pp.c | 1 | ||||
-rw-r--r-- | qbe.c | 57 | ||||
-rw-r--r-- | stmt.c | 14 | ||||
-rw-r--r-- | token.c | 1 |
5 files changed, 70 insertions, 7 deletions
@@ -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 *); @@ -571,6 +571,7 @@ keyword(struct token *tok) {"constexpr", TCONSTEXPR}, {"continue", TCONTINUE}, {"default", TDEFAULT}, + {"defer", TDEFER}, {"do", TDO}, {"double", TDOUBLE}, {"else", TELSE}, @@ -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; @@ -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; @@ -22,6 +22,7 @@ const char *tokstr[] = { [TCONSTEXPR] = "constexpr", [TCONTINUE] = "continue", [TDEFAULT] = "default", + [TDEFER] = "defer", [TDO] = "do", [TDOUBLE] = "double", [TELSE] = "else", |