aboutsummaryrefslogtreecommitdiffhomepage
path: root/aarch64/emit.c
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2026-03-17 13:22:00 +0100
committerlemon <lsof@mailbox.org>2026-03-17 13:22:00 +0100
commita8d6f8bf30c07edb775e56889f568ca20240bedf (patch)
treeb5a452b2675b2400f15013617291fe6061180bbf /aarch64/emit.c
parent24f14b7ad1af08d872971d72ce089a529911f657 (diff)
REFACTOR: move sources to src/
Diffstat (limited to 'aarch64/emit.c')
-rw-r--r--aarch64/emit.c1023
1 files changed, 0 insertions, 1023 deletions
diff --git a/aarch64/emit.c b/aarch64/emit.c
deleted file mode 100644
index 9fdcd83..0000000
--- a/aarch64/emit.c
+++ /dev/null
@@ -1,1023 +0,0 @@
-#include "all.h"
-#include "../obj/obj.h"
-#include "../endian.h"
-
-/* References:
- * ARM ARM https://developer.arm.com/documentation/ddi0628/aa/?lang=en
- * AAELF ABI https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
- */
-
-enum operkind { ONONE, OREGZR, OREG, OIMM, OMEM, OSYM };
-enum shiftkind { SLSL, SLSR, SASR, SROR };
-enum addrmode { AIMMIDX, AREGIDX, APREIDX, APOSTIDX };
-enum addrregext { XUXTW = 2, XLSL = 3, XSXTW = 6, XSXTX = 7 };
-struct oper {
- uchar t;
- union {
- struct { /* OREG (opt. shifted) */
- uchar reg;
- uchar shft : 2, /* enum shiftkind */
- shamt : 6;
- };
- struct { /* OMEM */
- uchar mode : 3; /* enum addrmode */
- uchar base : 5; /* reg */
- union {
- struct {
- uchar index : 5; /* reg */
- uchar ext : 3; /* enum addrregext */
- uchar shamt;
- };
- short disp;
- };
- } m;
- vlong imm; uvlong uimm; /* OIMM */
- struct { /* OSYM */
- ushort con;
- int cdisp;
- };
- };
-};
-
-#define REGZR ((struct oper){OREGZR, .reg=31})
-#define mkoper(t, ...) ((struct oper){(t), __VA_ARGS__})
-#define reg2oper(r) (assert((uint)(r) <= V(31)), mkoper(OREG, .reg = (r)))
-
-static struct oper
-mkmemoper(uint msiz, union ref r)
-{
- if (r.t == RTMP) {
- assert(in_range(instrtab[r.i].reg-1, R0, SP));
- return mkoper(OMEM, .m = {AIMMIDX, .base = instrtab[r.i].reg-1});
- } else if (r.t == RREG) {
- return mkoper(OMEM, .m = {AIMMIDX, .base = r.i});
- } else if (isaddrcon(r,1)) {
- return mkoper(OSYM, .con = r.i,);
- } else if (r.t == RADDR) {
- const struct addr *addr = &addrtab.p[r.i];
- assert(addr->shift <= 3 && (!addr->disp || !addr->index.bits));
- if (isaddrcon(addr->base,0)) {
- assert(!addr->index.bits);
- return mkoper(OSYM, .con = addr->base.i, .cdisp = addr->disp);
- }
- assert(addr->base.t == RREG);
- if (!addr->index.bits) {
- return mkoper(OMEM, .m = {.mode = AIMMIDX, .base = addr->base.i, .disp = addr->disp});
- } else {
- assert(addr->index.t == RREG);
- assert(addr->shift == 0 || 1<<addr->shift == msiz);
- return mkoper(OMEM, .m = {
- .mode = AREGIDX,
- .base = addr->base.i,
- .index = addr->index.i,
- .ext = XLSL,
- .shamt = !!addr->shift,
- });
- }
- }
- assert(!"nyi");
-}
-
-static struct oper
-ref2oper(union ref r)
-{
- switch (r.t) {
- case RTMP: return instrtab[r.i].reg ? mkoper(ONONE,) : reg2oper(instrtab[r.i].reg-1);
- case RREG: return reg2oper(r.i);
- case RICON: return mkoper(OIMM, .imm = r.i);
- case RXCON:
- if (kisint(contab.p[r.i].cls))
- return mkoper(OIMM, .imm = contab.p[r.i].i);
- else if (kisflt(contab.p[r.i].cls)) {
- assert(contab.p[r.i].f == 0.0);
- return mkoper(OIMM, .imm = 0);
- } else if (!contab.p[r.i].cls) {
- return mkoper(OSYM, .con = r.i);
- }
- assert(0);
- case RADDR: return mkmemoper(0, r);
- default: assert(0);
- }
-}
-
-enum operpat {
- PNONE,
- PGPRZ, /* R0-R30,ZR */
- PGPRSP, /* R0-R30,SP */
- PSP, /* SP */
- PGPRZSHFT, /* R0-30,ZR SFHT #n */
- PFPR, /* V0 - V31 */
- PZERO, /* zero immediate */
- PU6, /* 6-bit uimm */
- PU12SL12, /* 12 bit uimm, optionally left shifted by 12 */
- PU16SL16, /* 16 bit uimm, left shift by 0/16/32/48 */
- PLOGIMM, /* immediate for logical instrs */
- PMEMAIMM, /* addr 12bit immediate byte offset */
- PMEMAIMMH, /* addr 12bit immediate halfword offset (multiple of 2) */
- PMEMAIMMW, /* addr 12bit immediate word offset (multiple of 4) */
- PMEMAIMMX, /* addr 12bit immediate doubleword offset (multiple of 8) */
- PMEMPREPOST, /* addr signed 9bit immediate byte offset */
- PMEMAREG, /* addr reg offset, optionally left shifted */
- PSYM, /* symbol */
-};
-enum operenc {
- EN_ADDSUBEXT3R, /* add/sub-ext-reg */
- EN_ADDSUBSHFT3R, /* add/sub-shift-reg */
- EN_LOGSHFT3R, /* logical/shifted-reg */
- EN_ARITH2R, /* data-processing/1src */
- EN_ARITH3R, /* data-processing/2src */
- EN_ADDSUBIMM, /* add/subtract-imm */
- EN_LOGIMM, /* logical-imm */
- EN_MOVEIMM, /* move/wide-imm */
- EN_MEMAIMM, /* load/store/unsigned-imm */
- EN_MEMAIMMH, /* load/store/unsigned-imm (halfword) */
- EN_MEMAIMMW, /* load/store/unsigned-imm (word) */
- EN_MEMAIMMX, /* load/store/unsigned-imm (doubleword) */
- EN_MEMAPREPOST, /* load/store/pre/postidx-imm */
- EN_MEMAREG, /* load/store/reg-offset */
- EN_MEMPPREPOST, /* load/store-pair/pre/postidx-imm */
- EN_ADRSYMLO21, /* for ADR <sym> */
- EN_ADRSYMPGHI21, /* for ADRP <sym:pghi21> */
- EN_ADDSYMLO12, /* for ADD x,x, <sym:lo12> */
- EN_LDSYMLO19, /* for LDR (literal) */
- EN_FP2R, /* float 1src */
- EN_FP1GPR1, /* fpr + gpr */
- EN_FP3R, /* float 2src */
- EN_FPIMM, /* float-imm */
- EN_FPCMPZ, /* float cmp with zero */
- EN_FPCMP, /* float cmp-imm */
-};
-struct desc {
- uchar psiz; /* subset of {4,8} */
- uchar pt[3]; /* bitsets of enum operpat, up to 3 operands */
- uint opc;
- uchar operenc; /* enum operenc */
-};
-
-/* match operand against pattern */
-static inline bool
-opermatch(enum operpat pat, enum irclass k, struct oper o)
-{
- switch (pat) {
- case PNONE: return !o.t;
- case PGPRZ:
- return o.t == OREGZR || (o.t == OREG && in_range(o.reg, R0, R(30)) && !o.shamt);
- case PGPRSP:
- return o.t == OREG && in_range(o.reg, R0, R(31)) && !o.shamt;
- case PGPRZSHFT:
- return o.t == OREGZR || (o.t == OREG && in_range(o.reg, R0, R(30)));
- case PSP: return o.t == OREG && o.reg == SP;
- case PFPR: return o.t == OREG && in_range(o.reg, V0, V(31));
- case PZERO: return o.t == OIMM && o.imm == 0;
- case PU6: return o.t == OIMM && (uint)o.imm < 63;
- case PSYM: return o.t == OSYM;
- case PU12SL12:
- return o.t == OIMM && ((o.imm &~ 0xFFF) == 0 || (o.imm &~ 0xFFF000) == 0);
- case PU16SL16:
- return o.t == OIMM
- && ((o.imm &~ 0xFFFF) == 0 || (o.imm &~ 0xFFFF0000) == 0
- || (o.imm &~ (0xFFFFull<<32)) == 0 || (o.imm &~ (0xFFFFull<<48)) == 0);
- case PLOGIMM: return o.t == OIMM && aarch64_logimm(NULL, k, o.imm);
- case PMEMAIMM:
- return o.t == OMEM && o.m.mode == AIMMIDX && (uint)o.m.disp < (1<<12);
- case PMEMAIMMH:
- return o.t == OMEM && o.m.mode == AIMMIDX && (uint)o.m.disp < (1<<13) && !(o.m.disp % 2);
- case PMEMAIMMW:
- return o.t == OMEM && o.m.mode == AIMMIDX && (uint)o.m.disp < (1<<14) && !(o.m.disp % 4);
- case PMEMAIMMX:
- return o.t == OMEM && o.m.mode == AIMMIDX && (uint)o.m.disp < (1<<15) && !(o.m.disp % 8);
- case PMEMAREG:
- return o.t == OMEM && o.m.mode == AREGIDX;
- case PMEMPREPOST:
- return o.t == OMEM && (o.m.mode == APREIDX || o.m.mode == APOSTIDX
- || (o.m.mode == AIMMIDX && o.m.disp >= -256 && o.m.disp < 256));
- }
- assert(0);
-}
-
-/* code output helpers */
-#define W32(w) (wr32targ(*pcode, (w)), *pcode += 4)
-
-static uchar *fnstart;
-static internstr curfnsym;
-static bool usefp;
-static int rbpoff;
-
-/* Given an instruction description table, find the first entry that matches
- * the operands and encode it. */
-static void
-encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct oper o[3])
-{
- const struct desc *en = NULL;
- for (int i = 0; i < ntab; ++i) {
- if (!(tab[i].psiz & cls2siz[k])) continue;
- for (int j = 0; j < 3; ++j)
- if (!opermatch(tab[i].pt[j], k, o[j]))
- goto Skip;
- en = &tab[i];
- break;
- Skip:;
- }
- assert(en && "no match for instr");
-
- uint sf = cls2siz[k] >> 3;
- uint ins = en->opc, sh, nimmrs;
- switch (en->operenc) {
- default: assert(!"nyi enc");
- case EN_ADDSUBSHFT3R: case EN_LOGSHFT3R:
- ins |= sf<<31 | o[2].shft<<22 | o[2].reg<<16 | o[2].shamt<<10 | o[1].reg<<5 | o[0].reg;
- break;
- case EN_ARITH3R:
- ins |= sf<<31 | o[2].reg<<16 | o[1].reg<<5 | o[0].reg;
- break;
- case EN_ADDSUBIMM:
- sh = o[2].imm > 0xFFF;
- ins |= sf<<31 | sh<<22 | (o[2].uimm >> 12*sh)<<10 | o[1].reg<<5 | o[0].reg;
- break;
- case EN_LOGIMM:
- assert(aarch64_logimm(&nimmrs, k, o[2].uimm));
- ins |= sf<<31 | nimmrs<<10 | o[1].reg<<5 | o[0].reg;
- break;
- case EN_MOVEIMM:
- sh = o[1].imm ? lowestsetbit(o[1].imm) / 16 : 0;
- ins |= sf<<31 | sh<<21 | (o[1].uimm >> 16*sh)<<5 | o[0].reg;
- break;
- case EN_MEMAIMM: AImm:
- ins |= o[1].m.disp<<10 | o[1].m.base<<5 | (o[0].reg&31);
- break;
- case EN_MEMAIMMH: o[1].m.disp >>= 1; goto AImm;
- case EN_MEMAIMMW: o[1].m.disp >>= 2; goto AImm;
- case EN_MEMAIMMX: o[1].m.disp >>= 3; goto AImm;
- case EN_MEMAPREPOST:
- ins |= (o[1].m.disp&0x1FF)<<12 | o[1].m.base<<5 | (o[0].reg&31);
- if (o[1].m.mode == APREIDX) ins |= 3<<10;
- else if (o[1].m.mode == APOSTIDX) ins |= 1<<10;
- break;
- case EN_MEMAREG:
- assert(o[1].m.shamt <= 1);
- ins |= o[1].m.index<<16 | o[1].m.ext<<13 | o[1].m.shamt<<12 | o[1].m.base<<5 | (o[0].reg&31);
- break;
- case EN_MEMPPREPOST:
- assert(o[2].m.disp % 8 == 0);
- ins |= (o[2].m.disp/8&0x7F)<<15 | (o[1].reg&31)<<10 | o[2].m.base<<5 | (o[0].reg&31);
- if (o[2].m.mode == APREIDX) ins |= 3<<23;
- else if (o[2].m.mode == APOSTIDX) ins |= 1<<23;
- else ins |= 2<<23;
- break;
- case EN_ADRSYMLO21:
- ins |= o[0].reg;
- objreloc(xcon2sym(o[1].con), REL_ADR_PREL_LO21, Stext, *pcode - objout.textbegin, o[1].cdisp);
- break;
- case EN_ADRSYMPGHI21:
- ins |= o[0].reg;
- objreloc(xcon2sym(o[1].con), REL_ADR_PREL_PG_HI21, Stext, *pcode - objout.textbegin, o[1].cdisp);
- break;
- case EN_ADDSYMLO12:
- ins |= sf<<31 | o[1].reg<<5 | o[0].reg;
- objreloc(xcon2sym(o[2].con), REL_ADD_ABS_LO12_NC, Stext, *pcode - objout.textbegin, o[1].cdisp);
- break;
- case EN_LDSYMLO19:
- ins |= o[0].reg;
- objreloc(xcon2sym(o[1].con), REL_LD_PREL_LO19, Stext, *pcode - objout.textbegin, o[1].cdisp);
- break;
- case EN_FP2R:
- ins |= sf<<22 | (o[1].reg&31)<<5 | (o[0].reg&31);
- break;
- case EN_FP1GPR1:
- ins |= (o[1].reg&31)<<5 | (o[0].reg&31);
- break;
- case EN_FP3R:
- ins |= sf<<22 | (o[2].reg&31)<<16 | (o[1].reg&31)<<5 | (o[0].reg&31);
- break;
- case EN_FPCMPZ:
- ins |= sf<<22 | (o[0].reg&31)<<5;
- break;
- case EN_FPCMP:
- ins |= sf<<22 | (o[1].reg&31)<<16 | (o[0].reg&31)<<5;
- break;
- }
- W32(ins);
-}
-#define DEFINSTR1(X, ...) \
- static void \
- X(uchar **pcode, enum irclass k, struct oper a) \
- { \
- static const struct desc tab[] = { __VA_ARGS__ }; \
- encode(pcode, tab, countof(tab), k, ((struct oper [3]){a})); \
- }
-
-#define DEFINSTR2(X, ...) \
- static void \
- X(uchar **pcode, enum irclass k, struct oper op1, struct oper op2) \
- { \
- static const struct desc tab[] = { __VA_ARGS__ }; \
- encode(pcode, tab, countof(tab), k, ((struct oper [3]){op1,op2})); \
- }
-#define DEFINSTR3(X, ...) \
- static void \
- X(uchar **pcode, enum irclass k, struct oper op1, struct oper op2, struct oper op3) \
- { \
- static const struct desc tab[] = { __VA_ARGS__ }; \
- encode(pcode, tab, countof(tab), k, ((struct oper [3]){op1,op2,op3})); \
- }
-
-DEFINSTR2(Xadrp,
- {8, {PGPRZ, PSYM}, 0x90000000, EN_ADRSYMPGHI21} /* ADR (sym pg hi21) */
-)
-DEFINSTR2(Xadr,
- {8, {PGPRZ, PSYM}, 0x10000000, EN_ADRSYMLO21} /* ADR (sym pg hi21) */
-)
-
-DEFINSTR3(Xadd,
- {4|8, {PGPRSP, PGPRSP, PU12SL12}, 0x11000000, EN_ADDSUBIMM}, /* ADD (immediate) */
- {4|8, {PGPRZ, PGPRZ, PGPRZSHFT}, 0x0B000000, EN_ADDSUBSHFT3R}, /* ADD (shifted register) */
- { 8, {PGPRZ, PGPRZ, PSYM}, 0x11000000, EN_ADDSYMLO12}, /* ADD (sym lo12) */
-)
-DEFINSTR3(Xsub,
- {4|8, {PGPRSP, PGPRSP, PU12SL12}, 0x51000000, EN_ADDSUBIMM}, /* SUB (immediate) */
- {4|8, {PGPRZ, PGPRZ, PGPRZSHFT}, 0x4B000000, EN_ADDSUBSHFT3R}, /* SUB (shifted register) */
-)
-DEFINSTR3(Xsubs,
- {4|8, {PGPRZ, PGPRSP, PU12SL12}, 0x71000000, EN_ADDSUBIMM}, /* SUBS (immediate) */
- {4|8, {PGPRZ, PGPRZ, PGPRZSHFT}, 0x6B000000, EN_ADDSUBSHFT3R}, /* SUBS (shifted register) */
-)
-
-static void
-Xmadd(uchar **pcode, enum irclass k, struct oper d, struct oper n, struct oper m, struct oper a)
-{
- assert(opermatch(PGPRZ, k, d) && opermatch(PGPRZ, k, n)
- && opermatch(PGPRZ, k, a) && opermatch(PGPRZ, k, m));
- uint sf = k > KI32;
- W32(0x1B000000 | sf<<31 | m.reg<<16 | a.reg<<10 | n.reg<<5 | d.reg);
-}
-
-DEFINSTR3(Xsdiv, {4|8, {PGPRZ, PGPRZ, PGPRZ}, 0x1AC00C00, EN_ARITH3R})
-DEFINSTR3(Xudiv, {4|8, {PGPRZ, PGPRZ, PGPRZ}, 0x1AC00800, EN_ARITH3R})
-
-DEFINSTR3(Xand,
- {4|8, {PGPRSP, PGPRZ, PLOGIMM}, 0x12000000, EN_LOGIMM}, /* AND (immediate) */
- {4|8, {PGPRZ, PGPRZ, PGPRZSHFT}, 0x0A000000, EN_LOGSHFT3R}, /* AND (shifted register) */
-)
-DEFINSTR3(Xorr,
- {4|8, {PGPRSP, PGPRZ, PLOGIMM}, 0x32000000, EN_LOGIMM}, /* ORR (immediate) */
- {4|8, {PGPRZ, PGPRZ, PGPRZSHFT}, 0x2A000000, EN_LOGSHFT3R}, /* ORR (shifted register) */
-)
-DEFINSTR3(Xorn, {4|8, {PGPRZ, PGPRZ, PGPRZSHFT}, 0x2A200000, EN_LOGSHFT3R})
-DEFINSTR3(Xeor,
- {4|8, {PGPRSP, PGPRZ, PLOGIMM}, 0x52000000, EN_LOGIMM}, /* EOR (immediate) */
- {4|8, {PGPRZ, PGPRZ, PGPRZSHFT}, 0x4A000000, EN_LOGSHFT3R}, /* EOR (shifted register) */
-)
-DEFINSTR3(Xlslv, {4|8, {PGPRZ, PGPRZ, PGPRZ}, 0x1AC02000, EN_ARITH3R})
-DEFINSTR3(Xlsrv, {4|8, {PGPRZ, PGPRZ, PGPRZ}, 0x1AC02400, EN_ARITH3R})
-DEFINSTR3(Xasrv, {4|8, {PGPRZ, PGPRZ, PGPRZ}, 0x1AC02800, EN_ARITH3R})
-static void
-Xubfm(uchar **pcode, enum irclass k, struct oper rd, struct oper rn, uint immr, uint imms)
-{
- uint x = k != KI32;
- uint nbit = x ? 64 : 32;
- assert(opermatch(PGPRZ, k, rd) && opermatch(PGPRZ, k, rn) && immr < nbit && imms < nbit);
- W32(x<<31 | 0x53000000 | x<<22 | immr<<16 | imms<<10 | rn.reg<<5 | rd.reg);
-}
-static void
-Xsbfm(uchar **pcode, enum irclass k, struct oper rd, struct oper rn, uint immr, uint imms)
-{
- uint x = k != KI32;
- uint nbit = x ? 64 : 32;
- assert(opermatch(PGPRZ, k, rd) && opermatch(PGPRZ, k, rn) && immr < nbit && imms < nbit);
- W32(x<<31 | 0x13000000 | x<<22 | immr<<16 | imms<<10 | rn.reg<<5 | rd.reg);
-}
-
-DEFINSTR2(Xmovz, {4|8, {PGPRZ, PU16SL16}, 0x52800000, EN_MOVEIMM}, /* MOVZ */)
-DEFINSTR2(Xmovn, {4|8, {PGPRZ, PU16SL16}, 0x12800000, EN_MOVEIMM}, /* MOVN */)
-DEFINSTR2(Xmovk, {4|8, {PGPRZ, PU16SL16}, 0x72800000, EN_MOVEIMM}, /* MOVK */)
-DEFINSTR2(Xldr,
- {4, {PGPRZ, PMEMAIMMW}, 0xB9400000, EN_MEMAIMMW}, /* LDR (immediate) */
- {8, {PGPRZ, PMEMAIMMX}, 0xF9400000, EN_MEMAIMMX},
- {4, {PGPRZ, PMEMAREG}, 0xB8600800, EN_MEMAREG}, /* LDR (register) */
- {8, {PGPRZ, PMEMAREG}, 0xF8600800, EN_MEMAREG},
- {4, {PGPRZ, PSYM}, 0x18000000, EN_LDSYMLO19}, /* LDR (literal) */
- {8, {PGPRZ, PSYM}, 0x58000000, EN_LDSYMLO19},
- {4, {PGPRZ, PMEMPREPOST}, 0xB8400000, EN_MEMAPREPOST}, /* LDR (immediate, (pre/postinc)) */
- {8, {PGPRZ, PMEMPREPOST}, 0xF8400000, EN_MEMAPREPOST},
-)
-DEFINSTR2(Xfldr,
- {4, {PFPR, PMEMAIMMW}, 0xBD400000, EN_MEMAIMMW}, /* LDR (immediate) */
- {8, {PFPR, PMEMAIMMX}, 0xFD400000, EN_MEMAIMMX},
- {4, {PFPR, PMEMAREG}, 0xBC600800, EN_MEMAREG}, /* LDR (register) */
- {8, {PFPR, PMEMAREG}, 0xFC600800, EN_MEMAREG},
- {4, {PFPR, PMEMPREPOST}, 0xBC400000, EN_MEMAPREPOST}, /* LDR (immediate, (pre/postinc)) */
- {8, {PFPR, PMEMPREPOST}, 0xFC400000, EN_MEMAPREPOST},
-)
-DEFINSTR2(Xldrsw,
- {8, {PGPRZ, PMEMAIMMW}, 0xB9800000, EN_MEMAIMMW}, /* LDRSW (immediate) */
-// {8, {PGPRZ, PMEMAREG}, 0xB8A00800, EN_MEMAREG}, /* LDRSW (register) */
- {8, {PGPRZ, PMEMPREPOST}, 0xB8800000, EN_MEMAPREPOST}, /* LDRSW (immediate, (pre/postinc)) */
-)
-DEFINSTR2(Xldrh,
- {4|8, {PGPRZ, PMEMAIMMH}, 0x79400000, EN_MEMAIMMH}, /* LDRH (immediate) */
- {4|8, {PGPRZ, PMEMAREG}, 0x78600800, EN_MEMAREG}, /* LDRH (register) */
- {4|8, {PGPRZ, PMEMPREPOST}, 0x78400000, EN_MEMAPREPOST}, /* LDRH (immediate, (pre/postinc)) */
-)
-DEFINSTR2(Xldrsh,
- {4, {PGPRZ, PMEMAIMMH}, 0x79C00000, EN_MEMAIMMH}, /* LDRSH (immediate) */
- {8, {PGPRZ, PMEMAIMMH}, 0x79800000, EN_MEMAIMMH},
- {4, {PGPRZ, PMEMAREG}, 0x78E00800, EN_MEMAREG}, /* LDRSH (register) */
- {8, {PGPRZ, PMEMAREG}, 0x78A00800, EN_MEMAREG},
- {4, {PGPRZ, PMEMPREPOST}, 0x78C00000, EN_MEMAPREPOST}, /* LDRSH (immediate, (pre/postinc)) */
- {8, {PGPRZ, PMEMPREPOST}, 0x78800000, EN_MEMAPREPOST},
-)
-DEFINSTR2(Xldrb,
- {4|8, {PGPRZ, PMEMAIMM}, 0x39400000, EN_MEMAIMM}, /* LDRB (immediate) */
- {4|8, {PGPRZ, PMEMAREG}, 0x38600800, EN_MEMAREG}, /* LDRB (register) */
- {4|8, {PGPRZ, PMEMPREPOST}, 0x38400000, EN_MEMAPREPOST}, /* LDRB (immediate, (pre/postinc)) */
-)
-DEFINSTR2(Xldrsb,
- {4, {PGPRZ, PMEMAIMM}, 0x39C00000, EN_MEMAIMM}, /* LDRSB (immediate) */
- {8, {PGPRZ, PMEMAIMM}, 0x39800000, EN_MEMAIMM},
- {4, {PGPRZ, PMEMAREG}, 0x38E00800, EN_MEMAREG}, /* LDRSB (register) */
- {8, {PGPRZ, PMEMAREG}, 0x38A00800, EN_MEMAREG},
- {4, {PGPRZ, PMEMPREPOST}, 0x38C00000, EN_MEMAPREPOST}, /* LDRSB (immediate, (pre/postinc)) */
- {8, {PGPRZ, PMEMPREPOST}, 0x38800000, EN_MEMAPREPOST},
-)
-DEFINSTR2(Xstr,
- {4, {PGPRZ, PMEMAIMMW}, 0xB9000000, EN_MEMAIMMW}, /* STR (immediate) */
- {8, {PGPRZ, PMEMAIMMX}, 0xF9000000, EN_MEMAIMMX},
- {4, {PGPRZ, PMEMAREG}, 0xB8200800, EN_MEMAREG}, /* STR (register) */
- {8, {PGPRZ, PMEMAREG}, 0xF8200800, EN_MEMAREG},
- {4, {PGPRZ, PMEMPREPOST}, 0xB8000000, EN_MEMAPREPOST}, /* STR (immediate, (pre/postinc)) */
- {8, {PGPRZ, PMEMPREPOST}, 0xF8000000, EN_MEMAPREPOST},
-)
-DEFINSTR2(Xfstr,
- {4, {PFPR, PMEMAIMMW}, 0xBD000000, EN_MEMAIMMW}, /* LDR (immediate) */
- {8, {PFPR, PMEMAIMMX}, 0xFD000000, EN_MEMAIMMX},
- {4, {PFPR, PMEMAREG}, 0xBC200800, EN_MEMAREG}, /* LDR (register) */
- {8, {PFPR, PMEMAREG}, 0xFC200800, EN_MEMAREG},
- {4, {PFPR, PMEMPREPOST}, 0xBC000000, EN_MEMAPREPOST}, /* LDR (immediate, (pre/postinc)) */
- {8, {PFPR, PMEMPREPOST}, 0xFC000000, EN_MEMAPREPOST},
-)
-DEFINSTR2(Xstrh,
- {4|8, {PGPRZ, PMEMAIMMH}, 0x79000000, EN_MEMAIMMH}, /* STRH (immediate) */
- {4|8, {PGPRZ, PMEMAREG}, 0x78200800, EN_MEMAREG}, /* STRH (register) */
- {4|8, {PGPRZ, PMEMPREPOST}, 0x78000000, EN_MEMAPREPOST}, /* STRH (immediate, (pre/postinc)) */
-)
-DEFINSTR2(Xstrb,
- {4|8, {PGPRZ, PMEMAIMM}, 0x39000000, EN_MEMAIMM}, /* STRB (immediate) */
- {4|8, {PGPRZ, PMEMAREG}, 0x38200800, EN_MEMAREG}, /* STRB (register) */
- {4|8, {PGPRZ, PMEMPREPOST}, 0x38000000, EN_MEMAPREPOST}, /* STRB (immediate, (pre/postinc)) */
-)
-DEFINSTR3(Xldp,
- {8, {PGPRZ, PGPRZ, PMEMPREPOST}, 0xA8400000, EN_MEMPPREPOST} /* LDP (immediate, (pre/postinc)) */
-)
-DEFINSTR3(Xstp,
- {8, {PGPRZ, PGPRZ, PMEMPREPOST}, 0xA8000000, EN_MEMPPREPOST} /* STP (immediate, (pre/postinc)) */
-)
-DEFINSTR3(Xfldp,
- {8, {PFPR, PFPR, PMEMPREPOST}, 0x6CC00000, EN_MEMPPREPOST} /* LDP (immediate, (pre/postinc)) */
-)
-DEFINSTR3(Xfstp,
- {8, {PFPR, PFPR, PMEMPREPOST}, 0x6C800000, EN_MEMPPREPOST} /* STP (immediate, (pre/postinc)) */
-)
-static void
-Xcall(uchar **pcode, struct oper dst)
-{
- if (dst.t == OSYM) {
- objreloc(xcon2sym(dst.con), REL_CALL26, Stext, *pcode - objout.textbegin, 0);
- W32(0x94000000); /* BL <rel26> */
- } else {
- assert(opermatch(PGPRZ, KPTR, dst));
- W32(0xD63F0000 | dst.reg<<5); /* BLR Xn */
- }
-}
-DEFINSTR2(Xfmov,
- {4|8, {PFPR, PFPR}, 0x1E204000, EN_FP2R},
- {4, {PFPR, PGPRZ}, 0x1E270000, EN_FP1GPR1},
- { 8, {PFPR, PGPRZ}, 0x9E670000, EN_FP1GPR1},
- {4, {PGPRZ, PFPR}, 0x1E260000, EN_FP1GPR1},
- { 8, {PGPRZ, PFPR}, 0x9E660000, EN_FP1GPR1},
-)
-DEFINSTR2(Xfneg, {4|8, {PFPR, PFPR}, 0x1E214000, EN_FP2R})
-DEFINSTR2(Xscvtfw, {4|8, {PFPR, PGPRZ}, 0x1E220000, EN_FP2R})
-DEFINSTR2(Xscvtfx, {4|8, {PFPR, PGPRZ}, 0x9E220000, EN_FP2R})
-DEFINSTR2(Xfcvtzsw, {4|8, {PGPRZ, PFPR}, 0x1E380000, EN_FP2R})
-DEFINSTR2(Xfcvtzsx, {4|8, {PGPRZ, PFPR}, 0x9E380000, EN_FP2R})
-DEFINSTR2(Xucvtfw, {4|8, {PFPR, PGPRZ}, 0x1E230000, EN_FP2R})
-DEFINSTR2(Xucvtfx, {4|8, {PFPR, PGPRZ}, 0x9E230000, EN_FP2R})
-DEFINSTR2(Xfcvtzuw, {4|8, {PGPRZ, PFPR}, 0x1E390000, EN_FP2R})
-DEFINSTR2(Xfcvtzux, {4|8, {PGPRZ, PFPR}, 0x9E390000, EN_FP2R})
-DEFINSTR2(Xfcvtds, {4, {PFPR, PFPR}, 0x1E624000, EN_FP2R})
-DEFINSTR2(Xfcvtsd, {4, {PFPR, PFPR}, 0x1E22C000, EN_FP2R})
-DEFINSTR3(Xfadd, {4|8, {PFPR, PFPR, PFPR}, 0x1E202800, EN_FP3R})
-DEFINSTR3(Xfsub, {4|8, {PFPR, PFPR, PFPR}, 0x1E203800, EN_FP3R})
-DEFINSTR3(Xfmul, {4|8, {PFPR, PFPR, PFPR}, 0x1E200800, EN_FP3R})
-DEFINSTR3(Xfdiv, {4|8, {PFPR, PFPR, PFPR}, 0x1E201800, EN_FP3R})
-DEFINSTR2(Xfcmp,
- {4|8, {PFPR, PZERO}, 0x1E602008, EN_FPCMPZ},
- {4|8, {PFPR, PFPR}, 0x1E602000, EN_FPCMP},
-)
-
-static void
-gencopy(uchar **pcode, enum irclass cls, struct block *blk, int curi, struct oper dst, union ref val)
-{
- assert(dst.t == OREG);
- struct oper src;
- if (val.bits == UNDREF.bits) return;
- if (isintcon(val)) {
- assert(dst.reg <= R(31));
- /* MOV r, #imm */
- uvlong u = intconval(val);
- if (~u <= 0xFFFF) {
- /* immediate can be encoded with 1 MOVN instruction */
- Xmovn(pcode, cls, dst, mkoper(OIMM, .imm = ~u));
- } else {
- /* generate MOV (+ MOVKs) */
- if (cls == KI32) u = (uint)u;
- int s = 0;
- while (s < 48 && (u >> s & 0xFFFF) == 0) s += 16;
- if ((u &~ (0xFFFFull << s)) != 0 && aarch64_logimm(NULL, cls, u)) {
- /* can be encoded as a logical immediate in 1 instr */
- Xorr(pcode, cls, dst, REGZR, mkoper(OIMM, .uimm = u));
- } else {
- Xmovz(pcode, cls, dst, mkoper(OIMM, .imm = u & (0xFFFFull << s)));
- for (s += 16; s <= 48; s += 16) {
- if ((u >> s) & 0xFFFF)
- Xmovk(pcode, cls, dst, mkoper(OIMM, .imm = u & (0xFFFFull << s)));
- }
- }
- }
- } else if (opermatch(PGPRZ, cls, (src = ref2oper(val))) && kisint(cls)) {
- Xorr(pcode, cls, dst, REGZR, src); /* MOV Rd, Rn ==> ORR Rd, zr, Rn */
- } else if (kisflt(cls) || opermatch(PFPR, 0, src)) {
- if (src.t == OREG)
- Xfmov(pcode, cls, dst, src);
- else if (src.t == OIMM && src.imm == 0)
- Xfmov(pcode, cls, dst, REGZR);
- else assert(0);
- } else if (isaddrcon(val,0) || (val.t == RADDR && isaddrcon(addrtab.p[val.i].base,0))) {
- if ((ccopt.pic || (contab.p[val.i].flag & SFUNC)) && !(contab.p[val.i].flag & SLOCAL)) {
- Xadrp(pcode, KPTR, dst, src);
- Xadd(pcode, KPTR, dst, dst, src);
- } else {
- Xadr(pcode, KPTR, dst, src);
- }
- } else assert(0);
-}
-
-/* maps blk -> address when resolved; or to linked list of jump displacement
- * relocations */
-static struct blkaddr {
- bool resolved;
- union {
- uint addr;
- uint relreloc;
- };
-} *blkaddr;
-
-enum cc {
- CCEQ, CCNE, CCCS, CCCC, CCMI, CCPL, CCVS, CCVC,
- CCHI, CCLS, CCGE, CCLT, CCGT, CCLE, CCAL, CCNV,
- CCHS = CCCS, CCLO = CCCC,
-};
-
-static void
-Xbcc(uchar **pcode, enum cc cc, struct block *dst)
-{
- int disp, insaddr = *pcode - objout.textbegin;
-
- if (blkaddr[dst->id].resolved) {
- disp = (int)(blkaddr[dst->id].addr - insaddr)/4;
- assert(disp >= -(1<<18) && disp < (1<<18));
- } else {
- disp = blkaddr[dst->id].relreloc;
- blkaddr[dst->id].relreloc = insaddr;
- }
- assert(in_range(cc, 0, 0xF));
- W32(0x54000000 | (disp & 0x7FFFF)<<5 | cc);
-}
-
-static void
-Xcbcc(uchar **pcode, enum irclass k, uint rt, enum cc cc, struct block *dst)
-{
- int disp, insaddr = *pcode - objout.textbegin;
- if (blkaddr[dst->id].resolved) {
- disp = (int)(blkaddr[dst->id].addr - insaddr)/4;
- assert(disp >= -(1<<18) && disp < (1<<18));
- } else {
- disp = blkaddr[dst->id].relreloc;
- blkaddr[dst->id].relreloc = insaddr;
- }
- assert(in_range(cc, CCEQ, CCNE));
- assert(in_range(rt, 0, 31));
- W32(0x34000000 | (uint)(k > KI32)<<31 | cc<<24 | (disp & 0x7FFFF)<<5 | rt);
-}
-
-/* condition code for CMP */
-static const schar icmpop2cc[] = {
- [Oequ] = CCEQ, [Oneq] = CCNE,
- [Olth] = CCLT, [Ogth] = CCGT, [Olte] = CCLE, [Ogte] = CCGE,
- [Oulth] = CCLO, [Ougth] = CCHI, [Oulte] = CCLS, [Ougte] = CCHS,
-}, fcmpop2cc[] = {
- [Oequ] = CCEQ, [Oneq] = CCNE,
- [Olth] = CCLO, [Ogth] = CCGT, [Olte] = CCLS, [Ogte] = CCGE,
-};
-
-static void
-emitbranch(uchar **pcode, struct block *blk)
-{
- enum irclass cbk = 0;
- struct oper cbopr;
- enum cc cc = CCAL;
- assert(blk->s1);
- if (blk->s2) {
- /* conditional branch.. */
- union ref arg = blk->jmp.arg[0];
- assert(arg.t == RTMP);
- struct instr *ins = &instrtab[arg.i];
- if (in_range(ins->op, Oequ, Oneq) && ins->r.bits == ZEROREF.bits) {
- cc = ins->op == Oequ ? CCEQ : CCNE;
- cbk = ins->cls;
- cbopr = ref2oper(ins->l);
- assert(opermatch(PGPRZ, ins->cls, cbopr));
- } else if (oiscmp(ins->op)) {
- /* for CMP instr */
- cc = (kisint(ins->cls) ? icmpop2cc : fcmpop2cc)[ins->op];
- } else {
- /* implicit by ZF */
- cc = CCNE;
- }
- if (blk->s1 == blk->lnext) {
- /* if s1 is next adjacent block, swap s1,s2 and flip condition to emit a
- * single jump */
- struct block *tmp = blk->s1;
- blk->s1 = blk->s2;
- blk->s2 = tmp;
- cc ^= 1;
- }
- }
- /* make sure to fallthru if jumping to next adjacent block */
- if (blk->s2 || blk->s1 != blk->lnext) {
- if (cbk) Xcbcc(pcode, cbk, cbopr.reg, cc, blk->s1);
- else Xbcc(pcode, cc, blk->s1);
- }
- if (blk->s2 && blk->s2 != blk->lnext)
- Xbcc(pcode, CCAL, blk->s2);
-}
-
-static struct instr *lastcmp;
-
-static void
-emitinstr(uchar **pcode, struct function *fn, struct block *blk, int curi, struct instr *ins)
-{
- struct oper dst, o1, o2;
- enum irclass cls = ins->cls;
- void (*X3)(uchar **, enum irclass, struct oper, struct oper, struct oper) = NULL;
- void (*X2)(uchar **, enum irclass, struct oper, struct oper) = NULL;
-
- switch (ins->op) {
- default: fatal(NULL, "aarch64 unimplemented instr: %s", opnames[ins->op]);
- case Onop: break;
- case Omove:
- dst = ref2oper(ins->l);
- gencopy(pcode, cls, blk, curi, dst, ins->r);
- break;
- case Oextu32: cls = KI32;
- /* fallthru */
- case Ocopy:
- dst = reg2oper(ins->reg-1);
- gencopy(pcode, cls, blk, curi, dst, ins->l);
- break;
- case Oswap:
- o1 = ref2oper(ins->l), o2 = ref2oper(ins->r);
- if (kisflt(ins->cls) && ins->l.i != mctarg->fprscratch && ins->r.i != mctarg->fprscratch) {
- dst = reg2oper(mctarg->fprscratch);
- Xfmov(pcode, cls, dst, o1);
- Xfmov(pcode, cls, o1, o2);
- Xfmov(pcode, cls, o2, dst);
- } else if (ins->l.i != mctarg->gprscratch && ins->r.i != mctarg->gprscratch) {
- dst = reg2oper(mctarg->gprscratch);
- Xorr(pcode, cls, dst, REGZR, o1);
- Xorr(pcode, cls, o1, REGZR, o2);
- Xorr(pcode, cls, o2, REGZR, dst);
- } else {
- Xeor(pcode, cls, o1, o1, o2);
- Xeor(pcode, cls, o2, o1, o2);
- Xeor(pcode, cls, o1, o1, o2);
- }
- break;
- case Onot: /* MVN Rd, Rn ==> ORN Rd, zr, Rn */
- Xorn(pcode, cls, reg2oper(ins->reg-1), REGZR, ref2oper(ins->l));
- break;
- case Oneg:
- if (kisint(ins->cls)) /* NEG Rd, Rn ==> SUB Rd, zr, Rn */
- Xsub(pcode, cls, reg2oper(ins->reg-1), REGZR, ref2oper(ins->l));
- else
- Xfneg(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l));
- break;
- case Oexts8: case Oexts16: case Oexts32: /* SXTB/H/W Rd, Rn ==> SBFM Rd, Rn, #0, #7/15/31 */
- Xsbfm(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l), 0, (8<<(ins->op-Oexts8)/2)-1);
- break;
- case Oextu8: case Oextu16: /* UXTB/H Rd, Rn ==> UBFM Rd, Rn, #0, #7/15 */
- Xubfm(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l), 0, (8<<(ins->op-Oexts8)/2)-1);
- break;
- case Ocvts32f: X2 = Xscvtfw; goto Cvt;
- case Ocvts64f: X2 = Xscvtfx; goto Cvt;
- case Ocvtf32s:
- X2 = cls == KI32 ? Xfcvtzsw : Xfcvtzsx;
- cls = KF32;
- goto Cvt;
- case Ocvtf64s:
- X2 = cls == KI32 ? Xfcvtzsw : Xfcvtzsx;
- cls = KF64;
- goto Cvt;
- case Ocvtu32f: X2 = Xucvtfw; goto Cvt;
- case Ocvtu64f: X2 = Xucvtfx; goto Cvt;
- case Ocvtf32u:
- X2 = cls == KI32 ? Xfcvtzuw : Xfcvtzux;
- cls = KF32;
- goto Cvt;
- case Ocvtf64u:
- X2 = cls == KI32 ? Xfcvtzuw : Xfcvtzux;
- cls = KF64;
- goto Cvt;
- case Ocvtf32f64: cls = KF32; X2 = Xfcvtsd; goto Cvt;
- case Ocvtf64f32: cls = KF32; X2 = Xfcvtds; goto Cvt;
- Cvt:
- X2(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l));
- break;
- case Oadd: X3 = kisint(cls) ? Xadd : Xfadd; goto ALU3;
- case Osub: X3 = kisint(cls) ? Xsub : Xfsub; goto ALU3;
- case Omul: if (kisflt(cls)) { X3 = Xfmul; goto ALU3; }
- /* MUL Rd,Rn,Rm ==> MADD Rd,Rn,Rm,zr */
- Xmadd(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l), ref2oper(ins->r), REGZR);
- break;
- case Odiv: X3 = kisint(cls) ? Xsdiv : Xfdiv; goto ALU3;
- case Oudiv: X3 = Xudiv; goto ALU3;
- case Oand: X3 = Xand; goto ALU3;
- case Oior: X3 = Xorr; goto ALU3;
- case Oxor: X3 = Xeor; goto ALU3;
- ALU3:
- X3(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l), ref2oper(ins->r));
- break;
- case Oshl:
- if (ins->r.t == RICON) {
- uint nbit = cls == KI32 ? 32 : 64, s = ins->r.i & (nbit-1);
- assert(s > 0);
- Xubfm(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l), nbit-s, nbit-s-1);
- } else {
- X3 = Xlslv;
- goto ALU3;
- }
- break;
- case Oslr:
- if (ins->r.t == RICON) {
- uint nbit = cls == KI32 ? 32 : 64, s = ins->r.i & (nbit-1);
- assert(s > 0);
- Xubfm(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l), s, nbit-1);
- } else {
- X3 = Xlsrv;
- goto ALU3;
- }
- break;
- case Osar:
- if (ins->r.t == RICON) {
- uint nbit = cls == KI32 ? 32 : 64, s = ins->r.i & (nbit-1);
- assert(s > 0);
- Xsbfm(pcode, cls, reg2oper(ins->reg-1), ref2oper(ins->l), s, nbit-1);
- } else {
- X3 = Xasrv;
- goto ALU3;
- }
- break;
- case Oequ: case Oneq:
- if (!ins->reg && kisint(cls) && ins->r.bits == ZEROREF.bits) /* handled by emitbranch for CBZ/CBNZ */
- break;
- case Olth: case Ogth: case Olte: case Ogte:
- case Oulth: case Ougth: case Oulte: case Ougte:
- if (lastcmp && lastcmp->cls == cls
- && lastcmp->l.bits == ins->l.bits && lastcmp->r.bits == ins->r.bits)
- /* reuse flags from previous identical cmp */ ;
- else if (kisflt(cls))
- Xfcmp(pcode, cls, ref2oper(ins->l), ref2oper(ins->r));
- else /* CMP ... ==> SUBS zr, ... */
- Xsubs(pcode, cls, REGZR, ref2oper(ins->l), ref2oper(ins->r));
- lastcmp = ins;
- if (ins->reg) {
- enum cc cc = (kisflt(cls) ? fcmpop2cc : icmpop2cc)[ins->op];
- dst = reg2oper(ins->reg-1);
- assert(dst.reg < R(31));
- W32(0x1A9F07E0 | (cc^1)<<12 | dst.reg); /* CSET Wd, <invcond> */
- }
- break;
- case Oloadu8: X2 = Xldrb; goto Load;
- case Oloads8: X2 = Xldrsb; goto Load;
- case Oloadu16: X2 = Xldrh; goto Load;
- case Oloads16: X2 = Xldrsh; goto Load;
- case Oloads32:
- if (cls != KI32) {
- X2 = Xldrsw;
- goto Load;
- }
- case Oloadu32:
- cls = KI32;
- /* fallthru */
- case Oloadi64: X2 = Xldr;
- Load:
- X2(pcode, cls, reg2oper(ins->reg-1), mkmemoper(1<<(ins->op - Oloads8)/2, ins->l));
- break;
- case Oloadf32: case Oloadf64:
- Xfldr(pcode, cls, reg2oper(ins->reg-1), mkmemoper(ins->op == Oloadf32 ? 4 : 8, ins->l));
- break;
- case Ostorei8: cls = KI32; X2 = Xstrb; goto Store;
- case Ostorei16: cls = KI32; X2 = Xstrh; goto Store;
- case Ostorei32: cls = KI32; X2 = Xstr; goto Store;
- case Ostorei64: cls = KI64; X2 = Xstr;
- Store:
- X2(pcode, cls, ins->r.bits == ZEROREF.bits ? REGZR : ref2oper(ins->r),
- mkmemoper(1<<(ins->op-Ostorei8), ins->l));
- break;
- case Ostoref32: case Ostoref64:
- Xfstr(pcode, KF32 + ins->op-Ostoref32, ref2oper(ins->r), mkmemoper(ins->op == Oloadf32 ? 4 : 8, ins->l));
- break;
- case Ocall:
- Xcall(pcode, ref2oper(ins->l));
- break;
- }
-}
-
-struct frame {
- regset save;
- struct rpair { uchar a,b; } pairs[10];
- uchar single[2];
- uint nfpairs, ngpairs;
-};
-
-static void
-prologue(uchar **pcode, struct frame *frame, struct function *fn)
-{
- *frame = (struct frame){0};
- regset save = frame->save = (fn->regusage & mctarg->rcallee) | (usefp * BIT(FP)) | (!fn->isleaf * BIT(LR));
- if (save) {
- int prev = 0;
- struct rpair *p = frame->pairs;
- for (uint reg = V(8); reg <= V(15); ++reg) {
- if (!rstest(save, reg)) continue;
- if (prev) {
- *p++ = (struct rpair) {prev, reg};
- ++frame->nfpairs;
- prev = 0;
- } else prev = reg;
- }
- uint ngpr = popcnt(save & (BIT(32)-1));
- if (prev) {
- if (ngpr & 1) {
- frame->single[0] = prev;
- frame->single[1] = prev = lowestsetbit(save);
- rsclr(&save, prev);
- } else {
- *p++ = (struct rpair) {prev, V(0)};
- ++frame->nfpairs;
- }
- prev = 0;
- } else if (ngpr & 1) {
- prev = 0x100;
- }
- for (uint reg = R(19); reg <= LR; ++reg) {
- if (!rstest(save, reg)) continue;
- if (prev) {
- *p++ = (struct rpair) {prev, reg};
- ++frame->ngpairs;
- prev = 0;
- } else prev = reg;
- }
- assert(!prev);
-
- p = frame->pairs;
- struct oper adr = mkoper(OMEM, .m = {.mode = APREIDX, .base = SP, .disp = -16});
- for (int i = 0; i < frame->nfpairs; ++i, ++p)
- Xfstp(pcode, KF64, reg2oper(p->a), reg2oper(p->b), adr);
- adr.m.disp = -8;
- if (frame->single[0]) Xfstr(pcode, KF64, reg2oper(frame->single[0]), adr);
- if (frame->single[1]) Xstr(pcode, KPTR, reg2oper(frame->single[1]), adr);
- adr.m.disp = -16;
- for (int i = 0; i < frame->ngpairs; ++i, ++p)
- Xstp(pcode, KPTR, reg2oper(p->a), reg2oper(p->b), adr);
- }
-
- if (usefp) /* MOV x29, sp */
- Xadd(pcode, KPTR, reg2oper(FP), reg2oper(SP), mkoper(OIMM,));
-
- /* ensure stack is 16-byte aligned for function calls */
- if (!fn->isleaf && ((fn->stksiz) & 0xF) != 0) {
- assert(usefp);
- rbpoff -= 8;
- fn->stksiz += 8;
- }
- if (fn->stksiz) Xsub(pcode, KPTR, reg2oper(SP), reg2oper(SP), mkoper(OIMM, .imm = fn->stksiz));
-}
-
-static void
-epilogue(uchar **pcode, struct function *fn, struct frame *frame)
-{
- if (fn->stksiz) Xadd(pcode, KPTR, reg2oper(SP), reg2oper(SP), mkoper(OIMM, .imm = fn->stksiz));
- if (frame->save) {
- struct rpair *p = frame->pairs + frame->nfpairs + frame->ngpairs - 1;
- struct oper adr = mkoper(OMEM, .m = {.mode = APOSTIDX, .base = SP, .disp = 16});
- for (int i = 0; i < frame->ngpairs; ++i, --p)
- Xldp(pcode, KPTR, reg2oper(p->a), reg2oper(p->b), adr);
- adr.m.disp = 8;
- if (frame->single[1]) Xldr(pcode, KPTR, reg2oper(frame->single[1]), adr);
- if (frame->single[0]) Xfldr(pcode, KF64, reg2oper(frame->single[0]), adr);
- adr.m.disp = 16;
- for (int i = 0; i < frame->nfpairs; ++i, --p)
- Xfldp(pcode, KF64, reg2oper(p->a), reg2oper(p->b), adr);
- }
-}
-
-static void
-emitbin(struct function *fn)
-{
- struct block *blk;
- uchar **pcode = &objout.code;
-
- while ((*pcode - objout.textbegin) % 4) ++*pcode;
- fnstart = *pcode;
- curfnsym = fn->name;
-
- /** prologue **/
-
- /* only use frame pointer in non-leaf functions and functions that use the stack */
- usefp = !fn->isleaf || fn->stksiz;
- struct frame frame;
- prologue(pcode, &frame, fn);
-
- if (*pcode - fnstart > 8) {
- /* largue prologue -> largue epilogue -> transform to use single exit point */
- struct block *exit = NULL;
- blk = fn->entry->lprev;
- do {
- if (blk->jmp.t == Jret) {
- if (!exit) {
- if (blk->ins.n == 0) {
- exit = blk;
- continue;
- } else {
- exit = newblk(fn);
- exit->lnext = blk->lnext;
- exit->lprev = blk;
- blk->lnext = exit;
- exit->lnext->lprev = exit;
- exit->id = fn->nblk++;
- exit->jmp.t = Jret;
- }
- }
- blk->jmp.t = Jb;
- memset(blk->jmp.arg, 0, sizeof blk->jmp.arg);
- blk->s1 = exit;
- } else if (exit) {
- /* thread jumps to the exit block */
- if (blk->s1 && !blk->s1->ins.n && blk->s1->s1 == exit && !blk->s1->s2) blk->s1 = exit;
- if (blk->s2 && !blk->s2->ins.n && blk->s2->s1 == exit && !blk->s2->s2) blk->s2 = exit;
- }
- } while ((blk = blk->lprev) != fn->entry);
- }
-
- blkaddr = allocz(fn->passarena, fn->nblk * sizeof *blkaddr, 0);
-
- blk = fn->entry;
- do {
- struct blkaddr *bb = &blkaddr[blk->id];
- uint bbaddr = *pcode - objout.textbegin;
- assert(!bb->resolved);
- while (bb->relreloc) {
- int disp = (bbaddr - bb->relreloc)/4;
- assert(disp >= -(1<<18) && disp < (1<<18));
- uint tmp = rd32targ(objout.textbegin + bb->relreloc);
- wr32le(objout.textbegin + bb->relreloc, (tmp &~ (0x7FFFFu<<5)) | (disp & 0x7FFFF)<<5);
- bb->relreloc = tmp>>5 & 0x7FFFF;
- }
- bb->resolved = 1;
- bb->addr = bbaddr;
-
- lastcmp = NULL;
- for (int i = 0; i < blk->ins.n; ++i)
- emitinstr(pcode, fn, blk, i, &instrtab[blk->ins.p[i]]);
- if (blk->jmp.t == Jret) {
- if (blk->lnext != fn->entry && blk->lnext->jmp.t == Jret && blk->lnext->ins.n == 0)
- continue; /* fallthru to next blk's RET */
- epilogue(pcode, fn, &frame);
- W32(0xD65F03C0); /* RET */
- } else if (blk->jmp.t == Jtrap) {
- W32(0xD4200020); /* BRK #0x1 */
- } else emitbranch(pcode, blk);
- } while ((blk = blk->lnext) != fn->entry);
- objdeffunc(fn->name, fn->globl, fnstart - objout.textbegin, *pcode - fnstart);
-}
-
-void
-aarch64_emit(struct function *fn)
-{
- fn->stksiz = alignup(fn->stksiz, 8);
- if (fn->stksiz > 1<<24) error(NULL, "'%s' stack frame too big", fn->name);
- emitbin(fn);
-}
-
-/* vim:set ts=3 sw=3 expandtab: */