aboutsummaryrefslogtreecommitdiffhomepage
path: root/intrin.c
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2023-06-13 20:03:46 +0200
committerlemon <lsof@mailbox.org>2023-06-13 20:07:48 +0200
commit85530429ac0c5512d51cf52fa07022452791c1c4 (patch)
treeee3dfe52ded21fcbb15c3a1dbb71d929274cbdb7 /intrin.c
parentca85b61809d976164139eed20f063c596f7b9b75 (diff)
lowering of structcopy
Diffstat (limited to 'intrin.c')
-rw-r--r--intrin.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/intrin.c b/intrin.c
new file mode 100644
index 0000000..6a3bc7f
--- /dev/null
+++ b/intrin.c
@@ -0,0 +1,77 @@
+#include "common.h"
+#include "ir.h"
+
+struct arg { union ref *arg, *ty; };
+
+static int
+intrin(struct block *blk, int *curi, enum intrin in, struct arg *args, int narg, union irtype ret)
+{
+ struct instr *this = &instrtab[blk->ins.p[*curi]];
+ const struct typedata *td;
+ union irtype ty;
+ uint ncopy, step;
+
+ switch (in) {
+ case 0: assert(0);
+ case INstructcopy:
+ assert(narg == 2 && args[0].ty->bits == args[1].ty->bits);
+ ty = ref2type(*args[0].ty);
+ assert(ty.isagg);
+ td = &typedata[ty.cls];
+ step = td->align <= 8 ? td->align : 8;
+ ncopy = td->siz / step;
+ if (ncopy > 4) {
+ enum irclass cls = siz2intcls[cls2siz[KPTR]];
+ /* memcpy */
+ *args[1].ty = *args[0].ty = mktyperef(cls2type(KPTR));
+ insertinstr(blk, (*curi)++, mkarginstr(cls2type(cls), mkintcon(cls, td->siz)));
+ *this = mkinstr(Ocall, 0, mksymref("memcpy"), this->r);
+ calltab.p[this->r.i].narg = 3;
+ calltab.p[this->r.i].ret = cls2type(0);
+ return 0;
+ } else {
+ delinstr(blk, (*curi)--);
+ for (int off = 0; off < td->siz; off += step) {
+ union ref psrc = *args[1].arg, pdst = *args[0].arg, src;
+ if (off) {
+ pdst = mkaddr((struct addr) {.base = *args[0].arg, .disp = off});
+ psrc = mkaddr((struct addr) {.base = *args[1].arg, .disp = off});
+ }
+ src = insertinstr(blk, ++*curi, mkinstr(Oloads1 + 2*ilog2(step), step < 8 ? KI4 : KI8, psrc));
+ insertinstr(blk, ++*curi, mkinstr(Ostore1 + ilog2(step), 0, pdst, src));
+ }
+ return 1;
+ }
+ }
+ assert(0);
+}
+
+void
+lowerintrin(struct function *fn)
+{
+ struct block *blk = fn->entry;
+ static struct arg argsbuf[64];
+ vec_of(struct arg) args = VINIT(argsbuf, arraylength(argsbuf));
+
+ do {
+ for (int i = 0; i < blk->ins.n; ++i) {
+ struct instr *ins = &instrtab[blk->ins.p[i]];
+ if (ins->op == Oarg)
+ vpush(&args, ((struct arg){ &ins->r, &ins->l }));
+ else if (ins->op == Ocall)
+ vinit(&args, argsbuf, arraylength(argsbuf));
+ else if (ins->op == Ointrin) {
+ int arg0 = i - args.n;
+ assert(calltab.p[ins->r.i].narg == args.n);
+ if (intrin(blk, &i, ins->l.i, args.p, args.n, calltab.p[ins->r.i].ret))
+ for (int j = args.n; j > 0; --j, --i)
+ delinstr(blk, arg0);
+ else
+ abi0_call(fn, ins, blk, &i);
+ vinit(&args, argsbuf, arraylength(argsbuf));
+ } else if (ins->op != Omove) assert(args.n == 0);
+ }
+ } while ((blk = blk->lnext) != fn->entry);
+}
+
+/* vim:set ts=3 sw=3 expandtab: */