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_loadn
  17. emit_branch
  18. emit_call
  19. emit_movrr
  20. emit_spload
  21. emit_spstore
  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, int32_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_loadn(ASMState *as, Reg r, cTValue *tv)
 223 {
 224   int32_t i;
 225   if ((as->flags & JIT_F_VFPV3) && !tv->u32.lo) {
 226     uint32_t hi = tv->u32.hi;
 227     uint32_t b = ((hi >> 22) & 0x1ff);
 228     if (!(hi & 0xffff) && (b == 0x100 || b == 0x0ff)) {
 229       *--as->mcp = ARMI_VMOVI_D | ARMF_D(r & 15) |
 230                    ((tv->u32.hi >> 12) & 0x00080000) |
 231                    ((tv->u32.hi >> 4) & 0x00070000) |
 232                    ((tv->u32.hi >> 16) & 0x0000000f);
 233       return;
 234     }
 235   }
 236   i = i32ptr(tv);
 237   emit_vlso(as, ARMI_VLDR_D, r,
 238             ra_allock(as, (i & ~1020), RSET_GPR), (i & 1020));
 239 }
 240 #endif
 241 
 242 /* Get/set global_State fields. */
 243 #define emit_getgl(as, r, field) \
 244   emit_lsptr(as, ARMI_LDR, (r), (void *)&J2G(as->J)->field)
 245 #define emit_setgl(as, r, field) \
 246   emit_lsptr(as, ARMI_STR, (r), (void *)&J2G(as->J)->field)
 247 
 248 /* Trace number is determined from pc of exit instruction. */
 249 #define emit_setvmstate(as, i)          UNUSED(i)
 250 
 251 /* -- Emit control-flow instructions -------------------------------------- */
 252 
 253 /* Label for internal jumps. */
 254 typedef MCode *MCLabel;
 255 
 256 /* Return label pointing to current PC. */
 257 #define emit_label(as)          ((as)->mcp)
 258 
 259 static void emit_branch(ASMState *as, ARMIns ai, MCode *target)
 260 {
 261   MCode *p = as->mcp;
 262   ptrdiff_t delta = (target - p) - 1;
 263   lua_assert(((delta + 0x00800000) >> 24) == 0);
 264   *--p = ai | ((uint32_t)delta & 0x00ffffffu);
 265   as->mcp = p;
 266 }
 267 
 268 #define emit_jmp(as, target) emit_branch(as, ARMI_B, (target))
 269 
 270 static void emit_call(ASMState *as, void *target)
 271 {
 272   MCode *p = --as->mcp;
 273   ptrdiff_t delta = ((char *)target - (char *)p) - 8;
 274   if ((((delta>>2) + 0x00800000) >> 24) == 0) {
 275     if ((delta & 1))
 276       *p = ARMI_BLX | ((uint32_t)(delta>>2) & 0x00ffffffu) | ((delta&2) << 23);
 277     else
 278       *p = ARMI_BL | ((uint32_t)(delta>>2) & 0x00ffffffu);
 279   } else {  /* Target out of range: need indirect call. But don't use R0-R3. */
 280     Reg r = ra_allock(as, i32ptr(target), RSET_RANGE(RID_R4, RID_R12+1));
 281     *p = ARMI_BLXr | ARMF_M(r);
 282   }
 283 }
 284 
 285 /* -- Emit generic operations --------------------------------------------- */
 286 
 287 /* Generic move between two regs. */
 288 static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src)
 289 {
 290 #if LJ_SOFTFP
 291   lua_assert(!irt_isnum(ir->t)); UNUSED(ir);
 292 #else
 293   if (dst >= RID_MAX_GPR) {
 294     emit_dm(as, irt_isnum(ir->t) ? ARMI_VMOV_D : ARMI_VMOV_S,
 295             (dst & 15), (src & 15));
 296     return;
 297   }
 298 #endif
 299   if (as->mcp != as->mcloop) {  /* Swap early registers for loads/stores. */
 300     MCode ins = *as->mcp, swp = (src^dst);
 301     if ((ins & 0x0c000000) == 0x04000000 && (ins & 0x02000010) != 0x02000010) {
 302       if (!((ins ^ (dst << 16)) & 0x000f0000))
 303         *as->mcp = ins ^ (swp << 16);  /* Swap N in load/store. */
 304       if (!(ins & 0x00100000) && !((ins ^ (dst << 12)) & 0x0000f000))
 305         *as->mcp = ins ^ (swp << 12);  /* Swap D in store. */
 306     }
 307   }
 308   emit_dm(as, ARMI_MOV, dst, src);
 309 }
 310 
 311 /* Generic load of register from stack slot. */
 312 static void emit_spload(ASMState *as, IRIns *ir, Reg r, int32_t ofs)
 313 {
 314 #if LJ_SOFTFP
 315   lua_assert(!irt_isnum(ir->t)); UNUSED(ir);
 316 #else
 317   if (r >= RID_MAX_GPR)
 318     emit_vlso(as, irt_isnum(ir->t) ? ARMI_VLDR_D : ARMI_VLDR_S, r, RID_SP, ofs);
 319   else
 320 #endif
 321     emit_lso(as, ARMI_LDR, r, RID_SP, ofs);
 322 }
 323 
 324 /* Generic store of register to stack slot. */
 325 static void emit_spstore(ASMState *as, IRIns *ir, Reg r, int32_t ofs)
 326 {
 327 #if LJ_SOFTFP
 328   lua_assert(!irt_isnum(ir->t)); UNUSED(ir);
 329 #else
 330   if (r >= RID_MAX_GPR)
 331     emit_vlso(as, irt_isnum(ir->t) ? ARMI_VSTR_D : ARMI_VSTR_S, r, RID_SP, ofs);
 332   else
 333 #endif
 334     emit_lso(as, ARMI_STR, r, RID_SP, ofs);
 335 }
 336 
 337 /* Emit an arithmetic/logic operation with a constant operand. */
 338 static void emit_opk(ASMState *as, ARMIns ai, Reg dest, Reg src,
 339                      int32_t i, RegSet allow)
 340 {
 341   uint32_t k = emit_isk12(ai, i);
 342   if (k)
 343     emit_dn(as, ai^k, dest, src);
 344   else
 345     emit_dnm(as, ai, dest, src, ra_allock(as, i, allow));
 346 }
 347 
 348 /* Add offset to pointer. */
 349 static void emit_addptr(ASMState *as, Reg r, int32_t ofs)
 350 {
 351   if (ofs)
 352     emit_opk(as, ARMI_ADD, r, r, ofs, rset_exclude(RSET_GPR, r));
 353 }
 354 
 355 #define emit_spsub(as, ofs)     emit_addptr(as, RID_SP, -(ofs))
 356 

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