~mpu/qbe

m: Add 32-bit target support v1 PROPOSED

John Nunley: 1
 m: Add 32-bit target support

 13 files changed, 71 insertions(+), 39 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~mpu/qbe/patches/56712/mbox | git am -3
Learn more about email & git

[PATCH] m: Add 32-bit target support Export this patch

This commit re-arranges the internal plumbing of QBE to allow support
for 32-bit targets. Previously, there was a significant amount of code
that assumed that word sizes/register sizes would always be Kl (8
bytes). This commit aims to remove those assumptions.

A new field is added to "struct Target" named "align". The word size of
the target is defined as "2^align" bytes. For all existing targets (i.e.
64-bit targets) it is set to 3.

This commit then changes the machinery in "parse.c" to accomodate the
new "align" field. Firstly, "Km" is no longer an alias for "Kl", and the
resulting operations are set appropriately. Second, if it detects the
usage of "Kl" in any of the instructions and "T.align" is less than 3,
it will raise an error. This is in line with previous discussion on how
to handle "Kl" on 32-bit targets.

Finally, this commit removes "NAlign", which previously hardcoded the
word size to be "2^3". It also modifies code that previously assumed
"NAlign == 3" to consider "T.align" instead.

Signed-off-by: John Nunley <dev@notgull.net>
---
 all.h        |  5 ++++-
 amd64/emit.c |  4 ++--
 amd64/sysv.c |  4 ++--
 amd64/targ.c |  1 +
 arm64/abi.c  |  2 +-
 arm64/isel.c |  2 +-
 arm64/targ.c |  1 +
 mem.c        |  2 +-
 parse.c      | 43 +++++++++++++++++++++++++++++++++----------
 rv64/abi.c   |  2 +-
 rv64/isel.c  |  2 +-
 rv64/targ.c  |  1 +
 spill.c      | 41 ++++++++++++++++++++++-------------------
 13 files changed, 71 insertions(+), 39 deletions(-)

