root/lj_emit_arm.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. emit_isk12
  2. emit_dnm
  3. emit_dm
  4. emit_dn
  5. emit_nm
  6. emit_d
  7. emit_n
  8. emit_m
  9. emit_lsox
  10. emit_lso
  11. emit_vlso
  12. emit_kdelta1
  13. emit_kdelta2
  14. emit_loadi
  15. emit_lsptr
  16. emit_loadk64
  17. emit_branch
  18. emit_call
  19. emit_movrr
  20. emit_loadofs
  21. emit_storeofs
  22. emit_opk
  23. emit_addptr

   1 /*
   2 ** ARM instruction emitter.
   3 ** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h
   4 */
   5 
   6 /* -- Constant encoding --------------------------------------------------- */
   7 
   8 static uint8_t emit_invai[16] = {
   9   /* AND */ (ARMI_AND^ARMI_BIC) >> 21,
  10   /* EOR */ 0,
  11   /* SUB */ (ARMI_SUB^ARMI_ADD) >> 21,
  12   /* RSB */ 0,
  13   /* ADD */ (ARMI_ADD^ARMI_SUB) >> 21,
  14   /* ADC */ (ARMI_ADC^ARMI_SBC) >> 21,
  15   /* SBC */ (ARMI_SBC^ARMI_ADC) >> 21,
  16   /* RSC */ 0,
  17   /* TST */ 0,
  18   /* TEQ */ 0,
  19   /* CMP */ (ARMI_CMP^ARMI_CMN) >> 21,
  20   /* CMN */ (ARMI_CMN^ARMI_CMP) >> 21,
  21   /* ORR */ 0,
  22   /* MOV */ (ARMI_MOV^ARMI_MVN) >> 21,
  23   /* BIC */ (ARMI_BIC^ARMI_AND) >> 21,
  24   /* MVN */ (ARMI_MVN^ARMI_MOV) >> 21
  25 };
  26 
  27 /* Encode constant in K12 format for data processing instructions. */
  28 static uint32_t emit_isk12(ARMIns ai, int32_t n)
  29 {
  30   uint32_t invai, i, m = (uint32_t)n;
  31   /* K12: unsigned 8 bit value, rotated in steps of two bits. */
  32   for (i = 0; i < 4096; i += 256, m = lj_rol(m, 2))
  33     if (m <= 255) return ARMI_K12|m|i;
  34   /* Otherwise try negation/complement with the inverse instruction. */
  35   invai = emit_invai[((ai >> 21) & 15)];
  36   if (!invai) return 0;  /* Failed. No inverse instruction. */
  37   m = ~(uint32_t)n;
  38   if (invai == ((ARMI_SUB^ARMI_ADD) >> 21) ||
  39       invai == (ARMI_CMP^ARMI_CMN) >> 21) m++;
  40   for (i = 0; i < 4096; i += 256, m = lj_rol(m, 2))
  41     if (m <= 255) return ARMI_K12|(invai<<21)|m|i;
  42   return 0;  /* Failed. */
  43 }
  44 
  45 /* -- Emit basic instructions --------------------------------------------- */
  46 
  47 static void emit_dnm(ASMState *as, ARMIns ai, Reg rd, Reg rn, Reg rm)
  48 {
  49   *--as->mcp = ai | ARMF_D(rd) | ARMF_N(rn) | ARMF_M(rm);
  50 }
  51 
  52 static void emit_dm(ASMState *as, ARMIns ai, Reg rd, Reg rm)
  53 {
  54   *--as->mcp = ai | ARMF_D(rd) | ARMF_M(rm);
  55 }
  56 
  57 static void emit_dn(ASMState *as, ARMIns ai, Reg rd, Reg rn)
  58 {
  59   *--as->mcp = ai | ARMF_D(rd) | ARMF_N(rn);
  60 }
  61 
  62 static void emit_nm(ASMState *as, ARMIns ai, Reg rn, Reg rm)
  63 {
  64   *--as->mcp = ai | ARMF_N(rn) | ARMF_M(rm);
  65 }
  66 
  67 static void emit_d(ASMState *as, ARMIns ai, Reg rd)
  68 {
  69   *--as->mcp = ai | ARMF_D(rd);
  70 }
  71 
  72 static void emit_n(ASMState *as, ARMIns ai, Reg rn)
  73 {
  74   *--as->mcp = ai | ARMF_N(rn);
  75 }
  76 
  77 static void emit_m(ASMState *as, ARMIns ai, Reg rm)
  78 {
  79   *--as->mcp = ai | ARMF_M(rm);
  80 }
  81 
  82 static void emit_lsox(ASMState *as, ARMIns ai, Reg rd, Reg rn, int32_t ofs)
  83 {
  84   lua_assert(ofs >= -255 && ofs <= 255);
  85   if (ofs < 0) ofs = -ofs; else ai |= ARMI_LS_U;
  86   *--as->mcp = ai | ARMI_LS_P | ARMI_LSX_I | ARMF_D(rd) | ARMF_N(rn) |
  87                ((ofs & 0xf0) << 4) | (ofs & 0x0f);
  88 }
  89 
  90 static void emit_lso(ASMState *as, ARMIns ai, Reg rd, Reg rn, int32_t ofs)
  91 {
  92   lua_assert(ofs >= -4095 && ofs <= 4095);
  93   /* Combine LDR/STR pairs to LDRD/STRD. */
  94   if (*as->mcp == (ai|ARMI_LS_P|ARMI_LS_U|ARMF_D(rd^1)|ARMF_N(rn)|(ofs^4)) &&
  95       (ai & ~(ARMI_LDR^ARMI_STR)) == ARMI_STR && rd != rn &&
  96       (uint32_t)ofs <= 252 && !(ofs & 3) && !((rd ^ (ofs >>2)) & 1) &&
  97       as->mcp != as->mcloop) {
  98     as->mcp++;
  99     emit_lsox(as, ai == ARMI_LDR ? ARMI_LDRD : ARMI_STRD, rd&~1, rn, ofs&~4);
 100     return;
 101   }
 102   if (ofs < 0) ofs = -ofs; else ai |= ARMI_LS_U;
 103   *--as->mcp = ai | ARMI_LS_P | ARMF_D(rd) | ARMF_N(rn) | ofs;
 104 }
 105 
 106 #if !LJ_SOFTFP
 107 static void emit_vlso(ASMState *as, ARMIns ai, Reg rd, Reg rn, int32_t ofs)
 108 {
 109   lua_assert(ofs >= -1020 && ofs <= 1020 && (ofs&3) == 0);
 110   if (ofs < 0) ofs = -ofs; else ai |= ARMI_LS_U;
 111   *--as->mcp = ai | ARMI_LS_P | ARMF_D(rd & 15) | ARMF_N(rn) | (ofs >> 2);
 112 }
 113 #endif
 114 
 115 /* -- Emit loads/stores --------------------------------------------------- */
 116 
 117 /* Prefer spills of BASE/L. */
 118 #define emit_canremat(ref)      ((ref) < ASMREF_L)
 119 
 120 /* Try to find a one step delta relative to another constant. */
 121 static int emit_kdelta1(ASMState *as, Reg d, int32_t i)
 122 {
 123   RegSet work = ~as->freeset & RSET_GPR;
 124   while (work) {
 125     Reg r = rset_picktop(work);
 126     IRRef ref = regcost_ref(as->cost[r]);
 127     lua_assert(r != d);
 128     if (emit_canremat(ref)) {
 129       int32_t delta = i - (ra_iskref(ref) ? ra_krefk(as, ref) : IR(ref)->i);
 130       uint32_t k = emit_isk12(ARMI_ADD, delta);
 131       if (k) {
 132         if (k == ARMI_K12)
 133           emit_dm(as, ARMI_MOV, d, r);
 134         else
 135           emit_dn(as, ARMI_ADD^k, d, r);
 136         return 1;
 137       }
 138     }
 139     rset_clear(work, r);
 140   }
 141   return 0;  /* Failed. */
 142 }
 143 
 144 /* Try to find a two step delta relative to another constant. */
 145 static int emit_kdelta2(ASMState *as, Reg d, int32_t i)
 146 {
 147   RegSet work = ~as->freeset & RSET_GPR;
 148   while (work) {
 149     Reg r = rset_picktop(work);
 150     IRRef ref = regcost_ref(as->cost[r]);
 151     lua_assert(r != d);
 152     if (emit_canremat(ref)) {
 153       int32_t other = ra_iskref(ref) ? ra_krefk(as, ref) : IR(ref)->i;
 154       if (other) {
 155         int32_t delta = i - other;
 156         uint32_t sh, inv = 0, k2, k;
 157         if (delta < 0) { delta = -delta; inv = ARMI_ADD^ARMI_SUB; }
 158         sh = lj_ffs(delta) & ~1;
 159         k2 = emit_isk12(0, delta & (255 << sh));
 160         k = emit_isk12(0, delta & ~(255 << sh));
 161         if (k) {
 162           emit_dn(as, ARMI_ADD^k2^inv, d, d);
 163           emit_dn(as, ARMI_ADD^k^inv, d, r);
 164           return 1;
 165         }
 166       }
 167     }
 168     rset_clear(work, r);
 169   }
 170   return 0;  /* Failed. */
 171 }
 172 
 173 /* Load a 32 bit constant into a GPR. */
 174 static void emit_loadi(ASMState *as, Reg r, int32_t i)
 175 {
 176   uint32_t k = emit_isk12(ARMI_MOV, i);
 177   lua_assert(rset_test(as->freeset, r) || r == RID_TMP);
 178   if (k) {
 179     /* Standard K12 constant. */
 180     emit_d(as, ARMI_MOV^k, r);
 181   } else if ((as->flags & JIT_F_ARMV6T2) && (uint32_t)i < 0x00010000u) {
 182     /* 16 bit loword constant for ARMv6T2. */
 183     emit_d(as, ARMI_MOVW|(i & 0x0fff)|((i & 0xf000)<<4), r);
 184   } else if (emit_kdelta1(as, r, i)) {
 185     /* One step delta relative to another constant. */
 186   } else if ((as->flags & JIT_F_ARMV6T2)) {
 187     /* 32 bit hiword/loword constant for ARMv6T2. */
 188     emit_d(as, ARMI_MOVT|((i>>16) & 0x0fff)|(((i>>16) & 0xf000)<<4), r);
 189     emit_d(as, ARMI_MOVW|(i & 0x0fff)|((i & 0xf000)<<4), r);
 190   } else if (emit_kdelta2(as, r, i)) {
 191     /* Two step delta relative to another constant. */
 192   } else {
 193     /* Otherwise construct the constant with up to 4 instructions. */
 194     /* NYI: use mvn+bic, use pc-relative loads. */
 195     for (;;) {
 196       uint32_t sh = lj_ffs(i) & ~1;
 197       int32_t m = i & (255 << sh);
 198       i &= ~(255 << sh);
 199       if (i == 0) {
 200         emit_d(as, ARMI_MOV ^ emit_isk12(0, m), r);
 201         break;
 202       }
 203       emit_dn(as, ARMI_ORR ^ emit_isk12(0, m), r, r);
 204     }
 205   }
 206 }
 207 
 208 #define emit_loada(as, r, addr)         emit_loadi(as, (r), i32ptr((addr)))
 209 
 210 static Reg ra_allock(ASMState *as, intptr_t k, RegSet allow);
 211 
 212 /* Get/set from constant pointer. */
 213 static void emit_lsptr(ASMState *as, ARMIns ai, Reg r, void *p)
 214 {
 215   int32_t i = i32ptr(p);
 216   emit_lso(as, ai, r, ra_allock(as, (i & ~4095), rset_exclude(RSET_GPR, r)),
 217            (i & 4095));
 218 }
 219 
 220 #if !LJ_SOFTFP
 221 /* Load a number constant into an FPR. */
 222 static void emit_loadk64(ASMState *as, Reg r, IRIns *ir)
 223 {
 224   cTValue *tv = ir_knum(ir);
 225   int32_t i;
 226   if ((as->flags & JIT_F_VFPV3) && !tv->u32.lo) {
 227     uint32_t hi = tv->u32.hi;
 228     uint32_t b = ((hi >> 22) & 0x1ff);
 229     if (!(hi & 0xffff) && (b == 0x100 || b == 0x0ff)) {
 230       *--as->mcp = ARMI_VMOVI_D | ARMF_D(r & 15) |
 231                    ((tv->u32.hi >> 12) & 0x00080000) |
 232                    ((tv->u32.hi >> 4) & 0x00070000) |
 233                    ((tv->u32.hi >> 16) & 0x0000000f);
 234       return;
 235     }
 236   }
 237   i = i32ptr(tv);
 238   emit_vlso(as, ARMI_VLDR_D, r,
 239             ra_allock(as, (i & ~1020), RSET_GPR), (i & 1020));
 240 }
 241 #endif
 242 
 243 /* Get/set global_State fields. */
 244 #define emit_getgl(as, r, field) \
 245   emit_lsptr(as, ARMI_LDR, (r), (void *)&J2G(as->J)->field)
 246 #define emit_setgl(as, r, field) \
 247   emit_lsptr(as, ARMI_STR, (r), (void *)&J2G(as->J)->field)
 248 
 249 /* Trace number is determined from pc of exit instruction. */
 250 #define emit_setvmstate(as, i)          UNUSED(i)
 251 
 252 /* -- Emit control-flow instructions -------------------------------------- */
 253 
 254 /* Label for internal jumps. */
 255 typedef MCode *MCLabel;
 256 
 257 /* Return label pointing to current PC. */
 258 #define emit_label(as)          ((as)->mcp)
 259 
 260 static void emit_branch(ASMState *as, ARMIns ai, MCode *target)
 261 {
 262   MCode *p = as->mcp;
 263   ptrdiff_t delta = (target - p) - 1;
 264   lua_assert(((delta + 0x00800000) >> 24) == 0);
 265   *--p = ai | ((uint32_t)delta & 0x00ffffffu);
 266   as->mcp = p;
 267 }
 268 
 269 #define emit_jmp(as, target) emit_branch(as, ARMI_B, (target))
 270 
 271 static void emit_call(ASMState *as, void *target)
 272 {
 273   MCode *p = --as->mcp;
 274   ptrdiff_t delta = ((char *)target - (char *)p) - 8;
 275   if ((((delta>>2) + 0x00800000) >> 24) == 0) {
 276     if ((delta & 1))
 277       *p = ARMI_BLX | ((uint32_t)(delta>>2) & 0x00ffffffu) | ((delta&2) << 23);
 278     else
 279       *p = ARMI_BL | ((uint32_t)(delta>>2) & 0x00ffffffu);
 280   } else {  /* Target out of range: need indirect call. But don't use R0-R3. */
 281     Reg r = ra_allock(as, i32ptr(target), RSET_RANGE(RID_R4, RID_R12+1));
 282     *p = ARMI_BLXr | ARMF_M(r);
 283   }
 284 }
 285 
 286 /* -- Emit generic operations --------------------------------------------- */
 287 
 288 /* Generic move between two regs. */
 289 static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src)
 290 {
 291 #if LJ_SOFTFP
 292   lua_assert(!irt_isnum(ir->t)); UNUSED(ir);
 293 #else
 294   if (dst >= RID_MAX_GPR) {
 295     emit_dm(as, irt_isnum(ir->t) ? ARMI_VMOV_D : ARMI_VMOV_S,
 296             (dst & 15), (src & 15));
 297     return;
 298   }
 299 #endif
 300   if (as->mcp != as->mcloop) {  /* Swap early registers for loads/stores. */
 301     MCode ins = *as->mcp, swp = (src^dst);
 302     if ((ins & 0x0c000000) == 0x04000000 && (ins & 0x02000010) != 0x02000010) {
 303       if (!((ins ^ (dst << 16)) & 0x000f0000))
 304         *as->mcp = ins ^ (swp << 16);  /* Swap N in load/store. */
 305       if (!(ins & 0x00100000) && !((ins ^ (dst << 12)) & 0x0000f000))
 306         *as->mcp = ins ^ (swp << 12);  /* Swap D in store. */
 307     }
 308   }
 309   emit_dm(as, ARMI_MOV, dst, src);
 310 }
 311 
 312 /* Generic load of register with base and (small) offset address. */
 313 static void emit_loadofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs)
 314 {
 315 #if LJ_SOFTFP
 316   lua_assert(!irt_isnum(ir->t)); UNUSED(ir);
 317 #else
 318   if (r >= RID_MAX_GPR)
 319     emit_vlso(as, irt_isnum(ir->t) ? ARMI_VLDR_D : ARMI_VLDR_S, r, base, ofs);
 320   else
 321 #endif
 322     emit_lso(as, ARMI_LDR, r, base, ofs);
 323 }
 324 
 325 /* Generic store of register with base and (small) offset address. */
 326 static void emit_storeofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs)
 327 {
 328 #if LJ_SOFTFP
 329   lua_assert(!irt_isnum(ir->t)); UNUSED(ir);
 330 #else
 331   if (r >= RID_MAX_GPR)
 332     emit_vlso(as, irt_isnum(ir->t) ? ARMI_VSTR_D : ARMI_VSTR_S, r, base, ofs);
 333   else
 334 #endif
 335     emit_lso(as, ARMI_STR, r, base, ofs);
 336 }
 337 
 338 /* Emit an arithmetic/logic operation with a constant operand. */
 339 static void emit_opk(ASMState *as, ARMIns ai, Reg dest, Reg src,
 340                      int32_t i, RegSet allow)
 341 {
 342   uint32_t k = emit_isk12(ai, i);
 343   if (k)
 344     emit_dn(as, ai^k, dest, src);
 345   else
 346     emit_dnm(as, ai, dest, src, ra_allock(as, i, allow));
 347 }
 348 
 349 /* Add offset to pointer. */
 350 static void emit_addptr(ASMState *as, Reg r, int32_t ofs)
 351 {
 352   if (ofs)
 353     emit_opk(as, ARMI_ADD, r, r, ofs, rset_exclude(RSET_GPR, r));
 354 }
 355 
 356 #define emit_spsub(as, ofs)     emit_addptr(as, RID_SP, -(ofs))
 357 

/* [<][>][^][v][top][bottom][index][help] */