aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2019-02-13 15:28:40 -0800
committerMichael Forney <mforney@mforney.org>2019-02-13 19:42:25 -0800
commita0c394bdd0f0c554f504ee8119e8304551653562 (patch)
treee11fe7fd0679b7a1b47717aed632f918d888c797
parentabccebea439d961c39e4a6f7d54f72e0b24e9100 (diff)
Implement __builtin_va_arg
-rw-r--r--decl.c1
-rw-r--r--decl.h2
-rw-r--r--expr.c7
-rw-r--r--expr.h1
-rw-r--r--main.c1
-rw-r--r--qbe.c3
-rw-r--r--tests/varargs.c12
-rw-r--r--tests/varargs.qbe22
8 files changed, 48 insertions, 1 deletions
diff --git a/decl.c b/decl.c
index 235c975..4c89753 100644
--- a/decl.c
+++ b/decl.c
@@ -19,6 +19,7 @@
#include "type.h"
struct declaration builtinvastart = {.kind = DECLBUILTIN};
+struct declaration builtinvaarg = {.kind = DECLBUILTIN};
struct declaration builtinvaend = {.kind = DECLBUILTIN};
struct declaration builtinoffsetof = {.kind = DECLBUILTIN};
diff --git a/decl.h b/decl.h
index 4b3ef20..3e3c5af 100644
--- a/decl.h
+++ b/decl.h
@@ -25,7 +25,7 @@ struct declaration {
struct scope;
struct function;
-extern struct declaration builtinvastart, builtinvaend, builtinoffsetof;
+extern struct declaration builtinvastart, builtinvaarg, builtinvaend, builtinoffsetof;
struct declaration *mkdecl(enum declarationkind, struct type *, enum linkage);
_Bool decl(struct scope *, struct function *);
diff --git a/expr.c b/expr.c
index 70d8dbf..5973f67 100644
--- a/expr.c
+++ b/expr.c
@@ -410,6 +410,13 @@ postfixexpr(struct scope *s, struct expression *r)
free(expect(TIDENT, "after ','"));
// XXX: check that this was actually a parameter name?
expect(TRPAREN, "after parameter identifier");
+ } else if (r->ident.decl == &builtinvaarg) {
+ e = mkexpr(EXPRBUILTIN, NULL, 0);
+ e->builtin.kind = BUILTINVAARG;
+ e->builtin.arg = exprconvert(assignexpr(s), &typevalistptr);
+ expect(TCOMMA, "after va_list");
+ e->type = typename(s);
+ expect(TRPAREN, "after typename");
} else if (r->ident.decl == &builtinvaend) {
e = mkexpr(EXPRBUILTIN, &typevoid, 0);
e->builtin.kind = BUILTINVAEND;
diff --git a/expr.h b/expr.h
index 8269d22..5c8ef96 100644
--- a/expr.h
+++ b/expr.h
@@ -79,6 +79,7 @@ struct expression {
struct {
enum {
BUILTINVASTART,
+ BUILTINVAARG,
BUILTINVAEND,
} kind;
struct expression *arg;
diff --git a/main.c b/main.c
index fa50078..c8491eb 100644
--- a/main.c
+++ b/main.c
@@ -54,6 +54,7 @@ main(int argc, char *argv[])
}
} else {
scopeputdecl(&filescope, "__builtin_va_start", &builtinvastart);
+ scopeputdecl(&filescope, "__builtin_va_arg", &builtinvaarg);
scopeputdecl(&filescope, "__builtin_va_end", &builtinvaend);
scopeputdecl(&filescope, "__builtin_offsetof", &builtinoffsetof);
while (tok.kind != TEOF) {
diff --git a/qbe.c b/qbe.c
index fad1858..fde25e2 100644
--- a/qbe.c
+++ b/qbe.c
@@ -777,6 +777,9 @@ funcexpr(struct function *f, struct expression *e)
l = funcexpr(f, e->builtin.arg);
funcinst(f, IVASTART, NULL, (struct value *[]){l});
break;
+ case BUILTINVAARG:
+ l = funcexpr(f, e->builtin.arg);
+ return funcinst(f, IVAARG, e->type->repr, (struct value *[]){l});
case BUILTINVAEND:
/* no-op */
break;
diff --git a/tests/varargs.c b/tests/varargs.c
new file mode 100644
index 0000000..0b5a73d
--- /dev/null
+++ b/tests/varargs.c
@@ -0,0 +1,12 @@
+void f(int n, ...) {
+ __builtin_va_list ap;
+
+ __builtin_va_start(ap, n);
+ while (n) {
+ __builtin_va_arg(ap, int);
+ __builtin_va_arg(ap, float);
+ __builtin_va_arg(ap, char *);
+ --n;
+ }
+ __builtin_va_end(ap);
+}
diff --git a/tests/varargs.qbe b/tests/varargs.qbe
new file mode 100644
index 0000000..16db877
--- /dev/null
+++ b/tests/varargs.qbe
@@ -0,0 +1,22 @@
+export
+function $f(w %.1, ...) {
+@start.1
+ %.2 =l alloc4 4
+ storew %.1, %.2
+ %.3 =l alloc8 24
+@body.2
+ vastart %.3
+@while_cond.3
+ %.4 =w loadsw %.2
+ jnz %.4, @while_body.4, @while_join.5
+@while_body.4
+ %.5 =w vaarg %.3
+ %.6 =s vaarg %.3
+ %.7 =l vaarg %.3
+ %.8 =w loadsw %.2
+ %.9 =w sub %.8, 1
+ storew %.9, %.2
+ jmp @while_cond.3
+@while_join.5
+ ret
+}