From 3e51be9d94e561549281e386ce1b7554018ef4dd Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Wed, 6 Nov 2024 02:46:44 +0100 Subject: cproc: Implement defer statement. 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. --- cc.h | 4 ++++ pp.c | 1 + qbe.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++------- stmt.c | 14 ++++++++++++++ token.c | 1 + 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); @@ -565,17 +572,49 @@ functype(struct func *f) return f->type; } +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", -- cgit v1.2.3