diff options
author | Michael Forney <mforney@mforney.org> | 2024-04-26 18:31:18 -0700 |
---|---|---|
committer | Michael Forney <mforney@mforney.org> | 2024-04-27 02:48:40 -0700 |
commit | 186b7095eeb84bec527d6d7ac9320776a8b78734 (patch) | |
tree | 5a0da2807e8fa5a1d43aae307dc2316578c936ab | |
parent | 2b6c69c6b7f9dd4b30f00f3b1d9552c5b1c6077e (diff) |
Use hlt to implement noreturn
-rw-r--r-- | cc.h | 1 | ||||
-rw-r--r-- | decl.c | 2 | ||||
-rw-r--r-- | qbe.c | 29 | ||||
-rw-r--r-- | test/func-noreturn.c | 13 | ||||
-rw-r--r-- | test/func-noreturn.qbe | 28 |
5 files changed, 68 insertions, 5 deletions
@@ -556,6 +556,7 @@ struct value *funcexpr(struct func *, struct expr *); void funcjmp(struct func *, struct block *); void funcjnz(struct func *, struct value *, struct type *, struct block *, struct block *); void funcret(struct func *, struct value *); +void funchlt(struct func *); struct gotolabel *funcgoto(struct func *, char *); void funcswitch(struct func *, struct value *, struct switchcases *, struct block *); void funcinit(struct func *, struct decl *, struct init *, bool); @@ -1066,6 +1066,8 @@ decl(struct scope *s, struct func *f) s = funcscope; f = mkfunc(d, name, t, s); stmt(f, s); + if (d->u.func.isnoreturn) + funchlt(f); /* XXX: need to keep track of function in case a later declaration specifies extern */ if (!d->u.func.inlinedefn) emitfunc(f, d->linkage == LINKEXTERN); @@ -62,6 +62,7 @@ struct jump { JUMP_JMP, JUMP_JNZ, JUMP_RET, + JUMP_HLT, } kind; struct value *arg; struct block *blk[2]; @@ -598,6 +599,15 @@ funcret(struct func *f, struct value *v) } } +void +funchlt(struct func *f) +{ + struct block *b = f->end; + + if (!b->jump.kind) + b->jump.kind = JUMP_HLT; +} + struct gotolabel * funcgoto(struct func *f, char *name) { @@ -631,7 +641,7 @@ funclval(struct func *f, struct expr *e) case EXPRIDENT: d = e->u.ident.decl; if (d->kind != DECLOBJECT && d->kind != DECLFUNC) - error(&tok.loc, "identifier is not an object or function"); /* XXX: fix location, var name */ + error(&tok.loc, "identifier '%s' is not an object or function", d->name); if (d == f->namedecl) { fputs("data ", stdout); emitname(d->value); @@ -727,10 +737,12 @@ funcexpr(struct func *f, struct expr *e) t = arg->type; funcinst(f, IARG, qbetype(t).base, argvals[i], t->value); } - /* - if (functype->func.isnoreturn) - funcret(f, NULL); - */ + e = e->base; + if (e->kind == EXPRUNARY && e->op == TBAND) { + e = e->base; + if (e->kind == EXPRIDENT && e->u.ident.decl->u.func.isnoreturn) + funchlt(f); + } return v; case EXPRUNARY: switch (e->op) { @@ -1187,6 +1199,8 @@ static void emitjump(struct jump *j) { switch (j->kind) { + case JUMP_NONE: + break; case JUMP_RET: fputs("\tret", stdout); if (j->arg) { @@ -1209,6 +1223,11 @@ emitjump(struct jump *j) emitname(&j->blk[1]->label); putchar('\n'); break; + case JUMP_HLT: + fputs("\thlt\n", stdout); + break; + default: + assert(0); } } diff --git a/test/func-noreturn.c b/test/func-noreturn.c new file mode 100644 index 0000000..0cd7f7a --- /dev/null +++ b/test/func-noreturn.c @@ -0,0 +1,13 @@ +_Noreturn void exit(int); +int puts(const char *); +void _Noreturn f(void) { + static int c; + while (c) + puts("loop"); +} +int main(void) { + exit(0); + (*f)(); + (***f)(); + return 1; +} diff --git a/test/func-noreturn.qbe b/test/func-noreturn.qbe new file mode 100644 index 0000000..d1771dc --- /dev/null +++ b/test/func-noreturn.qbe @@ -0,0 +1,28 @@ +data $.Lc.2 = align 4 { z 4 } +data $.Lstring.3 = align 1 { b "loop\000", } +export +function $f() { +@start.1 +@body.2 +@while_cond.3 + %.1 =w loadw $.Lc.2 + jnz %.1, @while_body.4, @while_join.5 +@while_body.4 + %.2 =w call $puts(l $.Lstring.3) + jmp @while_cond.3 +@while_join.5 + hlt +} +export +function w $main() { +@start.6 +@body.7 + call $exit(w 0) + hlt +@dead.8 + call $f() + hlt +@dead.9 + call $f() + hlt +} |