aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--test/17-misc.c19
-rw-r--r--x86_64/isel.c136
2 files changed, 76 insertions, 79 deletions
diff --git a/test/17-misc.c b/test/17-misc.c
new file mode 100644
index 0000000..ecff06c
--- /dev/null
+++ b/test/17-misc.c
@@ -0,0 +1,19 @@
+/* EXPECT:
+-1155497588
+*/
+
+typedef unsigned long long uvlong;
+int fn1(uvlong p_9) {
+/* extract from ldrgen seed=637312671 */
+ uvlong v_73, q;
+ if (p_9) {
+ v_73 = 909910719;
+ }
+ q = (uvlong)p_9 / ((v_73 - 572547313ull) + 445ull);
+ return q;
+}
+
+extern int printf(const char *, ...);
+int main() {
+ printf("%d\n", fn1(-77ull));
+}
diff --git a/x86_64/isel.c b/x86_64/isel.c
index 19e8d0c..65c3e4d 100644
--- a/x86_64/isel.c
+++ b/x86_64/isel.c
@@ -40,11 +40,12 @@ static const uchar opflags[NOPER] = {
static int iflagsrc = -1;
+#define inscopy(blk, pcuri, k, r) insertinstr((blk), (*(pcuri))++, mkinstr(Ocopy, k, .l = (r)))
static void
picfixsym(union ref *r, struct block *blk, int *curi)
{
if (!ccopt.pic || !isaddrcon(*r,0)) return;
- *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, .l = *r));
+ *r = inscopy(blk, curi, KPTR, *r);
}
static void
@@ -73,7 +74,7 @@ Begin:
if (in_range(op, Ocopy, Omove) || op == Ophi)
*r = ZEROREF;
else
- *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, con->cls, ZEROREF));
+ *r = inscopy(blk, curi, con->cls, ZEROREF);
} else if (con->cls >= KI64) {
/* float immediates & 64bit immediates are loaded from memory */
uchar data[8];
@@ -81,7 +82,8 @@ Begin:
union type ctype;
/* can't use memory arg in rhs if lhs is memory */
bool docopy = ins && &ins->l != r && (oisstore(ins->op) || ins->l.t == RADDR);
- if (con->cls <= KPTR && in_range(op, Ocopy, Omove)) /* in this case we can use movabs */
+ if (con->cls <= KPTR && (in_range(op, Ocopy, Omove) || op == Ophi))
+ /* in this case we can use movabs */
return;
else if (!docopy || con->cls >= KF32) {
if (con->cls != KF32) {
@@ -95,14 +97,14 @@ Begin:
*r = mkdatref(NULL, ctype, ksiz, /*align*/ksiz, data, ksiz, /*deref*/1);
}
if (docopy)
- *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, con->cls, *r));
- } else if (op != Omove && (con->issym || (con->isdat && !con->deref)) && ins && r == &ins->r) {
- *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, *r));
+ *r = inscopy(blk, curi, con->cls, *r);
+ } else if (op != Omove && ins && isaddrcon(*r,0) && r == &ins->r) {
+ *r = inscopy(blk, curi, KPTR, *r);
} else if (in_range(op, Odiv, Ourem) && kisint(ins->cls))
goto DivImm;
} else if (r->t == RICON && in_range(op, Odiv, Ourem) && kisint(ins->cls) && r == &ins->r) {
DivImm: /* there is no division by immediate, must be copied to a register */
- *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, *r));
+ *r = inscopy(blk, curi, ins->cls, *r);
} else if (r->t == RICON && in_range(op, Oshl, Oslr) && r == &ins->r) {
sh = r->i;
ShiftImm: /* shift immediate is always 8bit */
@@ -113,8 +115,8 @@ Begin:
*ins = adr;
else
*r = insertinstr(blk, (*curi)++, adr);
- } else if (r->bits == UNDREF.bits && ins->op != Ocopy) {
- *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, *r));
+ } else if (r->bits == UNDREF.bits && ins && !in_range(op, Ocopy, Omove) && op != Ophi) {
+ *r = inscopy(blk, curi, ins->cls, *r);
}
picfixsym(r, blk, curi);
}
@@ -167,7 +169,7 @@ selcall(struct function *fn, struct instr *ins, struct block *blk, int *curi)
if (isimm32(ins->l))
ins->l = mkaddr((struct addr){.base = ins->l});
else if (isintcon(ins->l))
- ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, ins->l));
+ ins->l = inscopy(blk, curi, KPTR, ins->l);
if (call->vararg >= 0) {
/* variadic calls write number of sse regs used to AL */
@@ -233,51 +235,56 @@ ascale(struct addr *addr, union ref a, union ref b)
}
static bool
-aadd(struct addr *addr, struct block *blk, int *curi, union ref r)
+aadd(struct addr *out, struct block *blk, int *curi, union ref r, bool recurring)
{
if (r.t == RSTACK) {
- if (addr->base.bits || !aimm(addr, -r.i)) goto Ref;
- addr->base = mkref(RREG, RBP);
+ if (out->base.bits || !aimm(out, -r.i)) {
+ r = insertinstr(blk, (*curi)++, mkinstr(Oadd, KPTR, mkref(RREG, RBP), mkref(RICON, -r.i)));
+ goto Ref;
+ }
+ out->base = mkref(RREG, RBP);
} else if (r.t == RTMP) {
struct instr *ins = &instrtab[r.i];
+ struct addr adr = {0};
if (ins->op == Oadd) {
- if (!aadd(addr, blk, curi, ins->l)) goto Ref;
- if (!aadd(addr, blk, curi, ins->r)) goto Ref;
- ins->skip = 1;
- } else if (ins->op == Oshl) {
- if (!ascale(addr, ins->l, ins->r)) goto Ref;
- ins->skip = 1;
- } else if (ins->op == Ocopy && ins->l.t == RADDR) {
- struct addr save = *addr, *addr2 = &addrtab.p[ins->l.i];
- if ((!addr2->base.bits || aadd(addr, blk, curi, addr2->base))
- && aimm(addr, addr2->disp)
- && (!addr2->index.bits || ascale(addr, addr2->index, mkref(RICON, addr2->shift))))
- {
+ if (recurring) goto Ref;
+ if (aadd(&adr, blk, curi, ins->l, 1) && aadd(&adr, blk, curi, ins->r, 1)) {
+ Add2:;
+ int n1 = !!out->base.bits + !!out->index.bits;
+ int n2 = !!adr.base.bits + !!adr.index.bits;
+ vlong off = (vlong) out->disp + adr.disp;
+ if (n1+n2 > 2 || (int)off != off) goto Ref;
+ if (n1 == 0) {
+ *out = adr;
+ } else if (n1 == 1 && n2 == 1) {
+ assert(!out->index.bits);
+ out->index = adr.index.bits ? adr.index : adr.base;
+ out->shift = adr.shift;
+ } else assert(n1 <= 2 && n2 == 0);
+ out->disp = off;
ins->skip = 1;
- } else {
- *addr = save;
- goto Ref;
- }
- } else if (ins->op == Ocopy) {
- if (!aadd(addr, blk, curi, ins->l)) goto Ref;
+ } else return 0;
+ } else if (ins->op == Ocopy && ins->l.t == RADDR) {
+ const struct addr *adr2 = &addrtab.p[ins->l.i];
+ adr = *adr2;
+ goto Add2;
+ } else if (ins->op == Oshl) {
+ if (!ascale(out, ins->l, ins->r)) goto Ref;
ins->skip = 1;
} else goto Ref;
} else if (isnumcon(r)) {
assert(isintcon(r));
- return aimm(addr, intconval(r));
+ return aimm(out, intconval(r));
} else if (isaddrcon(r,1)) {
- if (!addr->base.bits && !isaddrcon(addr->index,1)) addr->base = r;
+ if (!out->base.bits && !isaddrcon(out->index,1)) out->base = r;
else return 0;
} else if (r.t == RREG) {
/* temporaries are single assignment, but register aren't, so they can't be *
* safely hoisted into an address value, unless they have global lifetime */
if (!rstest(mctarg->rglob, r.i)) return 0;
Ref:
- if (r.t == RSTACK && (addr->base.bits || addr->index.bits)) {
- r = insertinstr(blk, (*curi)++, mkinstr(Oadd, KPTR, mkref(RREG, RBP), mkref(RICON, -r.i)));
- }
- if (!addr->base.bits) addr->base = r;
- else if (!addr->index.bits) addr->index = r;
+ if (!out->base.bits) out->base = r;
+ else if (!out->index.bits) out->index = r;
else return 0;
} else return 0;
return 1;
@@ -289,24 +296,14 @@ fuseaddr(union ref *r, struct block *blk, int *curi)
struct addr addr = { 0 };
if (isaddrcon(*r,1)) return 1;
- if (r->t == RADDR) {
- const struct addr *a0 = &addrtab.p[r->i];
- if (aadd(&addr, blk, curi, a0->base)
- && (!addr.index.bits || ascale(&addr, a0->index, mkref(RICON, a0->shift)))
- && aadd(&addr, blk, curi, mkintcon(KPTR, a0->disp))) {
- *r = mkaddr(addr);
- }
- return 1;
- }
- if (r->t != RSTACK && r->t != RTMP) return 0;
- if (!aadd(&addr, blk, curi, *r)) return 0;
+ if (!aadd(&addr, blk, curi, *r, 0)) return 0;
if (isaddrcon(addr.base,0) && (ccopt.pic || (ccopt.pie && addr.index.bits) || (contab.p[addr.base.i].flag & SFUNC))) {
/* pic needs to load from GOT */
/* pie cannot encode RIP-relative address with index register */
/* first load symbol address into a temp register */
union ref temp = mkaddr((struct addr){.base = addr.base, .disp = ccopt.pic ? 0 : addr.disp});
- addr.base = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, .l = temp));
+ addr.base = inscopy(blk, curi, KPTR, temp);
if (!ccopt.pic) addr.disp = 0;
}
@@ -326,11 +323,11 @@ static bool
addarg4addrp(union ref r)
{
struct instr *ins;
- if (r.t == RXCON && !contab.p[r.i].cls && !contab.p[r.i].deref) return 1; /* sym or dat ref */
+ if (isaddrcon(r, 0)) return 1;
if (r.t == RSTACK) return 1;
if (r.t != RTMP) return 0;
ins = &instrtab[r.i];
- return ins->op == Oshl || (ins->op == Ocopy && ins->l.t == RADDR) || ins->op == Oadd;
+ return (ins->op == Ocopy && ins->l.t == RADDR) || ins->op == Oadd || ins->op == Oshl;
}
static void
@@ -340,8 +337,8 @@ loadstoreaddr(struct block *blk, union ref *r, int *curi)
*r = mkaddr((struct addr){.base = *r});
} else if (isaddrcon(*r, 0)) {
picfixsym(r, blk, curi);
- } else if (r->t == RTMP || r->t == RSTACK) {
- if (addarg4addrp(*r)) fuseaddr(r, blk, curi);
+ } else if (r->t == RSTACK || (r->t == RTMP && addarg4addrp(*r))) {
+ fuseaddr(r, blk, curi);
} else if (r->t != RREG) {
*r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, *r));
}
@@ -410,7 +407,7 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi)
rswap(ins->l, ins->r);
}
if (ins->l.t != RTMP && ins->l.t != RREG && ins->l.t != RSTACK)
- ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l));
+ ins->l = inscopy(blk, curi, ins->cls, ins->l);
else
fixarg(&ins->l, ins, blk, curi);
fixarg(&ins->r, ins, blk, curi);
@@ -456,30 +453,11 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi)
}
/* fallthru */
case Oadd:
- if (kisint(ins->cls)) {
- if ((addarg4addrp(ins->l) || addarg4addrp(ins->r))) {
- temp.op = Ocopy;
- temp.cls = ins->cls;
- temp.l = mkref(RTMP, t);
- if (fuseaddr(&temp.l, blk, curi)) {
- *ins = temp;
- break;
- } else if (ins->l.t == RSTACK && (ins->r.t == RTMP || isintcon(ins->r))) {
- /* handles an edge case with 'add [stack], %{add x,y}' (not ideal) */
- struct addr addr = {0};
- bool ok = aadd(&addr, blk, curi, ins->l);
- assert(ok);
- if (ins->r.t == RTMP) {
- addr.index = ins->r;
- temp.l = mkaddr(addr);
- *ins = temp;
- break;
- } else if (aadd(&addr, blk, curi, ins->r)) { /* aadd handles disp being too large */
- temp.l = mkaddr(addr);
- *ins = temp;
- break;
- }
- }
+ if (kisint(ins->cls) && (addarg4addrp(ins->l) || addarg4addrp(ins->r))) {
+ union ref it = mkref(RTMP, ins - instrtab);
+ if (fuseaddr(&it, blk, curi)) {
+ *ins = mkinstr(Ocopy, ins->cls, it);
+ break;
}
}
/* fallthru */