aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2025-12-18 08:50:47 +0100
committerlemon <lsof@mailbox.org>2025-12-18 08:50:47 +0100
commitd43993dfaa1cf9b684583a0c9266e18ea61abb4d (patch)
tree4472cf1bfe63fbce17815fea4da1cb9c818288a9
parentbfc03147a0cdac72250819443b529b379572471c (diff)
x86_64: for vararg calls, write to EAX in isel
Also, in regalloc ensure fixed intervals are sorted
-rw-r--r--ir/regalloc.c33
-rw-r--r--x86_64/emit.c10
-rw-r--r--x86_64/isel.c9
3 files changed, 30 insertions, 22 deletions
diff --git a/ir/regalloc.c b/ir/regalloc.c
index 794cdd6..7056dfd 100644
--- a/ir/regalloc.c
+++ b/ir/regalloc.c
@@ -560,10 +560,19 @@ usereg(struct rega *ra, int reg, struct block *blk, int pos)
static bool
defreg(struct rega *ra, int reg, int pos) {
if (rstest(mctarg->rglob, reg)) return 1;
- for (struct fixinterval *fxit = ra->intervals.fixed; fxit; fxit = fxit->next) {
+ for (struct fixinterval *prev = NULL, *fxit = ra->intervals.fixed; fxit; prev = fxit, fxit = fxit->next) {
if (fxit->rs == 1<<reg) {
if (fxit->range.from <= pos) {
fxit->range.from = pos;
+ struct fixinterval **at = &ra->intervals.fixed;
+ if ((*at)->range.from > pos) {
+ /* keep sorted */
+ //DBG("moved %s\n", mctarg->rnames[reg]);
+ if (prev) prev->next = fxit->next;
+ while ((*at)->range.from < pos) at = &(*at)->next;
+ fxit->next = *at;
+ *at = fxit;
+ }
//DBG(">>>def REG %s range %d-%d\n", mctarg->rnames[reg], fxit->range.from, fxit->range.to);
return 1;
}
@@ -649,14 +658,21 @@ buildintervals(struct rega *ra)
/* gather fixed intervals */
if (ins->op == Omove) {
assert(ins->l.t == RREG);
- if (ins->l.bits == ins->r.bits) /* special case `move Rx,Rx`: clobber reg, not a real use */
+ if (ins->l.bits == ins->r.bits) {/* special case `move Rx,Rx`: clobber reg, not a real use */
usereg(ra, ins->l.i, blk, pos);
+ //DBG("@ %d clob %s\n", pos, mctarg->rnames[ins->l.i]);
+ }
if (!defreg(ra, ins->l.i, pos)) {
- /* dead register use. for example if
- * move RCX, %1
- * %2 = shl 1, RCX
- * and %2 is dead, the move to RCX can be killed */
- *ins = mkinstr(Onop,0,);
+ if (ins->keep) { /* clobber here */
+ usereg(ra, ins->l.i, blk, pos);
+ assert(defreg(ra, ins->l.i, pos));
+ } else {
+ /* dead register use. for example if
+ * move RCX, %1
+ * %2 = shl 1, RCX
+ * and %2 is dead, the move to RCX can be killed */
+ *ins = mkinstr(Onop,0,);
+ }
}
if (ins->l.bits == ins->r.bits)
continue;
@@ -917,7 +933,8 @@ linearscan(struct rega *ra)
int end = itrange(current, current->nrange-1).to;
/* exclude regs from overlapping fixed intervals */
- for (struct fixinterval *fxit = intervals->fixed; fxit; fxit = fxit->next) {
+ for (struct fixinterval *last = NULL, *fxit = intervals->fixed; fxit; last = fxit, fxit = fxit->next) {
+ if (last) assert(last->range.from <= fxit->range.from && "unsorted fixintervals");
if (fxit->range.to <= pos) {
intervals->fixed = fxit->next;
continue;
diff --git a/x86_64/emit.c b/x86_64/emit.c
index 13121b3..89585b8 100644
--- a/x86_64/emit.c
+++ b/x86_64/emit.c
@@ -1171,16 +1171,6 @@ emitinstr(uchar **pcode, struct function *fn, struct block *blk, int curi, struc
Xpop(pcode, mkregoper(ins->l).reg);
break;
case Ocall:
- if (calltab.p[ins->r.i].vararg >= 0) {
- struct call *call = &calltab.p[ins->r.i];
- /* variadic functions need the caller to write num of args in sse regs to %al */
- int n = 0;
- for (int i = 0; i < call->narg; ++i)
- if (!call->abiarg[i].isstk && call->abiarg[i].reg >= XMM0)
- ++n;
- if (!n) DS("\x31\xC0"); /* XOR EAX, EAX */
- else B(0xB0), B(n); /* MOV AL, n */
- }
Xcall(pcode, KPTR, ref2oper(ins->l));
break;
case Oxvaprologue:
diff --git a/x86_64/isel.c b/x86_64/isel.c
index 67a4358..22a1290 100644
--- a/x86_64/isel.c
+++ b/x86_64/isel.c
@@ -126,6 +126,7 @@ selcall(struct function *fn, struct instr *ins, struct block *blk, int *curi)
int iarg = *curi - 1;
enum irclass cls;
uint argstksiz = alignup(call->argstksiz, 16);
+ int nsse = 0;
for (int i = call->narg - 1; i >= 0; --i) {
struct abiarg abi = call->abiarg[i];
@@ -139,6 +140,7 @@ selcall(struct function *fn, struct instr *ins, struct block *blk, int *curi)
if (!abi.isstk) {
assert(!abi.ty.isagg);
*arg = mkinstr(Omove, call->abiarg[i].ty.cls, mkref(RREG, abi.reg), arg->r);
+ if (abi.reg >= XMM0) ++nsse;
} else {
union ref adr = mkaddr((struct addr){mkref(RREG, RSP), .disp = abi.stk});
int iargsave = iarg;
@@ -165,10 +167,9 @@ selcall(struct function *fn, struct instr *ins, struct block *blk, int *curi)
else if (isintcon(ins->l))
ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, ins->l));
- if (call->vararg >= 0 && ins->l.t == RTMP) {
- /* variadic calls write number of sse regs used to AL, so mark it as clobbered such that
- * the function pointer of an indirect calls does not get allocated to RAX by regalloc */
- insertinstr(blk, (*curi)++, mkinstr(Omove, KPTR, mkref(RREG, RAX), mkref(RREG, RAX)));
+ if (call->vararg >= 0) {
+ /* variadic calls write number of sse regs used to AL */
+ insertinstr(blk, (*curi)++, mkinstr(Omove, KI32, mkref(RREG, RAX), mkref(RICON, nsse), .keep=1));
}
cls = ins->cls;
ins->cls = 0;