aboutsummaryrefslogtreecommitdiffhomepage
path: root/amd64
diff options
context:
space:
mode:
Diffstat (limited to 'amd64')
-rw-r--r--amd64/emit.c85
-rw-r--r--amd64/isel.c40
2 files changed, 91 insertions, 34 deletions
diff --git a/amd64/emit.c b/amd64/emit.c
index 70386be..3ccf595 100644
--- a/amd64/emit.c
+++ b/amd64/emit.c
@@ -6,18 +6,24 @@
*
* Can be a register, a 32-bit immediate,
* a memory reference [base + index * scale + disp],
- * or a RIP-relative reference to some symbol
+ * or a relocatable reference to some symbol plus a displacement and maybe index*scale
*/
-enum operkind { ONONE, OREG, OIMM, OMEM, OCONR };
-enum { NOBASE = 99, NOINDEX = 99 };
+enum operkind { ONONE, OREG, OIMM, OMEM, OSYM };
+enum { NOBASE = 63, NOINDEX = 63 };
static struct oper {
uchar t;
- struct { uchar shift, index, base; }; /* OMEM */
+ union {
+ struct { uchar base; }; /* OMEM */
+ struct { uchar cindex : 6, cshift : 2; }; /* OSYM */
+ };
+ union {
+ struct { uchar index, shift; }; /* OMEM */
+ ushort con; /* OSYM */
+ };
union {
uchar reg; /* OREG */
- int disp; /* OMEM */
+ int disp; /* OMEM, OSYM */
int imm; /* OIMM */
- int con; /* OCONR, conht index*/
};
} ioper[MAXINSTR];
#define mkoper(t, ...) ((struct oper){(t), __VA_ARGS__})
@@ -36,7 +42,7 @@ ref2oper(union ref r)
if (conht[r.i].cls == KI4)
return mkoper(OIMM, .imm = conht[r.i].i);
else if (!conht[r.i].cls)
- return mkoper(OCONR, .con = r.i);
+ return mkoper(OSYM, .con = r.i, .cindex = NOINDEX);
assert(0);
case RADDR: return mkmemoper(r);
default: assert(0);
@@ -119,13 +125,24 @@ mkmemoper(union ref r)
addmemoper(&mem, mkoper(OIMM, .imm = addr->disp));
return mem;
}
+ if (isaddrcon(addr->base)) {
+ return mkoper(OSYM, .con = addr->base.i,
+ .cindex = addr->index.bits ? mkregoper(addr->index).reg : NOINDEX,
+ .cshift = addr->shift,
+ .disp = addr->disp);
+ } else if (isaddrcon(addr->index)) {
+ assert(!addr->shift);
+ return mkoper(OSYM, .con = addr->index.i,
+ .cindex = addr->base.bits ? mkregoper(addr->base).reg : NOINDEX,
+ .disp = addr->disp);
+ }
return mkoper(OMEM, .base = addr->base.bits ? mkregoper(addr->base).reg : NOBASE,
.index = addr->index.bits ? mkregoper(addr->index).reg : NOINDEX,
.disp = addr->disp,
.shift = addr->shift);
} else if (r.t == RXCON) {
assert(!conht[r.i].cls);
- return mkoper(OCONR, .con = r.i);
+ return mkoper(OSYM, .con = r.i, .cindex = NOINDEX);
} else {
return mkoper(OMEM, .base = isregref(r) ? ref2oper(r).reg : NOBASE,
.index = NOINDEX,
@@ -200,8 +217,8 @@ opermatch(enum operpat pat, struct oper oper)
case PI16: return oper.t == OIMM && (short)oper.imm == oper.imm;
case PI32: return oper.t == OIMM;
case PU32: return oper.t == OIMM && oper.imm >= 0;
- case PMEM: return in_range(oper.t, OMEM, OCONR);
- case PSYM: return oper.t == OCONR;
+ case PMEM: return in_range(oper.t, OMEM, OSYM);
+ case PSYM: return oper.t == OSYM;
}
assert(0);
}
@@ -273,19 +290,33 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
mem = dst;
reg = en->ext;
Mem:
- if (mem.t == OCONR) { /* RIP-relative addressing with relocation */
- mod = 0;
- mem.disp = mem.con;
- mem.base = RBP;
- sib = 0;
- if (rex) B(0x40 | rex);
- goto EmitMem;
+ if (mem.t == OMEM) {
+ if (mem.base != NOBASE) rex |= mem.base >> 3; /* REX.B */
+ if (mem.index != NOINDEX) rex |= mem.index >> 3 << 1; /* REX.X */
+ } else {
+ if (mem.cindex != NOINDEX) rex |= mem.cindex >> 3 << 1; /* REX.X */
}
- rex |= mem.base >> 3; /* REX.B */
- if (mem.t != EN_M)
+ if (en->operenc != EN_M)
rex |= (reg >> 3) << 2; /* REX.R */
if (rex) B(0x40 | rex);
else if (en->r8 && in_range(reg, RSP, RDI)) B(0x40);
+
+ if (mem.t == OSYM) {
+ /* XXX PIC */
+ D(opc, nopc);
+ if (mem.cindex == NOINDEX) {
+ /* %rip(var) */
+ B(/*mod 0*/ (reg & 7) << 3 | RBP);
+ objreloc(xcon2sym(mem.con), REL_PCREL32, Stext, *pcode - objout.textbegin, -4 + mem.disp);
+ } else {
+ /* var(,%reg,shift) */
+ B(/*mod 0*/ (reg & 7) << 3 | RSP);
+ B(mem.cshift << 6 | mem.cindex << 3 | RBP); /* SIB [index*s + disp32] */
+ objreloc(xcon2sym(mem.con), REL_ABS32S, Stext, *pcode - objout.textbegin, mem.disp);
+ }
+ I32(0);
+ break;
+ }
if (mem.index == NOINDEX && mem.shift == 0) sib = 0;
else sib = 1;
mod = !mem.disp ? 0 /* disp = 0 -> mod = 00 */
@@ -293,17 +324,12 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
: 2; /* disp32 -> mod = 10 */
if (mod == 0 && (mem.base == RBP || mem.base == R13)) mod = 1;
if (mem.base == RSP || mem.base == R12) sib = 1;
- EmitMem:
D(opc, nopc);
B(mod << 6 | (reg & 7) << 3 | (sib ? 4 : mem.base));
if (sib)
B(mem.shift << 6 | (mem.index & 7) << 3 | (mem.base & 7));
if (mod == 1) B(mem.disp);
else if (mod == 2 || (mod == 0 && mem.base == RBP/*RIP-rel*/)) {
- if (mem.t == OCONR) {
- objreloc(xcon2sym(mem.con), REL_PCREL32, Stext, *pcode - objout.textbegin, -4);
- mem.disp = 0;
- }
I32(mem.disp);
}
if (en->operenc == EN_MI8) B(src.imm);
@@ -335,7 +361,7 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
case EN_R32:
if (rex) B(0x40 | rex);
D(opc, nopc);
- assert(dst.t == OCONR);
+ assert(dst.t == OSYM);
objreloc(xcon2sym(dst.con), REL_PCREL32, Stext, *pcode - objout.textbegin, -4);
I32(0);
break;
@@ -457,6 +483,10 @@ DEFINSTR2(Xshl,
{4|8, PGPR, PI32, "\xC1", EN_RI8, .ext=4}, /* SHL r32/64, imm */
{4|8, PGPR, PRCX, "\xD3", EN_R, .ext=4}, /* SHL r32/64, CL */
)
+DEFINSTR2(Xcvtss2sd,
+ {-1, PFPR, PFPR, "\xF3\x0F\x5A", EN_RR}, /* CVTSS2SD xmm, xmm */
+ {-1, PFPR, PMEM, "\xF3\x0F\x5A", EN_RM}, /* CVTSS2SD xmm, xmm */
+)
DEFINSTR1(Xinc,
{4|8, PGPR, 0, "\xFF", EN_R, .ext=0} /* INC r32/64 */
)
@@ -641,8 +671,8 @@ gencopy(uchar **pcode, enum irclass cls, struct block *blk, int curi, struct ope
} else if (val.bits == ZEROREF.bits && dst.t == OREG && !flagslivep(blk, curi)) {
/* dst = 0 -> xor dst, dst; but only if it is ok to clobber flags */
Xxor(pcode, kisint(cls) ? KI4 : cls, dst, dst);
- } else if (val.t == RXCON && conht[val.i].isdat && !conht[val.i].deref) {
- Xlea(pcode, cls, dst, mkoper(OCONR, .con = val.i));
+ } else if (isaddrcon(val)) {
+ Xlea(pcode, cls, dst, mkoper(OSYM, .con = val.i, .cindex = NOINDEX));
} else {
struct oper src = mkimmdatregoper(val);
if (memcmp(&dst, &src, sizeof dst) != 0)
@@ -702,6 +732,7 @@ emitinstr(uchar **pcode, struct function *fn, struct block *blk, int curi, struc
case Oloadu4: src = mkmemoper(ins->l); Movzxl: Xmov(pcode, KI4, reg2oper(ins->reg-1), src); break;
case Oloadf4: case Oloadf8: Xmov(pcode, cls, reg2oper(ins->reg-1), mkmemoper(ins->l)); break;
case Oloadi8: Xmov(pcode, KI8, reg2oper(ins->reg-1), mkmemoper(ins->l)); break;
+ case Ocvtf4f8: Xcvtss2sd(pcode, KF4, reg2oper(ins->reg-1), mkdatregoper(ins->l)); break;
case Oadd:
dst = mkregoper(ins->l);
if (kisflt(cls)) {
diff --git a/amd64/isel.c b/amd64/isel.c
index d6fbaf5..911bb2e 100644
--- a/amd64/isel.c
+++ b/amd64/isel.c
@@ -139,11 +139,29 @@ ascale(struct addr *addr, union ref a, union ref b)
{
if (b.t != RICON) return 0;
if (addr->index.bits) return 0;
- if (a.t != RTMP && a.t != RREG) return 0;
- if ((unsigned)b.i > 3) return 0;
- addr->shift = b.i;
- addr->index = a;
- return 1;
+ if (a.t == RREG) {
+ Scaled:
+ if ((unsigned)b.i > 3) return 0;
+ addr->index = a;
+ addr->shift = b.i;
+ return 1;
+ } else if (a.t == RTMP) {
+ struct instr *ins = &instrtab[a.i];
+ /* factor out shifted immediate from 'shl {add %x, imm}, s' */
+ /* XXX maybe we shouldn't do this here because it should be done by a generic
+ * arithemetic optimization pass ? */
+ if (ins->op == Oadd && (ins->l.t == RREG || ins->l.t == RTMP) && isintcon(ins->r)) {
+ vlong a = ((vlong) addr->disp + intconval(ins->r)) << b.i;
+ if (a != (int) a) return 0;
+ addr->disp = a;
+ addr->index = ins->l;
+ addr->shift = b.i;
+ return 1;
+ } else {
+ goto Scaled;
+ }
+ }
+ return 0;
}
static bool
@@ -176,6 +194,11 @@ aadd(struct addr *addr, union ref r)
} else goto Ref;
} else if (isnumcon(r)) {
return acon(addr, r);
+ } else if (isaddrcon(r)) {
+ /* XXX PIC */
+ if (!addr->base.bits && !isaddrcon(addr->index)) addr->base = r;
+ else if (!addr->index.bits && !isaddrcon(addr->base)) addr->index = 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 */
@@ -194,7 +217,7 @@ fuseaddr(union ref *r)
struct addr addr = { 0 };
if (r->t == RADDR) return 1;
- if (r->t == RXCON && (!conht[r->i].cls && !conht[r->i].deref)) return 1;
+ if (isaddrcon(*r)) return 1;
if (r->t != RTMP) return 0;
if (!aadd(&addr, *r)) return 0;
@@ -206,8 +229,10 @@ fuseaddr(union ref *r)
static bool
addarg4addrp(union ref r)
{
- struct instr *ins = &instrtab[r.i];
+ struct instr *ins;
+ if (r.t == RXCON && !conht[r.i].cls && !conht[r.i].deref) return 1; /* sym or dat ref */
if (r.t != RTMP) return 0;
+ ins = &instrtab[r.i];
return ins->op == Oshl || (ins->op == Ocopy && ins->l.t == RADDR) || ins->op == Oadd;
}
@@ -332,6 +357,7 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi)
if (iscon(ins->l))
rswap(ins->l, ins->r);
case Oneg: case Onot:
+ case Ocvtf4f8: case Ocvtf8f4: case Ocvtf4s: case Ocvtf8s:
case Oexts1: case Oextu1: case Oexts2: case Oextu2: case Oexts4: case Oextu4:
ALU:
if (!(op == Oadd && kisint(ins->cls))) /* 3-address add is lea */