aboutsummaryrefslogtreecommitdiffhomepage
path: root/ir
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2025-12-06 18:59:53 +0100
committerlemon <lsof@mailbox.org>2025-12-06 18:59:53 +0100
commitf7d68175fc2b52230b8f78bf78145c7f1d0ad9c5 (patch)
tree099243c78aaef479daee0b2ab5e8dc8215cea9e3 /ir
parentd82f3052c813f671561362126d0fbe08568542d3 (diff)
abi: fix aggregate passed by regs 2nd reg offset
It was broken for example `struct { i32 a; f64 b; }` (would try to load/store b from byte offset 4, not 8). Introduce r2off, realize in x86-64 it's always 8; even `struct {i32 a; f32 b;}` gets passed in one (integer) register. But not so in (future) ABIs like RISC-V, I believe there `{i32, f32}` would get passed in 1 integer and 1 float register (r2off = 4).
Diffstat (limited to 'ir')
-rw-r--r--ir/abi0.c43
-rw-r--r--ir/ir.h9
2 files changed, 28 insertions, 24 deletions
diff --git a/ir/abi0.c b/ir/abi0.c
index 331d9be..00090e3 100644
--- a/ir/abi0.c
+++ b/ir/abi0.c
@@ -10,13 +10,13 @@
struct abiargsvec { vec_of(struct abiarg); };
static int
-abiret(struct abiarg abiret[2], struct abiargsvec *abiargs, int *ni, union irtype retty)
+abiret(struct abiarg abiret[2], struct abiargsvec *abiargs, uchar *r2off, int *ni, union irtype retty)
{
short r[2];
uchar cls[2];
int retreg = 0;
+ retreg = mctarg->abiret(r, cls, r2off, ni, retty);
if (retty.isagg) {
- retreg = mctarg->abiret(r, cls, ni, retty);
if (!retreg) {
vpush(abiargs, ((struct abiarg) { cls2type(KPTR), .reg = r[1] }));
if (r[0] == -1) {
@@ -27,7 +27,6 @@ abiret(struct abiarg abiret[2], struct abiargsvec *abiargs, int *ni, union irtyp
}
}
} else if (retty.cls) {
- retreg = mctarg->abiret(r, cls, ni, retty);
assert(retreg == 1);
}
for (int i = 0; i < retreg; ++i) {
@@ -39,11 +38,11 @@ abiret(struct abiarg abiret[2], struct abiargsvec *abiargs, int *ni, union irtyp
}
static int
-abiarg(struct abiargsvec *abiargs, int *ni, int *nf, int *ns, union irtype ty)
+abiarg(struct abiargsvec *abiargs, uchar *r2off, int *ni, int *nf, int *ns, union irtype ty)
{
short r[2];
uchar cls[2];
- int ret = mctarg->abiarg(r, cls, ni, nf, ns, ty);
+ int ret = mctarg->abiarg(r, cls, r2off, ni, nf, ns, ty);
if (!ret) { /* in stack */
vpush(abiargs, ((struct abiarg) { ty, .isstk = 1, .stk = r[0] }));
} else if (ret == 1 && ty.isagg && cls[0] == KPTR) { /* aggregate by pointer */
@@ -84,7 +83,7 @@ copyparam(struct function *fn, int *curi, int param, struct abiarg abi)
}
static void
-patchparam(struct function *fn, int *curi, int *param, int tydat, int nabi, struct abiarg abi[2])
+patchparam(struct function *fn, int *curi, int *param, int tydat, int nabi, struct abiarg abi[2], uchar r2off)
{
struct block *blk = fn->entry;
assert(in_range(nabi,1,2));
@@ -125,7 +124,7 @@ patchparam(struct function *fn, int *curi, int *param, int tydat, int nabi, stru
st = mkinstr(Ostore8 + ilog2(cls2siz[abi[0].ty.cls]), 0, alloc, r[0]);
insertinstr(blk, ++*curi, st);
if (nabi > 1) {
- struct instr tmp = mkinstr(Oadd, KPTR, alloc, mkref(RICON, cls2siz[abi[0].ty.cls]));
+ struct instr tmp = mkinstr(Oadd, KPTR, alloc, mkref(RICON, r2off));
st = mkinstr(Ostore8 + ilog2(cls2siz[abi[1].ty.cls]), 0, insertinstr(blk, ++*curi, tmp), r[1]);
insertinstr(blk, ++*curi, st);
}
@@ -137,7 +136,7 @@ patchparam(struct function *fn, int *curi, int *param, int tydat, int nabi, stru
}
static void
-load2regs(union ref out[2], union irtype typ, union ref src, int nabi, struct abiarg abi[2], struct block *blk, int *curi)
+load2regs(union ref out[2], union irtype typ, union ref src, int nabi, struct abiarg abi[2], uchar r2off, struct block *blk, int *curi)
{
uint align = typedata[typ.dat].align;
uint siz = typedata[typ.dat].siz;
@@ -166,7 +165,7 @@ load2regs(union ref out[2], union irtype typ, union ref src, int nabi, struct ab
if (i == 0)
ins.l = src;
else {
- struct instr adr = mkinstr(Oadd, KPTR, src, mkref(RICON, cls2siz[abi[0].ty.cls]));
+ struct instr adr = mkinstr(Oadd, KPTR, src, mkref(RICON, r2off));
ins.l = insertinstr(blk, (*curi)++, adr);
}
temp = insertinstr(blk, (*curi)++, ins);
@@ -185,7 +184,7 @@ load2regs(union ref out[2], union irtype typ, union ref src, int nabi, struct ab
if (i+o == 0)
ld.l = src;
else {
- struct instr adr = mkinstr(Oadd, KPTR, src, mkref(RICON, (i == 0 ? 0 : cls2siz[ld.cls]) + o*align));
+ struct instr adr = mkinstr(Oadd, KPTR, src, mkref(RICON, (i == 0 ? 0 : r2off) + o*align));
ld.l = insertinstr(blk, (*curi)++, adr);
}
temp = insertinstr(blk, (*curi)++, ld);
@@ -204,7 +203,7 @@ load2regs(union ref out[2], union irtype typ, union ref src, int nabi, struct ab
static int
patcharg(struct block *blk, int *icall, struct call *call,
- int argidx, int nabi, struct abiarg abi[2])
+ int argidx, int nabi, struct abiarg abi[2], uchar r2off)
{
int arginst = *icall - (call->narg - argidx);
struct instr *arg = &instrtab[blk->ins.p[arginst]];
@@ -237,7 +236,7 @@ patcharg(struct block *blk, int *icall, struct call *call,
} else { /* aggregate in registers */
union ref r[2];
delinstr(blk, arginst);
- load2regs(r, ref2type(arg->l), arg->r, nabi, abi, blk, &arginst);
+ load2regs(r, ref2type(arg->l), arg->r, nabi, abi, r2off, blk, &arginst);
for (int i = 0; i < nabi; ++i)
insertinstr(blk, arginst++, mkinstr(Oarg, 0, mktyperef(abi[i].ty), r[i]));
*icall = arginst + (call->narg - argidx - 1);
@@ -262,7 +261,7 @@ abi0_call(struct function *fn, struct instr *ins, struct block *blk, int *curi)
vararg = call->vararg;
ni = nf = ns = 0;
assert(!ins->cls == !call->ret.bits);
- nret = abiret(call->abiret, &abiargs, &ni, call->ret);
+ nret = abiret(call->abiret, &abiargs, &call->r2off, &ni, call->ret);
if (call->ret.isagg) { /* adjust struct return */
union irtype retty = call->ret;
struct typedata *td = &typedata[retty.dat];
@@ -292,9 +291,10 @@ abi0_call(struct function *fn, struct instr *ins, struct block *blk, int *curi)
int arginst = *curi - (call->narg - i);
struct instr *arg = &instrtab[blk->ins.p[arginst]];
union irtype pty = ref2type(arg->l);
+ uchar r2off;
int first = abiargs.n;
- int ret = abiarg(&abiargs, &ni, &nf, &ns, pty);
- ret = patcharg(blk, curi, call, i, ret, &abiargs.p[first]);
+ int ret = abiarg(&abiargs, &r2off, &ni, &nf, &ns, pty);
+ ret = patcharg(blk, curi, call, i, ret, &abiargs.p[first], r2off);
if (call->vararg == i) vararg = i2;
i2 += ret;
}
@@ -332,8 +332,7 @@ abi0_call(struct function *fn, struct instr *ins, struct block *blk, int *curi)
if (i == 0) {
store.l = retmem;
} else {
- union ref off = mkref(RICON, cls2siz[call->abiret[0].ty.cls]);
- struct instr addr = mkinstr(Oadd, KPTR, retmem, off);
+ struct instr addr = mkinstr(Oadd, KPTR, retmem, mkref(RICON, call->r2off));
store.l = insertinstr(blk, ++*curi, addr);
}
store.r = r[i];
@@ -367,6 +366,7 @@ abi0(struct function *fn)
struct abiargsvec abiargs = {VINIT(abiargsbuf, arraylength(abiargsbuf))};
int rvovar = -1;
int ni = 0, nf = 0, ns = 0, istart = 0;
+ uchar r2off;
struct block *blk;
union ref sret = {0};
@@ -375,7 +375,7 @@ abi0(struct function *fn)
if (fn->retty.t == TYVOID) {
fn->nabiret = 0;
} else {
- fn->nabiret = abiret(fn->abiret, &abiargs, &ni, mkirtype(fn->retty));
+ fn->nabiret = abiret(fn->abiret, &abiargs, &r2off, &ni, mkirtype(fn->retty));
if (!fn->nabiret && isagg(fn->retty)) { /* ret agg by hidden pointer */
struct instr param = copyparam(fn, NULL, 0, abiargs.p[0]);
sret = insertinstr(fn->entry, 0, param);
@@ -392,8 +392,9 @@ abi0(struct function *fn)
for (int i = 0, param = abiargs.n; i < nparam; ++i) {
union irtype pty = mkirtype(paramty[i]);
int first = abiargs.n;
- int ret = abiarg(&abiargs, &ni, &nf, &ns, pty);
- patchparam(fn, &istart, &param, pty.isagg ? pty.dat : -1, ret+!ret, &abiargs.p[first]);
+ uchar r2off;
+ int ret = abiarg(&abiargs, &r2off, &ni, &nf, &ns, pty);
+ patchparam(fn, &istart, &param, pty.isagg ? pty.dat : -1, ret+!ret, &abiargs.p[first], r2off);
}
fn->abiarg = alloccopy(fn->arena, abiargs.p, abiargs.n * sizeof *abiargs.p, 0);
fn->nabiarg = abiargs.n;
@@ -440,7 +441,7 @@ abi0(struct function *fn)
if (fn->nabiret) { /* aggregate return in register(s) */
union ref r[2];
int curi = blk->ins.n;
- load2regs(r, mkirtype(fn->retty), blk->jmp.arg[0], fn->nabiret, fn->abiret, blk, &curi);
+ load2regs(r, mkirtype(fn->retty), blk->jmp.arg[0], fn->nabiret, fn->abiret, r2off, blk, &curi);
for (int i = 0; i < fn->nabiret; ++i)
blk->jmp.arg[i] = r[i];
} else {
diff --git a/ir/ir.h b/ir/ir.h
index b2077a0..297de82 100644
--- a/ir/ir.h
+++ b/ir/ir.h
@@ -51,6 +51,7 @@ struct call {
ushort argstksiz;
struct abiarg *abiarg;
struct abiarg abiret[2];
+ uchar r2off;
};
enum refkind {
@@ -193,23 +194,25 @@ struct mctarg {
enum objkind objkind;
/* abiret: lower return type:
* scalar/small struct -> returns number of regs (1..2),
- * r & cls filled with reg and irclass of each scalar return
+ * r & cls filled with reg and irclass of each scalar return,
+ * for struct, r2off filled with byte offset within struct for 2nd reg
* big struct -> returns 0, is passed via hidden pointer argument,
* r[0] contains register for returning said pointer or -1,
* r[1] contains register for hidden pointer argument,
* *ni is set to 1 if said register is the first ABI integer argument
*/
- int (*abiret)(short r[2], uchar cls[2], int *ni, union irtype);
+ int (*abiret)(short r[2], uchar cls[2], uchar *r2off, int *ni, union irtype);
/* abiarg: lower argument type:
* scalar/small struct -> returns number of regs (1..2),
* r & cls filled with reg and irclass of each scalar arg
+ * for struct, r2off filled with byte offset within struct for 2nd reg
* if reg == -1 -> stack
* big struct -> returns 0,
* if passed in stack cls[0] == 0, r[0] == negative SP offset
* if passed by pointer cls[0] == KPTR, r[0] contains integer register
* or negative SP offset if stack
*/
- int (*abiarg)(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype);
+ int (*abiarg)(short r[2], uchar cls[2], uchar *r2off, int *ni, int *nf, int *ns, union irtype);
void (*vastart)(struct function *, struct block *, int *curi);
void (*vaarg)(struct function *, struct block *, int *curi);
void (*isel)(struct function *);