aboutsummaryrefslogtreecommitdiff
path: root/src/prc1/cgen
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi92@disroot.org>2026-10-01 01:04:30 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2026-10-11 00:14:35 +0200
commit4ce39e4fc1de9536de7dedfe91b6dee38dec76ae (patch)
treef9820f96b8eab624430c3700f32a2878a7036ee2 /src/prc1/cgen
parent9140988be4e5bb028b7140aca1a67af1370c72a7 (diff)
downloadprc-4ce39e4fc1de9536de7dedfe91b6dee38dec76ae.tar.gz
WIP sym + cgen
Diffstat (limited to 'src/prc1/cgen')
-rw-r--r--src/prc1/cgen/CMakeLists.txt12
-rw-r--r--src/prc1/cgen/call.c8
-rw-r--r--src/prc1/cgen/fn.c8
-rw-r--r--src/prc1/cgen/free.c8
-rw-r--r--src/prc1/cgen/gl.c8
-rw-r--r--src/prc1/cgen/global.c32
-rw-r--r--src/prc1/cgen/param.c15
-rw-r--r--src/prc1/cgen/print.c8
-rw-r--r--src/prc1/cgen/prv.h23
-rw-r--r--src/prc1/cgen/qbe/CMakeLists.txt10
-rw-r--r--src/prc1/cgen/qbe/call.c163
-rw-r--r--src/prc1/cgen/qbe/fn.c94
-rw-r--r--src/prc1/cgen/qbe/free.c7
-rw-r--r--src/prc1/cgen/qbe/gl.c16
-rw-r--r--src/prc1/cgen/qbe/print.c95
-rw-r--r--src/prc1/cgen/qbe/qbe.c187
-rw-r--r--src/prc1/cgen/qbe/qbe.h26
-rw-r--r--src/prc1/cgen/qbe/set.c11
-rw-r--r--src/prc1/cgen/qbe/ws.c23
-rw-r--r--src/prc1/cgen/set.c8
-rw-r--r--src/prc1/cgen/ws.c8
21 files changed, 770 insertions, 0 deletions
diff --git a/src/prc1/cgen/CMakeLists.txt b/src/prc1/cgen/CMakeLists.txt
new file mode 100644
index 0000000..77aa786
--- /dev/null
+++ b/src/prc1/cgen/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_subdirectory(qbe)
+target_sources(prc1 PRIVATE
+ call.c
+ fn.c
+ free.c
+ gl.c
+ global.c
+ param.c
+ print.c
+ set.c
+ ws.c
+)
diff --git a/src/prc1/cgen/call.c b/src/prc1/cgen/call.c
new file mode 100644
index 0000000..dc3545b
--- /dev/null
+++ b/src/prc1/cgen/call.c
@@ -0,0 +1,8 @@
+#include "call.h"
+#include "cgen.h"
+#include "cgen/prv.h"
+
+int call_cgen(const struct call *m, struct cgen *c)
+{
+ return c->drv->call(m, c);
+}
diff --git a/src/prc1/cgen/fn.c b/src/prc1/cgen/fn.c
new file mode 100644
index 0000000..0a79a1d
--- /dev/null
+++ b/src/prc1/cgen/fn.c
@@ -0,0 +1,8 @@
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "fn.h"
+
+int fn_cgen(const struct fn *fn, struct cgen *c)
+{
+ return c->drv->fn(fn, c);
+}
diff --git a/src/prc1/cgen/free.c b/src/prc1/cgen/free.c
new file mode 100644
index 0000000..a7b88bb
--- /dev/null
+++ b/src/prc1/cgen/free.c
@@ -0,0 +1,8 @@
+#include "cgen.h"
+#include "cgen/prv.h"
+
+void cgen_free(struct cgen *c)
+{
+ if (c->drv)
+ c->drv->free(c->p);
+}
diff --git a/src/prc1/cgen/gl.c b/src/prc1/cgen/gl.c
new file mode 100644
index 0000000..8b436b8
--- /dev/null
+++ b/src/prc1/cgen/gl.c
@@ -0,0 +1,8 @@
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "gl.h"
+
+int gl_cgen(const struct storage *gl, struct cgen *c)
+{
+ return c->drv->gl(gl, c);
+}
diff --git a/src/prc1/cgen/global.c b/src/prc1/cgen/global.c
new file mode 100644
index 0000000..6371b96
--- /dev/null
+++ b/src/prc1/cgen/global.c
@@ -0,0 +1,32 @@
+#include "cgen.h"
+#include <string.h>
+
+int cgen_global(const struct fn *fn, const struct stentry *e)
+{
+ const struct storage *ws = fn->ws, *gl = fn->gl;
+ const struct pr *pr = fn->pr;
+ const struct param *ret = pr->ret;
+ const char *name = e->tk->s;
+
+ /* A variable is only global if it is defined inside the global
+ * variables section ("gl"), or if it is not listed on the working
+ * storage section ("ws"), as a function parameter or a return value. */
+
+ if (gl)
+ for (size_t i = 0; i < gl->nentries; i++)
+ if (!strcmp(gl->entries[i].tk->s, name))
+ return 1;
+
+ if (cgen_param(fn, e) || (ret && !strcmp(ret->tk->s, name)))
+ return 0;
+ else if (ws)
+ for (size_t i = 0; i < ws->nentries; i++)
+ if (!strcmp(ws->entries[i].tk->s, name))
+ return 0;
+
+ for (const struct tmp *t = fn->tmps; t; t = t->next)
+ if (!strcmp(t->tk.s, name))
+ return 0;
+
+ return 1;
+}
diff --git a/src/prc1/cgen/param.c b/src/prc1/cgen/param.c
new file mode 100644
index 0000000..e31f1f1
--- /dev/null
+++ b/src/prc1/cgen/param.c
@@ -0,0 +1,15 @@
+#include "cgen.h"
+#include <stddef.h>
+#include <string.h>
+
+int cgen_param(const struct fn *fn, const struct stentry *e)
+{
+ const struct pr *pr = fn->pr;
+ const char *name = e->tk->s;
+
+ for (size_t i = 0; i < pr->nparams; i++)
+ if (!strcmp(pr->params[i].tk->s, name))
+ return 1;
+
+ return 0;
+}
diff --git a/src/prc1/cgen/print.c b/src/prc1/cgen/print.c
new file mode 100644
index 0000000..32c01f3
--- /dev/null
+++ b/src/prc1/cgen/print.c
@@ -0,0 +1,8 @@
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "print.h"
+
+int print_cgen(const struct print *p, struct cgen *c, const char *f)
+{
+ return c->drv->print(p, c, f);
+}
diff --git a/src/prc1/cgen/prv.h b/src/prc1/cgen/prv.h
new file mode 100644
index 0000000..dc99404
--- /dev/null
+++ b/src/prc1/cgen/prv.h
@@ -0,0 +1,23 @@
+#ifndef CGEN_PRV_H
+#define CGEN_PRV_H
+
+#include "cgen.h"
+#include "call.h"
+#include "print.h"
+#include "set.h"
+#include "storage.h"
+
+struct cgen_drv
+{
+ int (*print)(const struct print *, struct cgen *, const char *);
+ int (*gl)(const struct storage *, struct cgen *);
+ int (*ws)(const struct storage *, struct cgen *);
+ int (*call)(const struct call *, struct cgen *);
+ int (*set)(const struct set *, struct cgen *);
+ int (*fn)(const struct fn *, struct cgen *);
+ void (*free)(struct cgen_prv *);
+};
+
+int cgen_qbe(const struct ast *ast, struct cgen *c);
+
+#endif
diff --git a/src/prc1/cgen/qbe/CMakeLists.txt b/src/prc1/cgen/qbe/CMakeLists.txt
new file mode 100644
index 0000000..5d0bd63
--- /dev/null
+++ b/src/prc1/cgen/qbe/CMakeLists.txt
@@ -0,0 +1,10 @@
+target_sources(prc1 PRIVATE
+ call.c
+ free.c
+ fn.c
+ gl.c
+ print.c
+ set.c
+ qbe.c
+ ws.c
+)
diff --git a/src/prc1/cgen/qbe/call.c b/src/prc1/cgen/qbe/call.c
new file mode 100644
index 0000000..0f49d76
--- /dev/null
+++ b/src/prc1/cgen/qbe/call.c
@@ -0,0 +1,163 @@
+#include "call.h"
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "qbe.h"
+#include <stdio.h>
+
+#if 0
+static int gettmp(char *tmp, size_t n)
+{
+ size_t j;
+
+ for (j = 0; j < n; j++)
+ {
+ char c = tmp[j];
+
+ if (!c)
+ return tmp[j] = 'a';
+ else if (c != 'z')
+ return ++(tmp[j]);
+ }
+
+ return EOF;
+}
+
+static int param_lit(const struct lit *lit, struct cgen *c)
+{
+ printf("l $%s", lit->name);
+ return 0;
+}
+
+static int param_id(const struct stentry *e, struct cgen *c, char *tmp,
+ size_t n)
+{
+ const char *name = e->tk->s;
+ const struct type *t = e->t;
+
+ printf("%s ", cgen_sz(t->sz));
+
+ if (cgen_global(c->fn, e))
+ printf("$%s", name);
+ else if (cgen_abity(t))
+ {
+ if (gettmp(tmp, n) == EOF)
+ return -1;
+
+ printf("%%%s", tmp);
+ }
+ else
+ printf("%%%s_", name);
+
+ return 0;
+}
+
+static int param_c(const struct type *t, struct cgen *cg)
+{
+ const union c *c = &t->u.c;
+
+ /* TODO: adapt type to expected param size */
+ fputs("l ", stdout);
+
+ if (t->sign)
+ printf("%lld", c->v);
+ else
+ printf("%llu", c->uv);
+
+ return 0;
+}
+
+static int param_num(const struct callparam *cp, struct cgen *c)
+{
+ const union callv *u = &cp->u;
+
+ /* TODO: adapt type to expected param size */
+ fputs("w ", stdout);
+
+ if (cp->sign)
+ printf("%lld", u->v);
+ else
+ printf("%llu", u->uv);
+
+ return 0;
+}
+
+static int param_load(const struct call *m, struct cgen *c)
+{
+ char tmp[sizeof "abcdefghijklmnopqrstuvwxyz"] = {0};
+
+ for (size_t i = 0; i < m->nparams; i++)
+ {
+ const struct callparam *cp = &m->params[i];
+ const struct stentry *e = cp->entry;
+
+ if (!e || cgen_global(c->fn, e) || !cgen_abity(e->t))
+ continue;
+ else if (gettmp(tmp, sizeof tmp - 1) == EOF)
+ {
+ fprintf(stderr, "%s: exhausted temporaries\n", __func__);
+ return -1;
+ }
+
+ printf("%%%s =%s load%s %%%s_\n", tmp, cgen_sz(e->t->sz),
+ cgen_load(e->t), e->tk->s);
+ }
+
+ return 0;
+}
+
+int call_cgen(const struct call *m, struct cgen *c)
+{
+ char tmps[sizeof "abcdefghijklmnopqrstuvwxyz"] = {0};
+ const struct stentry *ret = m->ret;
+ const char *tmp;
+ size_t sz;
+
+ if (param_load(m, c))
+ return -1;
+ else if (ret)
+ {
+ tmp = cgen_tmp((sz = ret->t->sz));
+ printf("%%%s =%s ", tmp, cgen_sz(sz));
+ }
+
+ printf("call $%s(", m->tk->s);
+
+ for (size_t i = 0; i < m->nparams; i++)
+ {
+ const struct callparam *cp = &m->params[i];
+ int var = m->pr->variadic;
+
+ if (cp->lit)
+ param_lit(cp->lit, c);
+ else if (cp->entry)
+ param_id(cp->entry, c, tmps, sizeof tmps - 1);
+ else if (cp->t)
+ param_c(cp->t, c);
+ else
+ param_num(cp, c);
+
+ if (var && i + 1 == var - 1)
+ fputs(", ...", stdout);
+
+ if (i + 1 < m->nparams)
+ fputs(", ", stdout);
+ }
+
+ puts(")");
+
+ if (ret)
+ {
+ printf("store%s %%%s, ", cgen_sz(sz), tmp);
+ cgen_printvar(c->fn, ret);
+ putchar('\n');
+ }
+
+ return 0;
+}
+#endif
+
+int qbe_call(const struct call *m, struct cgen *c)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
diff --git a/src/prc1/cgen/qbe/fn.c b/src/prc1/cgen/qbe/fn.c
new file mode 100644
index 0000000..61b0bf0
--- /dev/null
+++ b/src/prc1/cgen/qbe/fn.c
@@ -0,0 +1,94 @@
+#include "cgen.h"
+#include "fn.h"
+#include "gl.h"
+#include "stmt.h"
+#include "qbe.h"
+#include "ws.h"
+#include <stdio.h>
+
+int qbe_fn(const struct fn *fn, struct cgen *c)
+{
+ const struct pr *pr = fn->pr;
+ const struct param *ret = pr->ret;
+ const struct type *rt = NULL;
+ const char *s = NULL;
+ struct fn_cgen fnc = {0};
+
+ c->fnc = &fnc;
+
+ if (fn->gl && gl_cgen(fn->gl, c))
+ return -1;
+
+ switch (fn->linkage)
+ {
+ case STATIC:
+ printf("function ");
+ break;
+ case EXTERNAL:
+ printf("export function ");
+ break;
+ }
+
+ if (ret)
+ {
+ rt = ret->entry->t;
+ s = ret->entry->tk->s;
+ printf("%s ", cgen_abity(ret->entry->t));
+ }
+
+ printf("$%s(", fn->tk->s);
+
+ for (size_t i = 0; i < pr->nparams; i++)
+ {
+ const struct param *p = &pr->params[i];
+
+ printf(" %s", cgen_abity(p->entry->t));
+ printf(" %%__param_%s ", p->tk->s);
+
+ if (i + 1 < pr->nparams)
+ fputs(", ", stdout);
+ }
+
+ puts(") {\n@start");
+
+ for (size_t i = 0; i < pr->nparams; i++)
+ {
+ const struct param *p = &pr->params[i];
+ const struct type *t = p->entry->t;
+ const char *s = p->tk->s;
+
+ printf("%%%s_ =l alloc%zu %zu\n", s, t->align, t->sz);
+ printf("store%s %%__param_%s, %%%s_\n", cgen_sz(t->sz), s, s);
+ }
+
+ if (ret)
+ printf("%%%s_ =l alloc%zu %zu\n", s, rt->align, rt->sz);
+
+ if (fn->ws && ws_cgen(fn->ws, c))
+ return -1;
+
+ for (const struct tmp *tmp = fn->tmps; tmp; tmp = tmp->next)
+ {
+ const struct stentry *e = &tmp->e;
+ const struct type *t = tmp->e.t;
+
+ printf("%%%s_ =l alloc%zu %zu\n", e->tk->s, t->align, t->sz);
+ }
+
+ for (size_t i = 0; i < fn->nstmts; i++)
+ if (stmt_cgen(&fn->stmts[i], c))
+ return -1;
+
+ if (ret)
+ {
+ const char *t = cgen_tmp(rt->sz);
+
+ printf("%%%s =%s load%s %%%s_\n", t, cgen_sz(rt->sz), cgen_load(rt), s);
+ printf("ret %%%s", t);
+ }
+ else
+ fputs("ret", stdout);
+
+ puts("\n}");
+ return 0;
+}
diff --git a/src/prc1/cgen/qbe/free.c b/src/prc1/cgen/qbe/free.c
new file mode 100644
index 0000000..c05e72d
--- /dev/null
+++ b/src/prc1/cgen/qbe/free.c
@@ -0,0 +1,7 @@
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "qbe.h"
+
+void qbe_free(struct cgen_prv *p)
+{
+}
diff --git a/src/prc1/cgen/qbe/gl.c b/src/prc1/cgen/qbe/gl.c
new file mode 100644
index 0000000..61e9d7a
--- /dev/null
+++ b/src/prc1/cgen/qbe/gl.c
@@ -0,0 +1,16 @@
+#include "cgen.h"
+#include "qbe.h"
+#include <stddef.h>
+#include <stdio.h>
+
+int qbe_gl(const struct storage *gl, struct cgen *c)
+{
+ for (size_t i = 0; i < gl->nentries; i++)
+ {
+ const struct stentry *e = &gl->entries[i];
+
+ printf("export data $%s = { z %zu }\n", e->tk->s, e->t->sz);
+ }
+
+ return 0;
+}
diff --git a/src/prc1/cgen/qbe/print.c b/src/prc1/cgen/qbe/print.c
new file mode 100644
index 0000000..2890eb8
--- /dev/null
+++ b/src/prc1/cgen/qbe/print.c
@@ -0,0 +1,95 @@
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "print.h"
+#include "qbe.h"
+#include <stdio.h>
+
+static int cg_str(const struct prisym *s, struct cgen *c, const char *f)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+static int cg_ptr(const struct prisym *s, struct cgen *c, const char *f)
+{
+ const char *tmpf = cgen_tmp2(c), *tmpv = cgen_tmp2(c),
+ *name = s->s->tk->s;
+
+ if (!tmpf || !tmpv)
+ return -1;
+
+ printf("%%%s =l loadl ", tmpv);
+
+ if (cgen_global(c->fn, s->u.e))
+ printf("$%s", name);
+ else
+ printf("%%%s_", name);
+
+ fputc('\n', stdout);
+ printf("%%%s =l loadl $%s\n", tmpf, f);
+ printf("call $fprintf(w %%%s, l $____fmtlx, ..., l %%%s)\n", tmpf, tmpv);
+ return 0;
+}
+
+static int cg_num(const struct prisym *s, struct cgen *c, const char *f)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
+
+static int cg_nam(const struct prisym *ps, struct cgen *c, const char *f)
+{
+ const char *tmpf = cgen_tmp2(c), *name = ps->u.name;
+
+ if (!tmpf)
+ return -1;
+
+ printf("%%%s =l loadl $%s\n", tmpf, f);
+ printf("call $fprintf(w %%%s, l $____fmtlx, ..., l $%s)\n", tmpf, name);
+ return 0;
+}
+
+static int cg_lit(const struct prisym *s, struct cgen *c, const char *f)
+{
+ const struct lit *l = s->u.lit;
+ const char *tmp;
+
+ if (!l)
+ {
+ fprintf(stderr, "%s: unexpected null literal\n", __func__);
+ return -1;
+ }
+
+ /* TODO: ask target for sizeof (void *) */
+ tmp = cgen_tmp(sizeof (void *));
+ printf("%%%s =l loadl $%s\n", tmp, f);
+ printf("call $fputs(l $%s, w %%%s)\n", l->name, tmp);
+ return 0;
+}
+
+int qbe_print(const struct print *pr, struct cgen *c, const char *f)
+{
+ for (size_t i = 0; i < pr->nsyms; i++)
+ {
+ const struct prisym *ps = &pr->syms[i];
+ static int (*fn[])(const struct prisym *, struct cgen *, const char *) =
+ {
+ [PRS_LIT] = cg_lit,
+ [PRS_STR] = cg_str,
+ [PRS_NAM] = cg_nam,
+ [PRS_PTR] = cg_ptr,
+ [PRS_NUM] = cg_num
+ };
+
+ if (fn[ps->type](ps, c, f))
+ return -1;
+ }
+
+ if (!pr->skipln)
+ {
+ printf("%%v =l loadl $%s\n", f);
+ puts("call $fputc(l 10 , w %v)");
+ }
+
+ return 0;
+}
diff --git a/src/prc1/cgen/qbe/qbe.c b/src/prc1/cgen/qbe/qbe.c
new file mode 100644
index 0000000..c27bad6
--- /dev/null
+++ b/src/prc1/cgen/qbe/qbe.c
@@ -0,0 +1,187 @@
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "fn.h"
+#include "lex.h"
+#include "lit.h"
+#include "parse.h"
+#include "qbe.h"
+#include "storage.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+const char *cgen_sz(const size_t sz)
+{
+ switch (sz)
+ {
+ case 1:
+ return "b";
+ case 2:
+ return "h";
+ case 4:
+ return "w";
+ case 8:
+ return "l";
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+const char *cgen_tmp2(struct cgen *c)
+{
+ struct fn_cgen *fnc = c->fnc;
+
+ for (size_t i = 0; i < sizeof fnc->tmp - 1; i++)
+ {
+ char *s = &fnc->tmp[i];
+
+ if (!*s)
+ *s = 'a';
+ else if (*s >= 'z')
+ *s = 'A';
+ else if (*s >= 'Z')
+ continue;
+ else
+ (*s)++;
+
+ return s;
+ }
+
+ fprintf(stderr, "%s: ran out of temporaries\n", __func__);
+ return NULL;
+}
+
+const char *cgen_tmp(const size_t sz)
+{
+ switch (sz)
+ {
+ case 1:
+ return "vb";
+ case 2:
+ return "vh";
+ case 4:
+ return "vw";
+ case 8:
+ return "vl";
+ default:
+ break;
+ }
+
+ return "v";
+}
+
+const char *cgen_type(const struct type *t)
+{
+ switch (t->sz)
+ {
+ case 1:
+ return t->sign ? "b" : "ub";
+ case 2:
+ return t->sign ? "h" : "uh";
+ case 4:
+ return t->sign ? "w" : "uw";
+ case 8:
+ return t->sign ? "l" : "ul";
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+const char *cgen_abity(const struct type *t)
+{
+ switch (t->sz)
+ {
+ case 1:
+ return t->sign ? "b" : "ub";
+ case 2:
+ return t->sign ? "h" : "uh";
+ case 4:
+ return "w";
+ case 8:
+ return "l";
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+const char *cgen_load(const struct type *t)
+{
+ switch (t->sz)
+ {
+ case 1:
+ return t->sign ? "sb" : "ub";
+ case 2:
+ return t->sign ? "sh" : "uh";
+ case 4:
+ return t->sign ? "sw" : "uw";
+ case 8:
+ return "l";
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+void cgen_printvar(const struct fn *fn, const struct stentry *e)
+{
+ const char *name = e->tk->s;
+
+ if (cgen_global(fn, e))
+ printf("$%s", name);
+ else
+ printf("%%%s_", name);
+}
+
+static void pushfmt(const char *type, const char *fmt)
+{
+ printf("data $____fmt%s = { b \"%s\" , b 0 }\n", type, fmt);
+}
+
+int cgen_qbe(const struct ast *ast, struct cgen *c)
+{
+ static const struct cgen_drv drv =
+ {
+ .print = qbe_print,
+ .call = qbe_call,
+ .free = qbe_free,
+ .set = qbe_set,
+ .gl = qbe_gl,
+ .ws = qbe_ws,
+ .fn = qbe_fn
+ };
+
+ c->drv = &drv;
+ /* generate literals to use with printf(3) */
+ pushfmt("b", "%hhd");
+ pushfmt("h", "%hd");
+ pushfmt("w", "%d");
+ pushfmt("l", "%ld");
+ pushfmt("ub", "%hhu");
+ pushfmt("uh", "%hu");
+ pushfmt("uw", "%u");
+ pushfmt("ul", "%lu");
+ pushfmt("bx", "%#hhx");
+ pushfmt("hx", "%#hx");
+ pushfmt("wx", "%#x");
+ pushfmt("lx", "%#lx");
+
+ for (const struct lit *l = ast->lits; l; l = l->next)
+ if (lit_cgen(l))
+ return-1;
+
+ for (size_t i = 0; i < ast->nfns; i++)
+ if (fn_cgen((c->fn = &ast->fns[i]), c))
+ return -1;
+
+ return 0;
+}
diff --git a/src/prc1/cgen/qbe/qbe.h b/src/prc1/cgen/qbe/qbe.h
new file mode 100644
index 0000000..b0e66f1
--- /dev/null
+++ b/src/prc1/cgen/qbe/qbe.h
@@ -0,0 +1,26 @@
+#ifndef QBE_H
+#define QBE_H
+
+#include "call.h"
+#include "cgen.h"
+#include "print.h"
+#include "set.h"
+#include "storage.h"
+#include <stddef.h>
+
+const char *cgen_sz(const size_t sz);
+const char *cgen_tmp(const size_t sz);
+const char *cgen_tmp2(struct cgen *c);
+const char *cgen_type(const struct type *t);
+const char *cgen_abity(const struct type *t);
+const char *cgen_load(const struct type *t);
+int qbe_print(const struct print *m, struct cgen *c, const char *f);
+void cgen_printvar(const struct fn *fn, const struct stentry *e);
+int qbe_call(const struct call *m, struct cgen *c);
+int qbe_set(const struct set *m, struct cgen *c);
+int qbe_gl(const struct storage *gl, struct cgen *c);
+int qbe_ws(const struct storage *s, struct cgen *c);
+int qbe_fn(const struct fn *fn, struct cgen *c);
+void qbe_free(struct cgen_prv *p);
+
+#endif
diff --git a/src/prc1/cgen/qbe/set.c b/src/prc1/cgen/qbe/set.c
new file mode 100644
index 0000000..5dad08f
--- /dev/null
+++ b/src/prc1/cgen/qbe/set.c
@@ -0,0 +1,11 @@
+#include "call.h"
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "qbe.h"
+#include <stdio.h>
+
+int qbe_set(const struct set *s, struct cgen *c)
+{
+ fprintf(stderr, "%s: TODO\n", __func__);
+ return -1;
+}
diff --git a/src/prc1/cgen/qbe/ws.c b/src/prc1/cgen/qbe/ws.c
new file mode 100644
index 0000000..4f4a6df
--- /dev/null
+++ b/src/prc1/cgen/qbe/ws.c
@@ -0,0 +1,23 @@
+#include "cgen.h"
+#include "storage.h"
+#include "type.h"
+#include "qbe.h"
+#include <stddef.h>
+#include <stdio.h>
+
+static int wspush(const struct stentry *e, struct cgen *c)
+{
+ const struct type *t = e->t;
+
+ printf("%%%s_ =l alloc%zu %zu\n", e->tk->s, t->align, t->sz);
+ return 0;
+}
+
+int qbe_ws(const struct storage *s, struct cgen *c)
+{
+ for (size_t i = 0; i < s->nentries; i++)
+ if (wspush(&s->entries[i], c))
+ return -1;
+
+ return 0;
+}
diff --git a/src/prc1/cgen/set.c b/src/prc1/cgen/set.c
new file mode 100644
index 0000000..07e654e
--- /dev/null
+++ b/src/prc1/cgen/set.c
@@ -0,0 +1,8 @@
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "set.h"
+
+int set_cgen(const struct set *s, struct cgen *c)
+{
+ return c->drv->set(s, c);
+}
diff --git a/src/prc1/cgen/ws.c b/src/prc1/cgen/ws.c
new file mode 100644
index 0000000..a836e21
--- /dev/null
+++ b/src/prc1/cgen/ws.c
@@ -0,0 +1,8 @@
+#include "cgen.h"
+#include "cgen/prv.h"
+#include "ws.h"
+
+int ws_cgen(const struct storage *s, struct cgen *c)
+{
+ return c->drv->ws(s, c);
+}