aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cc.h1
-rw-r--r--decl.c2
-rw-r--r--qbe.c29
-rw-r--r--test/func-noreturn.c13
-rw-r--r--test/func-noreturn.qbe28
5 files changed, 68 insertions, 5 deletions
diff --git a/cc.h b/cc.h
index 1cd1b32..ea237c5 100644
--- a/cc.h
+++ b/cc.h
@@ -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);
diff --git a/decl.c b/decl.c
index 0749017..39c272d 100644
--- a/decl.c
+++ b/decl.c
@@ -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);
diff --git a/qbe.c b/qbe.c
index a6781dd..5c76bb2 100644
--- a/qbe.c
+++ b/qbe.c
@@ -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
+}