diff --git a/all.h b/all.h
index 97cc41c..c13697e 100644
--- a/all.h
+++ b/all.h
@@ -36,7 +36,6 @@ typedef struct Target Target;
enum {
	NString = 80,
	NIns    = 1 << 20,
	NAlign  = 3,
	NField  = 32,
	NBit    = CHAR_BIT * sizeof(bits),
};
@@ -44,6 +43,7 @@ enum {
struct Target {
	char name[16];
	char apple;
	int align;
	int gpr0;   /* first general purpose reg */
	int ngpr;
	int fpr0;   /* first floating point reg */
@@ -64,6 +64,9 @@ struct Target {
	char assym[4];
};

#define Kmem(T)  (T.align >= 3 ? Kl : Kw)
#define WIDTH(T) ((2 << (T.align - 1)) * 8)

#define BIT(n) ((bits)1 << (n))

enum {
diff --git a/amd64/emit.c b/amd64/emit.c
index 8f36188..2cd8e3e 100644
--- a/amd64/emit.c
+++ b/amd64/emit.c
@@ -158,7 +158,7 @@ slot(Ref r, E *e)

	s = rsval(r);
	assert(s <= e->fn->slot);
	/* specific to NAlign == 3 */
	/* specific to T.align == 3 */
	if (s < 0) {
		if (e->fp == RSP)
			return 4*-s - 8 + e->fsz + e->nclob*8;
@@ -584,7 +584,7 @@ framesz(E *e)
{
	uint64_t i, o, f;

	/* specific to NAlign == 3 */
	/* specific to T.align == 3 */
	o = 0;
	if (!e->fn->leaf) {
		for (i=0, o=0; i<NCLR; i++)
diff --git a/amd64/sysv.c b/amd64/sysv.c
index 5b559c1..7675a24 100644
--- a/amd64/sysv.c
+++ b/amd64/sysv.c
@@ -361,7 +361,7 @@ selcall(Fn *fn, Ins *i0, Ins *i1, RAlloc **rap)
		}
		/* allocate return pad */
		ra = alloc(sizeof *ra);
		/* specific to NAlign == 3 */
		/* specific to T.align == 3 */
		al = aret.align >= 2 ? aret.align - 2 : 0;
		ra->i = (Ins){Oalloc+al, Kl, r1, {getcon(aret.size, fn)}};
		ra->link = (*rap);
@@ -456,7 +456,7 @@ selpar(Fn *fn, Ins *i0, Ins *i1)
		}
		a->ref[0] = newtmp("abi", Kl, fn);
		emit(Ostorel, 0, R, a->ref[0], i->to);
		/* specific to NAlign == 3 */
		/* specific to T.align == 3 */
		al = a->align >= 2 ? a->align - 2 : 0;
		emit(Oalloc+al, Kl, i->to, getcon(a->size, fn), R);
	}
diff --git a/amd64/targ.c b/amd64/targ.c
index fba9144..db8b9ac 100644
--- a/amd64/targ.c
+++ b/amd64/targ.c
@@ -13,6 +13,7 @@ amd64_memargs(int op)
}

#define AMD64_COMMON \
	.align = 3, \
	.gpr0 = RAX, \
	.ngpr = NGPR, \
	.fpr0 = XMM0, \
diff --git a/arm64/abi.c b/arm64/abi.c
index b9e6a14..c22b30c 100644
--- a/arm64/abi.c
+++ b/arm64/abi.c
@@ -348,7 +348,7 @@ stkblob(Ref r, Class *c, Fn *fn, Insl **ilp)
	uint64_t sz;

	il = alloc(sizeof *il);
	al = c->t->align - 2; /* NAlign == 3 */
	al = c->t->align - 2; /* T.align == 3 */
	if (al < 0)
		al = 0;
	sz = c->class & Cptr ? c->t->size : c->size;
diff --git a/arm64/isel.c b/arm64/isel.c
index 9ce6adc..936b033 100644
--- a/arm64/isel.c
+++ b/arm64/isel.c
@@ -279,7 +279,7 @@ arm64_isel(Fn *fn)

	/* assign slots to fast allocs */
	b = fn->start;
	/* specific to NAlign == 3 */ /* or change n=4 and sz /= 4 below */
	/* specific to T.align == 3 */ /* or change n=4 and sz /= 4 below */
	for (al=Oalloc, n=4; al<=Oalloc1; al++, n*=2)
		for (i=b->ins; i<&b->ins[b->nins]; i++)
			if (i->op == al) {
diff --git a/arm64/targ.c b/arm64/targ.c
index 232376d..2b3fe55 100644
--- a/arm64/targ.c
+++ b/arm64/targ.c
@@ -26,6 +26,7 @@ arm64_memargs(int op)
}

#define ARM64_COMMON \
	.align = 3, \
	.gpr0 = R0, \
	.ngpr = NGPR, \
	.fpr0 = V0, \
diff --git a/mem.c b/mem.c
index 3265b39..8b7435e 100644
--- a/mem.c
+++ b/mem.c
@@ -19,7 +19,7 @@ promote(Fn *fn)
	for (i=b->ins; i<&b->ins[b->nins]; i++) {
		if (Oalloc > i->op || i->op > Oalloc1)
			continue;
		/* specific to NAlign == 3 */
		/* specific to NAlign == 3 */ /* TODO: how? */
		assert(rtype(i->to) == RTmp);
		t = &fn->tmp[i->to.val];
		if (t->ndef != 1)
diff --git a/parse.c b/parse.c
index e896679..a913676 100644
--- a/parse.c
+++ b/parse.c
@@ -11,7 +11,7 @@ enum {
	K0,

	Ke = -2, /* erroneous mode */
	Km = Kl, /* memory pointer */
	Km = -3  /* memory pointer */ 
};

Op optab[NOp] = {
@@ -480,6 +480,8 @@ parsecls(int *tyn)
	case Tw:
		return Kw;
	case Tl:
		if (WIDTH(T) < 64)
			err("cannot use 64-bit integer on %d-bit target", WIDTH(T));
		return Kl;
	case Ts:
		return Ks;
@@ -519,7 +521,7 @@ parserefl(int arg)
			hasenv = 1;
			env = 1;
			next();
			k = Kl;
			k = Kmem(T);
			break;
		default:
			env = 0;
@@ -538,9 +540,9 @@ parserefl(int arg)
				*curi = (Ins){Opare, k, r, {R}};
		else if (k == Kc)
			if (arg)
				*curi = (Ins){Oargc, Kl, R, {TYPE(ty), r}};
				*curi = (Ins){Oargc, Kmem(T), R, {TYPE(ty), r}};
			else
				*curi = (Ins){Oparc, Kl, r, {TYPE(ty)}};
				*curi = (Ins){Oparc, Kmem(T), r, {TYPE(ty)}};
		else if (k >= Ksb)
			if (arg)
				*curi = (Ins){Oargsb+(k-Ksb), Kw, R, {r}};
@@ -700,7 +702,7 @@ parseline(PState ps)
		op = Ocall;
		expect(Tnl);
		if (k == Kc) {
			k = Kl;
			k = Kmem(T);
			arg[1] = TYPE(ty);
		}
		if (k >= Ksb)
@@ -831,6 +833,8 @@ typecheck(Fn *fn)
			t = &fn->tmp[p->to.val];
			for (n=0; n<p->narg; n++) {
				k = t->cls;
				if (k == Km)
					k = t->cls = Kmem(T);
				if (bshas(ppb, p->blk[n]->id))
					err("multiple entries for @%s in phi %%%s",
						p->blk[n]->name, t->name);
@@ -842,11 +846,16 @@ typecheck(Fn *fn)
			if (!bsequal(pb, ppb))
				err("predecessors not matched in phi %%%s", t->name);
		}
		for (i=b->ins; i<&b->ins[b->nins]; i++)
		for (i=b->ins; i<&b->ins[b->nins]; i++) {
			if (WIDTH(T) < 64 && i->cls == Kl)
				err("cannot use 64-bit integer for %d-bit taget for op %s",
					WIDTH(T), optab[i->op].name);
			for (n=0; n<2; n++) {
				k = optab[i->op].argcls[n][i->cls];
				r = i->arg[n];
				t = &fn->tmp[r.val];
				if (k == Km)
					k = Kmem(T);
				if (k == Ke)
					err("invalid instruction type in %s",
						optab[i->op].name);
@@ -865,10 +874,11 @@ typecheck(Fn *fn)
						n == 1 ? "second" : "first",
						t->name, optab[i->op].name);
			}
		}
		r = b->jmp.arg;
		if (isret(b->jmp.type)) {
			if (b->jmp.type == Jretc)
				k = Kl;
				k = Kmem(T);
			else if (b->jmp.type >= Jretsb)
				k = Kw;
			else
@@ -891,9 +901,22 @@ static Fn *
parsefn(Lnk *lnk)
{
	Blk *b;
	int i;
	int i, greg, freg;
	PState ps;

	switch (WIDTH(T)) {
	case 32:
	greg = Kw;
	freg = Ks;
	break;
	case 64:
	greg = Kl;
	freg = Kd;
	break;
	default:
	die("unreachable");
	}

	curb = 0;
	nblk = 0;
	curi = insb;
@@ -904,9 +927,9 @@ parsefn(Lnk *lnk)
	curf->con = vnew(curf->ncon, sizeof curf->con[0], PFn);
	for (i=0; i<Tmp0; ++i)
		if (T.fpr0 <= i && i < T.fpr0 + T.nfpr)
			newtmp(0, Kd, curf);
			newtmp(0, freg, curf);
		else
			newtmp(0, Kl, curf);
			newtmp(0, greg, curf);
	curf->con[0].type = CBits;
	curf->con[0].bits.i = 0xdeaddead;  /* UNDEF */
	curf->con[1].type = CBits;
diff --git a/rv64/abi.c b/rv64/abi.c
index e31425c..965b5ef 100644
--- a/rv64/abi.c
+++ b/rv64/abi.c
@@ -326,7 +326,7 @@ stkblob(Ref r, Typ *t, Fn *fn, Insl **ilp)
	uint64_t sz;

	il = alloc(sizeof *il);
	al = t->align - 2; /* specific to NAlign == 3 */
	al = t->align - 2; /* specific to T.align == 3 */
	if (al < 0)
		al = 0;
	sz = (t->size + 7) & ~7;
diff --git a/rv64/isel.c b/rv64/isel.c
index 8921a07..6d3af25 100644
--- a/rv64/isel.c
+++ b/rv64/isel.c
@@ -215,7 +215,7 @@ rv64_isel(Fn *fn)

	/* assign slots to fast allocs */
	b = fn->start;
	/* specific to NAlign == 3 */ /* or change n=4 and sz /= 4 below */
	/* specific to T.align == 3 */ /* or change n=4 and sz /= 4 below */
	for (al=Oalloc, n=4; al<=Oalloc1; al++, n*=2)
		for (i=b->ins; i<&b->ins[b->nins]; i++)
			if (i->op == al) {
diff --git a/rv64/targ.c b/rv64/targ.c
index c0e5e18..50ba153 100644
--- a/rv64/targ.c
+++ b/rv64/targ.c
@@ -33,6 +33,7 @@ rv64_memargs(int op)

Target T_rv64 = {
	.name = "rv64",
	.align = 3,
	.gpr0 = T0,
	.ngpr = NGPR,
	.fpr0 = FT0,
diff --git a/spill.c b/spill.c
index 2ce1d4f..1926773 100644
--- a/spill.c
+++ b/spill.c
@@ -129,25 +129,29 @@ slot(int t)
	assert(t >= Tmp0 && "cannot spill register");
	s = tmp[t].slot;
	if (s == -1) {
		/* specific to NAlign == 3 */
		/* nice logic to pack stack slots
		 * on demand, there can be only
		 * one hole and slot4 points to it
		 *
		 * invariant: slot4 <= slot8
		 */
		if (KWIDE(tmp[t].cls)) {
			s = slot8;
			if (slot4 == slot8)
				slot4 += 2;
			slot8 += 2;
		if (T.align > 2) {
			/* nice logic to pack stack slots
			* on demand, there can be only
			* one hole and slot4 points to it
			*
			* invariant: slot4 <= slot8
			*/
			if (KWIDE(tmp[t].cls)) {
				s = slot8;
				if (slot4 == slot8)
					slot4 += 2;
				slot8 += 2;
			} else {
				s = slot4;
				if (slot4 == slot8) {
					slot8 += 2;
					slot4 += 1;
				} else
					slot4 = slot8;
			}
		} else {
			s = slot4;
			if (slot4 == slot8) {
				slot8 += 2;
				slot4 += 1;
			} else
				slot4 = slot8;
			slot4 += 1;
		}
		s += locs;
		tmp[t].slot = s;
@@ -522,8 +526,7 @@ spill(Fn *fn)
	}

	/* align the locals to a 16 byte boundary */
	/* specific to NAlign == 3 */
	slot8 += slot8 & 3;
	slot8 += slot8 & T.align;
	fn->slot += slot8;

	if (debug['S']) {
-- 
2.34.1
Nice! Haven't looked at it in much detail but yes, agreed that the
various "assumes align == 3" are somewhat opaque to me too.

Minor quibble - I prefer the term "order" (size is 1 << order) instead
of "align", following linux practice, but understand the QBE heritage.