diff options
Diffstat (limited to 'utils/tl/thumb.c')
| -rw-r--r-- | utils/tl/thumb.c | 551 |
1 files changed, 541 insertions, 10 deletions
diff --git a/utils/tl/thumb.c b/utils/tl/thumb.c index f16175ea..a309f82b 100644 --- a/utils/tl/thumb.c +++ b/utils/tl/thumb.c @@ -2,6 +2,9 @@ static long thumboprr(int); static long thumboprrr(int, int); +static long thumbopfp(int, int); +static long thumbomvl(Prog *p, Adr *a, int dr); +static long thumbofsr(int a, int r, long v, int b, Prog *p); static long thumbopirr(int , int); static long thumbopri(int); static long thumbophh(int); @@ -13,6 +16,11 @@ static void numr(Prog *, int, int, int); static void regis(Prog *, int, int, int); static void dis(int, int); +/* Encode the 32-bit instructions as two 16-bit ones */ +#define SPLIT_INS(filled, empty) \ + empty = filled >> 16; \ + filled = filled & 0xffff; + // build a constant using neg, add and shift - only worth it if < 6 bytes */ static int immbuildcon(int c, Prog *p) @@ -48,7 +56,7 @@ immoreg(int off, Prog *p) if(off < 0) return C_GOREG; - if(as == AMOVW) + if(as == AMOVW || as == AMOVF || as == AMOVD) v = 4; else if(as == AMOVH || as == AMOVHU) v = 2; @@ -80,11 +88,12 @@ immacon(int off, Prog *p, int t1, int t2) static int immauto(int off, Prog *p) { - if(p->as != AMOVW) - diag("bad op in immauto"); + if(p->as != AMOVW && p->as != AMOVF && p->as != AMOVD) + diag("bad op %d in immauto", p->as); mult(p, off, 4); if(off >= 0 && off <= 1020) return C_SAUTO; + return C_LAUTO; } @@ -153,7 +162,6 @@ thumbaclass(Adr *a, Prog *p) diag("D_SHIFT in thumbaclass"); return C_SHIFT; case D_FREG: - diag("D_FREG in thumbaclass"); return C_FREG; case D_FPCR: diag("D_FPCR in thumbaclass"); @@ -173,7 +181,7 @@ thumbaclass(Adr *a, Prog *p) a->sym->name, TNAME); a->sym->type = SDATA; } - instoffset = a->sym->value + a->offset + INITDAT; + instoffset = a->sym->value + a->offset + INITDAT + a->sym->base; return C_LEXT; /* INITDAT unknown at this stage */ // return immacon(instoffset, p, C_SEXT, C_LEXT); case D_AUTO: @@ -205,7 +213,7 @@ thumbaclass(Adr *a, Prog *p) s->name, TNAME); s->type = SDATA; } - instoffset = s->value + a->offset + INITDAT; + instoffset = s->value + a->offset + INITDAT + s->base; if(s->type == STEXT || s->type == SLEAF){ instoffset = s->value + a->offset; #ifdef CALLEEBX @@ -221,7 +229,6 @@ thumbaclass(Adr *a, Prog *p) } return C_GOK; case D_FCONST: - diag("D_FCONST in thumaclass"); return C_FCON; case D_CONST: switch(a->name) { @@ -255,7 +262,7 @@ thumbaclass(Adr *a, Prog *p) #endif return C_LCON; } - instoffset = s->value + a->offset + INITDAT; + instoffset = s->value + a->offset + INITDAT + s->base; return C_LCON; /* INITDAT unknown at this stage */ // return immcon(instoffset, p); case D_AUTO: @@ -339,7 +346,6 @@ Optab thumboptab[] = { AADD, C_SCON, C_REG, C_REG, 3, 2, 0 }, { AADD, C_LCON, C_REG, C_REG, 49, 4, 0 }, { AADD, C_GCON, C_REG, C_REG, 36, 4, 0, LFROM }, - // { AADD, C_LCON, C_NONE, C_REG, 3, 2, 0, LFROM }, { ASRL, C_SCON, C_REG, C_REG, 4, 2, 0 }, { ASRL, C_SCON, C_NONE, C_REG, 4, 2, 0 }, { AADD, C_SCON, C_NONE, C_REG, 5, 2, 0 }, @@ -350,6 +356,9 @@ Optab thumboptab[] = { AMOVW, C_SCON, C_NONE, C_REG, 5, 2, 0 }, { AMOVW, C_BCON, C_NONE, C_REG, 47, 4, 0 }, { AMOVW, C_LCON, C_NONE, C_REG, 38, 2, 0, LFROM }, + { AMVN, C_LCON, C_NONE, C_REG, 61, 4, 0 }, + { AMVN, C_LCON, C_REG, C_REG, 61, 4, 0 }, + { ARSB, C_LCON, C_REG, C_REG, 61, 4, 0 }, // { AADD, C_LCON, C_PC, C_REG, 6, 2, 0, LFROM }, // { AADD, C_LCON, C_SP, C_REG, 6, 2, 0, LFROM }, { AADD, C_SCON, C_NONE, C_SP, 7, 2, 0 }, @@ -450,10 +459,81 @@ Optab thumboptab[] = { AMOVHU, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, { AMOVBU, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, + { AMOVF, C_FREG, C_NONE, C_FEXT, 52, 4, REGSB }, + { AMOVF, C_FREG, C_NONE, C_FAUTO, 52, 4, REGSP }, + { AMOVF, C_FREG, C_NONE, C_FOREG, 52, 4, 0 }, + + { AMOVD, C_FREG, C_NONE, C_SAUTO, 52, 4, REGSP }, + { AMOVD, C_FREG, C_NONE, C_SOREG, 52, 4, 0 }, + + { AMOVF, C_FEXT, C_NONE, C_FREG, 53, 4, REGSB }, + { AMOVF, C_FAUTO,C_NONE, C_FREG, 53, 4, REGSP }, + { AMOVF, C_FOREG,C_NONE, C_FREG, 53, 4, 0 }, + + { AMOVD, C_SAUTO,C_NONE, C_FREG, 53, 4, REGSP }, + { AMOVD, C_SOREG,C_NONE, C_FREG, 53, 4, 0 }, + + { AMOVF, C_FREG, C_NONE, C_LEXT, 54, 6, REGSB, LTO }, + { AMOVF, C_FREG, C_NONE, C_LAUTO, 54, 8, REGSP, LTO }, + { AMOVF, C_FREG, C_NONE, C_LOREG, 54, 8, 0, LTO }, + + { AMOVD, C_FREG, C_NONE, C_LEXT, 54, 6, REGSB, LTO }, + + { AMOVF, C_LEXT, C_NONE, C_FREG, 55, 6, REGSB, LFROM }, + { AMOVF, C_LAUTO,C_NONE, C_FREG, 55, 8, REGSP, LFROM }, + { AMOVF, C_LOREG,C_NONE, C_FREG, 55, 8, 0, LFROM }, + + { AMOVD, C_LEXT, C_NONE, C_FREG, 55, 6, REGSB, LFROM }, + + { AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO }, + { AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM }, + + { AADDF, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + { AADDF, C_FREG, C_REG, C_FREG, 56, 4, 0 }, + { AADDF, C_FCON, C_NONE, C_FREG, 56, 4, 0 }, + { AADDF, C_FCON, C_REG, C_FREG, 56, 4, 0 }, + { AMOVF, C_FCON, C_NONE, C_FREG, 56, 4, 0 }, + { AMOVF, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + + { AADDD, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + { AADDD, C_FREG, C_REG, C_FREG, 56, 4, 0 }, + { ASUBF, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + { ASUBF, C_FREG, C_REG, C_FREG, 56, 4, 0 }, + { ASUBD, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + { ASUBD, C_FREG, C_REG, C_FREG, 56, 4, 0 }, + { ADIVD, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + { ADIVF, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + { AMULF, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + { AMULD, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + + { AMOVD, C_FREG, C_NONE, C_FREG, 56, 4, 0 }, + { AMOVD, C_FCON, C_NONE, C_FREG, 56, 4, 0 }, + + { ACMPF, C_FREG, C_REG, C_NONE, 57, 8, 0 }, + { ACMPF, C_FCON, C_REG, C_NONE, 57, 8, 0 }, + + { ACMPD, C_FREG, C_REG, C_NONE, 57, 8, 0 }, + { ACMPD, C_FCON, C_REG, C_NONE, 57, 8, 0 }, + + { AMOVFW, C_FREG, C_NONE, C_REG, 58, 8, 0 }, + { AMOVWF, C_REG, C_NONE, C_FREG, 58, 8, 0 }, + { AMOVWD, C_REG, C_NONE, C_FREG, 58, 8, 0 }, + { AMOVDW, C_FREG, C_NONE, C_REG, 58, 8, 0 }, + { AMOVFD, C_FREG, C_NONE, C_FREG, 58, 4, 0 }, + { AMOVDF, C_FREG, C_NONE, C_FREG, 58, 4, 0 }, + + { AMOVW, C_REG, C_NONE, C_FCR, 59, 4, 0 }, + { AMOVW, C_FCR, C_NONE, C_REG, 60, 4, 0 }, + + { ADIV, C_REG, C_NONE, C_REG, 62, 4, 0 }, + { ADIVU, C_REG, C_NONE, C_REG, 62, 4, 0 }, + { AMOD, C_REG, C_NONE, C_REG, 63, 10, 0 }, + { AMODU, C_REG, C_NONE, C_REG, 64, 10, 0 }, + { AXXX, C_NONE, C_NONE, C_NONE, 0, 2, 0 }, }; -#define OPCNTSZ 52 +#define OPCNTSZ 65 int opcount[OPCNTSZ]; // is this too pessimistic ? @@ -642,6 +722,17 @@ thumbbuildop() } } +/* Map chipfloat indices to high and low immediate constant values for VMOV */ +static int thumbfloatmap[] = { + 0x00000007, /* 1 = 2**0 * 1 */ + 0x00000000, /* 2 = 2**1 * 1 */ + 0x00080000, /* 3 = 2**1 * 1.5 */ + 0x00000001, /* 4 = 2**2 * 1 */ + 0x00040001, /* 5 = 2**2 * 1.25 */ + 0x00000006, /* 0.5 = 2**-1 * 1 */ + 0x00040002, /* 10 = 2**3 * 1.25 */ +}; + void thumbasmout(Prog *p, Optab *o) { @@ -1125,6 +1216,337 @@ if(debug['G']) print("%ulx: %s: thumb\n", (ulong)(p->pc), p->from.sym->name); o1 = mvlh(REGPC, REGLINK); // mov pc, lr o2 = mvlh(rt, REGPC); // mov r, pc break; + + case 52: /* floating point store */ + v = regoff(&p->to); + r = p->to.reg; + if(r == NREG) + r = o->param; + o1 = thumbofsr(p->as, p->from.reg, v, r, p); + if (p->as == AMOVD) + o1 |= 1 << 24; + SPLIT_INS(o1, o2); + break; + + case 53: /* floating point load */ + v = regoff(&p->from); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 = thumbofsr(p->as, p->to.reg, v, r, p) | (1<<4); + SPLIT_INS(o1, o2); + break; + + case 54: /* floating point store, long offset UGLY */ + /* Load an address or offset from a PC-relative address */ + o1 = thumbomvl(p, &p->to, REGTMPT); + if(!o1) + break; + r = p->to.reg; + if(r == NREG) + r = o->param; + if (o->param == REGSB) { + /* Store directly to the address */ + o2 = thumbofsr(p->as, p->from.reg, 0, REGTMPT, p); + SPLIT_INS(o2, o3) + } else { + /* Add the offset to the stack pointer */ + /* ADD (ARM ARM Thumb-2 Supplement, 4.6.4), T2 */ + o2 = 0x4400 | ((REGTMPT & 0x8) << 4) | (r << 3) | (REGTMPT & 0x7); + /* Store to the calculated address */ + o3 = thumbofsr(p->as, p->from.reg, 0, REGTMPT, p); + SPLIT_INS(o3, o4) + } + break; + + case 55: /* floating point load, long offset UGLY */ + /* Load an address or offset from a PC-relative address */ + o1 = thumbomvl(p, &p->from, REGTMPT); + if(!o1) + break; + r = p->from.reg; + if(r == NREG) + r = o->param; + if (o->param == REGSB) { + /* Load directly from the address */ + o2 = thumbofsr(p->as, p->to.reg, 0, REGTMPT, p) | (1<<4); + SPLIT_INS(o2, o3) + } else { + /* Add the offset to the stack pointer */ + /* ADD (ARM ARM Thumb-2 Supplement, 4.6.4), T2 */ + o2 = 0x4400 | ((REGTMPT & 0x8) << 4) | (r << 3) | (REGTMPT & 0x7); + /* Load from the calculated address */ + o3 = thumbofsr(p->as, p->to.reg, 0, REGTMPT, p) | (1<<4); + SPLIT_INS(o3, o4) + } + break; + + case 56: /* floating point arith */ + o1 = thumbopfp(p->as, p->scond); + if(p->from.type == D_FCONST) { + rf = chipfloat(p->from.ieee); + if(rf < 0){ + diag("invalid floating-point immediate\n%P", p); + rf = 0; + } + } else + rf = p->from.reg; + + rt = p->to.reg; + r = p->reg; + if(r == NREG) + r = rt; + + switch (p->as) { + case AADDF: + case ADIVF: + case AMULF: + case ASUBF: +// o1 |= ((r >> 1) & 0xf) | ((r & 1) << 23) | /* Vn */ +// (((rf >> 1) & 0xf)<<16) | ((rf & 1) << 21) | /* Vm */ +// (((rt >> 1) & 0xf)<<28) | ((rt & 1) << 6); /* Vd */ +// break; + case AADDD: + case ADIVD: + case AMULD: + case ASUBD: // assume that NFREG < 16 + o1 |= (r & 0xf) | /* Vn */ + ((rf & 0xf)<<16) | /* Vm */ + ((rt & 0xf)<<28); /* Vd */ + break; + case AMOVF: + case AMOVD: /* TODO: actually use double precision */ + if(p->from.type == D_FCONST) { + if (rf == 0) { + /* Float constant was zero */ + /* VSUB rt, rt, rt (ARMv7-M ARM, A7.7.257) */ + o1 ^= 1 << 7; + o1 |= (rt & 0xf); /* Vn */ + o1 |= ((rt & 0xf)<<16); /* Vm */ + } else { + /* VMOV immediate (ARMv7-M ARM, A7.7.236) */ + o1 ^= 1 << 22; + o1 |= thumbfloatmap[rf - 1]; + } + } else { + /* VMOV register (ARMv7-M ARM, A7.7.237) */ + o1 |= ((rf & 0xf)<<16); /* Vm */ + } + o1 |= ((rt & 0xf)<<28); /* Vd */ + if (p->as == AMOVD) + o1 |= 1 << 24; + break; + default: + print("%A %d %d %d\n", p->as, rf, r, rt); + diag("not implemented: %A", p->as); + break; + } + SPLIT_INS(o1, o2); + break; + + case 57: /* floating point compare */ + o1 = thumbopfp(p->as, p->scond); + rf = p->from.reg; + rt = p->reg; + + if(p->from.type == D_FCONST) { + if(p->from.ieee->h != 0 || p->from.ieee->l != 0) + diag("invalid floating-point immediate\n%P", p); + // VCMP (ARMv7-M ARM, A7.7.223, T2) compare to 0.0 + o1 |= 1; + rf = 0; + } + + // VCMP (ARMv7-M ARM, A7.7.223, T1) + o1 |= ((rf & 0x0f)<<16) | ((rt & 0x0f)<<28); /* Vm, Vd */ + + /* VMRS (ARMv7-M ARM, A7.7.243) with Rt=15 (for flags) */ + o3 = 0x0a10eef1 | (15 << 28); + + SPLIT_INS(o1, o2); + SPLIT_INS(o3, o4); + break; + + case 58: /* floating point fix and float (MOVDW/MOVWD) */ + o1 = thumbopfp(p->as, p->scond); + rf = p->from.reg; + rt = p->to.reg; + + if (p->as == AMOVDF) { + // Vd:D M:Vm - only support 16 registers + o1 |= ((rt & 1) << 6) | ((rt & 0xe) << 27) | ((rf & 0x0f) << 16); + SPLIT_INS(o1, o2); + } else if (p->as == AMOVFD) { + // MOVFD: D:Vd Vm:M - only support 16 registers + o1 |= ((rt & 0x0f)<<28) | ((rf & 1) << 21) | ((rf & 0x0e) << 16); + SPLIT_INS(o1, o2); + } else { + if (p->from.type == D_REG) { // int -> float + // VCVT (A7.7.225) + o3 = o1 | (rt << 16); // Vm:M (*2 to align with doubles) + if (p->as == AMOVWD) + o3 |= rt << 28; // D:Vd + else + o3 |= ((rt >> 1) << 28) | ((rt & 1) << 6); // Vd:D + // VMOV F,R (A7.7.240) + o1 = 0x0a10ee00; + o1 |= rt | rf<<28; // rt = Vn:N (*2 to align with doubles) + } else { // float -> int + // VCVT (A7.7.225) rf -> FREGTMP + o1 |= FREGTMP<<28; // Vd:D + if (p->as == AMOVDW) + o1 |= rf << 16; // M:Vm + else + o1 |= ((rf & 1) << 21) | ((rf >> 1) << 16); // Vm:M + // VMOV R,F (A7.7.240) FREFTMP -> rt + o3 = 0x0a10ee10; + o3 |= FREGTMP | rt<<28; + } + SPLIT_INS(o1, o2); + SPLIT_INS(o3, o4); + } + break; + + case 59: /* move to FP[CS]R */ + diag("59", p); + o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); + o1 |= ((p->to.reg+1)<<21) | (p->from.reg << 12); + break; + + case 60: /* move from FP[CS]R */ + diag("60", p); + o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); + o1 |= ((p->from.reg+1)<<21) | (p->to.reg<<12) | (1<<20); + break; + + case 61: /* op $c, r */ + rt = p->to.reg; + if (p->reg == NREG) + r = rt; + else + r = p->reg; + + thumbaclass(&p->from, p); + + switch (p->as) { + case AAND: + o1 = 0x0000f000; + break; + case AORR: + o1 = 0x0000f040; + break; + case ARSB: + o1 = 0x0000f1c0; + break; + default: + print("%A %d %d %d\n", p->as, instoffset, p->reg, p->to.reg); + diag("not implemented: %A", p->as); + break; + } + + if (p->scond & C_SBIT) + o1 |= (1 << 4); + + /* Only certain ranges of constants are supported. */ + if ((instoffset & 0xff) == instoffset) { + o1 |= r | (rt<<24) | (instoffset << 16); + } else if (((instoffset & 0xff00ff) == instoffset) && + ((instoffset & 0xff) == ((instoffset >> 16) & 0xff))) { + o1 |= r | (rt<<24) | ((instoffset & 0xff) << 16) | (1 << 28); + } else if (((instoffset & 0xff00ff00) == instoffset) && + (((instoffset >> 8) & 0xff) == ((instoffset >> 24) & 0xff))) { + o1 |= r | (rt<<24) | ((instoffset & 0xff) << 16) | (2 << 28); + } else if (((instoffset & 0xff) == ((instoffset >> 8) & 0xff)) && + ((instoffset & 0xff) == ((instoffset >> 16) & 0xff)) && + ((instoffset & 0xff) == ((instoffset >> 24) & 0xff))) { + o1 |= r | (rt<<24) | ((instoffset & 0xff) << 16) | (3 << 28); + } else { + print("%A %d %d %d\n", p->as, instoffset, p->reg, p->to.reg); + diag("constant not supported: %ux", instoffset); + } + SPLIT_INS(o1, o2); + break; + + case 62: /* div(u) */ + rf = p->from.reg; + rt = p->to.reg; + if (p->reg == NREG) + r = rt; + else + r = p->reg; + + switch (p->as) { + case ADIV: + /* SDIV (4.6.126, ARM Architecture Reference Manual Thumb-2 Supplement) */ + o1 = 0xf0f0fb90; + break; + case ADIVU: + /* UDIV (4.6.198, ARM Architecture Reference Manual Thumb-2 Supplement) */ + o1 = 0xf0f0fbb0; + break; + default: + print("%A %d %d %d\n", p->as, rf, r, rt); + diag("not implemented: %A", p->as); + break; + } + + o1 |= r | (rf<<16) | (rt<<24); + SPLIT_INS(o1, o2); + break; + + case 63: /* mod */ + rf = p->from.reg; + rt = p->to.reg; + lowreg(p, rf); + lowreg(p, rt); + if (p->reg == NREG) + r = rt; + else + r = p->reg; + + /* rf is Rm in the manual, which is the second operand */ + + /* From ARM Architecture Reference Manual Thumb-2 Supplement, A-16: + x MOD y = x - y * (x DIV y) */ + /* TMP = N SDIV D or TMP = r / rf */ + o1 = 0xf0f0fb90 | (REGTMPT<<24) | r | (rf<<16); + /* MUL (4.6.84, ARM Architecture Reference Manual Thumb-2 Supplement) */ + /* TMP = D * (N DIV D) or TMP = TMP * rf */ + o3 = 0xf000fb00 | (REGTMPT<<24) | (rf<<16) | REGTMPT; + /* SUB (4.6.177, ARM Architecture Reference Manual Thumb-2 Supplement) */ + /* Q = N - D * (N DIV D) */ + o5 = 0x1a00 | (REGTMPT<<6) | (r<<3) | rt; + + SPLIT_INS(o1, o2); + SPLIT_INS(o3, o4); + break; + + case 64: /* modu */ + rf = p->from.reg; + rt = p->to.reg; + lowreg(p, rf); + lowreg(p, rt); + if (p->reg == NREG) + r = rt; + else + r = p->reg; + + /* rf is Rm in the manual, which is the second operand */ + + /* From ARM Architecture Reference Manual Thumb-2 Supplement, A-16: + x MOD y = x - y * (x DIV y) */ + /* TMP = N UDIV D or TMP = r / rf */ + o1 = 0xf0f0fbb0 | (REGTMPT<<24) | r | (rf<<16); + /* MUL (4.6.84, ARM Architecture Reference Manual Thumb-2 Supplement) */ + /* TMP = D * (N DIV D) or TMP = TMP * rf */ + o3 = 0xf000fb00 | (REGTMPT<<24) | (rf<<16) | REGTMPT; + /* SUB (4.6.177, ARM Architecture Reference Manual Thumb-2 Supplement) */ + /* Q = N - D * (N DIV D) */ + o5 = 0x1a00 | (REGTMPT<<6) | (r<<3) | rt; + + SPLIT_INS(o1, o2); + SPLIT_INS(o3, o4); + break; } v = p->pc; @@ -1277,6 +1699,92 @@ thumboprrr(int a, int ld) return 0; } +long +thumbopfp(int a, int sc) +{ + /* See opvfprrr in 5l/asm.c for hints. */ + long o = 0; + + o |= 0xee00; // Prepare the first half-word in the sequence. + + // VCVT opc2 bits are to_int,0,signed + + switch(a) { + case AMOVWF: + /* VCVT (ARMv7-M ARM, A7.7.225), encoding T1, op=1, sz=0, opc2=b_000 */ + return o | (0x0a << 24) | (0xc0 << 16) | 0xb8; + case AMOVFW: + /* VCVT (ARMv7-M ARM, A7.7.225), encoding T1, op=1, sz=0, opc2=b_101 */ + return o | (0x0a << 24) | (0xc0 << 16) | 0xbd; + case AMOVWD: + /* VCVT (ARMv7-M ARM, A7.7.225), encoding T1, op=1, sz=1, opc2=b_000 */ + return o | (0x0b << 24) | (0xc0 << 16) | 0xb8; + case AMOVDW: + /* VCVT (ARMv7-M ARM, A7.7.225), encoding T1, op=1, sz=1, opc2=b_101 */ + return o | (0x0b << 24) | (0xc0 << 16) | 0xbd; + /* VCVT (ARMv7-M ARM, A7.7.227), encoding T1 */ + case AMOVFD: return o | (0x0a<<24) | (0xc0<<16) | 0xb7; + case AMOVDF: return o | (0x0b<<24) | (0xc0<<16) | 0xb7; + /* VMOV (register) (ARMv7-M ARM, A7.7.237) */ + case AMOVF: return o | (0x0a<<24) | (0x40<<16) | 0xb0; + case AMOVD: return o | (0x0b<<24) | (0x40<<16) | 0xb0; + /* VCMP (ARMv7-M ARM, A7.7.223), encoding T1 */ + case ACMPF: return o | (0x0a<<24) | (0x40<<16) | 0xb4; + case ACMPD: return o | (0x0b<<24) | (0x40<<16) | 0xb4; + /* arguably, ACMPF should expand to RNDF, CMPD */ + /* VADD (ARMv7-M ARM, A7.7.222) */ + case AADDD: return o | (0x0b<<24) | 0x30; + case AADDF: return o | (0x0a<<24) | 0x30; + /* VSUB (ARMv7-M ARM, A7.7.257) */ + case ASUBD: return o | (0x0b<<24) | (0x4<<20) | 0x30; + case ASUBF: return o | (0x0a<<24) | (0x4<<20) | 0x30; + /* VMUL (ARMv7-M ARM, A7.7.245) */ + case AMULD: return o | (0x0b<<24) | 0x20; + case AMULF: return o | (0x0a<<24) | 0x20; + /* VDIV (ARMv7-M ARM, A7.7.229) */ + case ADIVD: return o | (0x0b<<24) | 0x80; + case ADIVF: return o | (0x0a<<24) | 0x80; + } + diag("bad fp %d", a); + prasm(curp); + return 0; +} + +static long +thumbofsr(int a, int r, long v, int b, Prog *p) +{ + long o; + + /* VSTR (ARMv7-M ARM, A7.7.256), encoding T2 */ + o = 0x0a00ed00 | (1 << 7); /* Set U (add offset) by default */ + if(v < 0) { + v = -v; + o ^= 1 << 7; /* clear U if offset is negative */ + } + if(v & 3) + diag("odd offset for floating point op: %d\n%P", v, p); + else if(v >= (1<<10)) + diag("literal span too large: %d\n%P", v, p); + o |= ((v>>2) & 0xFF) << 16; /* offset */ + o |= b; /* Rn */ + o |= ((r & 0x0f) << 28); /* Vd */ + + switch(a) { + default: + diag("bad fst %A", a); + case AMOVD: + /* Make the encoding T1 instead of T2 for double precision */ + o |= 1 << 24; + break; + case AMOVF: + break; + } + + /* The caller will OR the result with 1 << 4 to create a load, if required */ + /* VLDR (ARMv7-M ARM, A7.7.233), encoding T2 */ + return o; +} + static long thumbopri(int a) { @@ -1348,6 +1856,29 @@ thumbopmv(int a, int ld) return 0; } +static long +thumbomvl(Prog *p, Adr *a, int dr) +{ + long v, o1; + if(!p->cond) { + thumbaclass(a, p); + v = immrot(~instoffset); + if(v == 0) { + diag("missing literal"); + prasm(p); + return 0; + } + print("omvl: %d\n", dr); + o1 = thumboprrr(AMVN, dr); + o1 |= v; + } else { + v = p->cond->pc - p->pc - 4; + /* A PC-relative load into dr */ + o1 = mv(p, dr, v); + } + return o1; +} + static void lowreg(Prog *p, int r) { |
