tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MacroAssembler-vixl.cpp (67802B)


      1 // Copyright 2015, ARM Limited
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are met:
      6 //
      7 //   * Redistributions of source code must retain the above copyright notice,
      8 //     this list of conditions and the following disclaimer.
      9 //   * Redistributions in binary form must reproduce the above copyright notice,
     10 //     this list of conditions and the following disclaimer in the documentation
     11 //     and/or other materials provided with the distribution.
     12 //   * Neither the name of ARM Limited nor the names of its contributors may be
     13 //     used to endorse or promote products derived from this software without
     14 //     specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
     17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
     20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26 
     27 #include "jit/arm64/vixl/MacroAssembler-vixl.h"
     28 
     29 #include <ctype.h>
     30 #include <limits>
     31 
     32 #include "jit/RegisterSets.h"
     33 
     34 namespace vixl {
     35 
     36 MacroAssembler::MacroAssembler()
     37    : js::jit::Assembler(),
     38      sp_(x28),
     39      tmp_list_(ip0, ip1),
     40      fptmp_list_(d31)
     41 {
     42 }
     43 
     44 
     45 void MacroAssembler::FinalizeCode() {
     46  Assembler::FinalizeCode();
     47 }
     48 
     49 
     50 int MacroAssembler::MoveImmediateHelper(MacroAssembler* masm,
     51                                        const Register &rd,
     52                                        uint64_t imm) {
     53  bool emit_code = (masm != NULL);
     54  VIXL_ASSERT(IsUint32(imm) || IsInt32(imm) || rd.Is64Bits());
     55  // The worst case for size is mov 64-bit immediate to sp:
     56  //  * up to 4 instructions to materialise the constant
     57  //  * 1 instruction to move to sp
     58  MacroEmissionCheckScope guard(masm);
     59 
     60  // Immediates on Aarch64 can be produced using an initial value, and zero to
     61  // three move keep operations.
     62  //
     63  // Initial values can be generated with:
     64  //  1. 64-bit move zero (movz).
     65  //  2. 32-bit move inverted (movn).
     66  //  3. 64-bit move inverted.
     67  //  4. 32-bit orr immediate.
     68  //  5. 64-bit orr immediate.
     69  // Move-keep may then be used to modify each of the 16-bit half words.
     70  //
     71  // The code below supports all five initial value generators, and
     72  // applying move-keep operations to move-zero and move-inverted initial
     73  // values.
     74 
     75  // Try to move the immediate in one instruction, and if that fails, switch to
     76  // using multiple instructions.
     77  if (OneInstrMoveImmediateHelper(masm, rd, imm)) {
     78    return 1;
     79  } else {
     80    int instruction_count = 0;
     81    unsigned reg_size = rd.size();
     82 
     83    // Generic immediate case. Imm will be represented by
     84    //   [imm3, imm2, imm1, imm0], where each imm is 16 bits.
     85    // A move-zero or move-inverted is generated for the first non-zero or
     86    // non-0xffff immX, and a move-keep for subsequent non-zero immX.
     87 
     88    uint64_t ignored_halfword = 0;
     89    bool invert_move = false;
     90    // If the number of 0xffff halfwords is greater than the number of 0x0000
     91    // halfwords, it's more efficient to use move-inverted.
     92    if (CountClearHalfWords(~imm, reg_size) >
     93        CountClearHalfWords(imm, reg_size)) {
     94      ignored_halfword = 0xffff;
     95      invert_move = true;
     96    }
     97 
     98    // Mov instructions can't move values into the stack pointer, so set up a
     99    // temporary register, if needed.
    100    UseScratchRegisterScope temps;
    101    Register temp;
    102    if (emit_code) {
    103      temps.Open(masm);
    104      temp = rd.IsSP() ? temps.AcquireSameSizeAs(rd) : rd;
    105    }
    106 
    107    // Iterate through the halfwords. Use movn/movz for the first non-ignored
    108    // halfword, and movk for subsequent halfwords.
    109    VIXL_ASSERT((reg_size % 16) == 0);
    110    bool first_mov_done = false;
    111    for (unsigned i = 0; i < (temp.size() / 16); i++) {
    112      uint64_t imm16 = (imm >> (16 * i)) & 0xffff;
    113      if (imm16 != ignored_halfword) {
    114        if (!first_mov_done) {
    115          if (invert_move) {
    116            if (emit_code) masm->movn(temp, ~imm16 & 0xffff, 16 * i);
    117            instruction_count++;
    118          } else {
    119            if (emit_code) masm->movz(temp, imm16, 16 * i);
    120            instruction_count++;
    121          }
    122          first_mov_done = true;
    123        } else {
    124          // Construct a wider constant.
    125          if (emit_code) masm->movk(temp, imm16, 16 * i);
    126          instruction_count++;
    127        }
    128      }
    129    }
    130 
    131    VIXL_ASSERT(first_mov_done);
    132 
    133    // Move the temporary if the original destination register was the stack
    134    // pointer.
    135    if (rd.IsSP()) {
    136      if (emit_code) masm->mov(rd, temp);
    137      instruction_count++;
    138    }
    139    return instruction_count;
    140  }
    141 }
    142 
    143 
    144 bool MacroAssembler::OneInstrMoveImmediateHelper(MacroAssembler* masm,
    145                                                 const Register& dst,
    146                                                 int64_t imm) {
    147  bool emit_code = masm != NULL;
    148  unsigned n, imm_s, imm_r;
    149  int reg_size = dst.size();
    150 
    151  if (IsImmMovz(imm, reg_size) && !dst.IsSP()) {
    152    // Immediate can be represented in a move zero instruction. Movz can't write
    153    // to the stack pointer.
    154    if (emit_code) {
    155      masm->movz(dst, imm);
    156    }
    157    return true;
    158  } else if (IsImmMovn(imm, reg_size) && !dst.IsSP()) {
    159    // Immediate can be represented in a move negative instruction. Movn can't
    160    // write to the stack pointer.
    161    if (emit_code) {
    162      masm->movn(dst, dst.Is64Bits() ? ~imm : (~imm & kWRegMask));
    163    }
    164    return true;
    165  } else if (IsImmLogical(imm, reg_size, &n, &imm_s, &imm_r)) {
    166    // Immediate can be represented in a logical orr instruction.
    167    VIXL_ASSERT(!dst.IsZero());
    168    if (emit_code) {
    169      masm->LogicalImmediate(
    170          dst, AppropriateZeroRegFor(dst), n, imm_s, imm_r, ORR);
    171    }
    172    return true;
    173  }
    174  return false;
    175 }
    176 
    177 
    178 void MacroAssembler::B(Label* label, BranchType type, Register reg, int bit) {
    179  VIXL_ASSERT((reg.Is(NoReg) || (type >= kBranchTypeFirstUsingReg)) &&
    180              ((bit == -1) || (type >= kBranchTypeFirstUsingBit)));
    181  if (kBranchTypeFirstCondition <= type && type <= kBranchTypeLastCondition) {
    182    B(static_cast<Condition>(type), label);
    183  } else {
    184    switch (type) {
    185      case always:        B(label);              break;
    186      case never:         break;
    187      case reg_zero:      Cbz(reg, label);       break;
    188      case reg_not_zero:  Cbnz(reg, label);      break;
    189      case reg_bit_clear: Tbz(reg, bit, label);  break;
    190      case reg_bit_set:   Tbnz(reg, bit, label); break;
    191      default:
    192        VIXL_UNREACHABLE();
    193    }
    194  }
    195 }
    196 
    197 
    198 void MacroAssembler::B(Label* label) {
    199  SingleEmissionCheckScope guard(this);
    200  b(label);
    201 }
    202 
    203 
    204 void MacroAssembler::B(Label* label, Condition cond) {
    205  VIXL_ASSERT((cond != al) && (cond != nv));
    206  EmissionCheckScope guard(this, 2 * kInstructionSize);
    207 
    208  if (label->bound() && LabelIsOutOfRange(label, CondBranchType)) {
    209    Label done;
    210    b(&done, InvertCondition(cond));
    211    b(label);
    212    bind(&done);
    213  } else {
    214    b(label, cond);
    215  }
    216 }
    217 
    218 
    219 void MacroAssembler::Cbnz(const Register& rt, Label* label) {
    220  VIXL_ASSERT(!rt.IsZero());
    221  EmissionCheckScope guard(this, 2 * kInstructionSize);
    222 
    223  if (label->bound() && LabelIsOutOfRange(label, CondBranchType)) {
    224    Label done;
    225    cbz(rt, &done);
    226    b(label);
    227    bind(&done);
    228  } else {
    229    cbnz(rt, label);
    230  }
    231 }
    232 
    233 
    234 void MacroAssembler::Cbz(const Register& rt, Label* label) {
    235  VIXL_ASSERT(!rt.IsZero());
    236  EmissionCheckScope guard(this, 2 * kInstructionSize);
    237 
    238  if (label->bound() && LabelIsOutOfRange(label, CondBranchType)) {
    239    Label done;
    240    cbnz(rt, &done);
    241    b(label);
    242    bind(&done);
    243  } else {
    244    cbz(rt, label);
    245  }
    246 }
    247 
    248 
    249 void MacroAssembler::Tbnz(const Register& rt, unsigned bit_pos, Label* label) {
    250  VIXL_ASSERT(!rt.IsZero());
    251  EmissionCheckScope guard(this, 2 * kInstructionSize);
    252 
    253  if (label->bound() && LabelIsOutOfRange(label, TestBranchType)) {
    254    Label done;
    255    tbz(rt, bit_pos, &done);
    256    b(label);
    257    bind(&done);
    258  } else {
    259    tbnz(rt, bit_pos, label);
    260  }
    261 }
    262 
    263 
    264 void MacroAssembler::Tbz(const Register& rt, unsigned bit_pos, Label* label) {
    265  VIXL_ASSERT(!rt.IsZero());
    266  EmissionCheckScope guard(this, 2 * kInstructionSize);
    267 
    268  if (label->bound() && LabelIsOutOfRange(label, TestBranchType)) {
    269    Label done;
    270    tbnz(rt, bit_pos, &done);
    271    b(label);
    272    bind(&done);
    273  } else {
    274    tbz(rt, bit_pos, label);
    275  }
    276 }
    277 
    278 
    279 void MacroAssembler::And(const Register& rd,
    280                         const Register& rn,
    281                         const Operand& operand) {
    282  LogicalMacro(rd, rn, operand, AND);
    283 }
    284 
    285 
    286 void MacroAssembler::Ands(const Register& rd,
    287                          const Register& rn,
    288                          const Operand& operand) {
    289  LogicalMacro(rd, rn, operand, ANDS);
    290 }
    291 
    292 
    293 void MacroAssembler::Tst(const Register& rn,
    294                         const Operand& operand) {
    295  Ands(AppropriateZeroRegFor(rn), rn, operand);
    296 }
    297 
    298 
    299 void MacroAssembler::Bic(const Register& rd,
    300                         const Register& rn,
    301                         const Operand& operand) {
    302  LogicalMacro(rd, rn, operand, BIC);
    303 }
    304 
    305 
    306 void MacroAssembler::Bics(const Register& rd,
    307                          const Register& rn,
    308                          const Operand& operand) {
    309  LogicalMacro(rd, rn, operand, BICS);
    310 }
    311 
    312 
    313 void MacroAssembler::Orr(const Register& rd,
    314                         const Register& rn,
    315                         const Operand& operand) {
    316  LogicalMacro(rd, rn, operand, ORR);
    317 }
    318 
    319 
    320 void MacroAssembler::Orn(const Register& rd,
    321                         const Register& rn,
    322                         const Operand& operand) {
    323  LogicalMacro(rd, rn, operand, ORN);
    324 }
    325 
    326 
    327 void MacroAssembler::Eor(const Register& rd,
    328                         const Register& rn,
    329                         const Operand& operand) {
    330  LogicalMacro(rd, rn, operand, EOR);
    331 }
    332 
    333 
    334 void MacroAssembler::Eon(const Register& rd,
    335                         const Register& rn,
    336                         const Operand& operand) {
    337  LogicalMacro(rd, rn, operand, EON);
    338 }
    339 
    340 
    341 void MacroAssembler::LogicalMacro(const Register& rd,
    342                                  const Register& rn,
    343                                  const Operand& operand,
    344                                  LogicalOp op) {
    345  // The worst case for size is logical immediate to sp:
    346  //  * up to 4 instructions to materialise the constant
    347  //  * 1 instruction to do the operation
    348  //  * 1 instruction to move to sp
    349  MacroEmissionCheckScope guard(this);
    350  UseScratchRegisterScope temps(this);
    351 
    352  if (operand.IsImmediate()) {
    353    int64_t immediate = operand.immediate();
    354    unsigned reg_size = rd.size();
    355 
    356    // If the operation is NOT, invert the operation and immediate.
    357    if ((op & NOT) == NOT) {
    358      op = static_cast<LogicalOp>(op & ~NOT);
    359      immediate = ~immediate;
    360    }
    361 
    362    // Ignore the top 32 bits of an immediate if we're moving to a W register.
    363    if (rd.Is32Bits()) {
    364      // Check that the top 32 bits are consistent.
    365      VIXL_ASSERT(((immediate >> kWRegSize) == 0) ||
    366                  ((immediate >> kWRegSize) == -1));
    367      immediate &= kWRegMask;
    368    }
    369 
    370    VIXL_ASSERT(rd.Is64Bits() || IsUint32(immediate));
    371 
    372    // Special cases for all set or all clear immediates.
    373    if (immediate == 0) {
    374      switch (op) {
    375        case AND:
    376          Mov(rd, 0);
    377          return;
    378        case ORR:
    379          VIXL_FALLTHROUGH();
    380        case EOR:
    381          Mov(rd, rn);
    382          return;
    383        case ANDS:
    384          VIXL_FALLTHROUGH();
    385        case BICS:
    386          break;
    387        default:
    388          VIXL_UNREACHABLE();
    389      }
    390    } else if ((rd.Is64Bits() && (immediate == -1)) ||
    391               (rd.Is32Bits() && (immediate == 0xffffffff))) {
    392      switch (op) {
    393        case AND:
    394          Mov(rd, rn);
    395          return;
    396        case ORR:
    397          Mov(rd, immediate);
    398          return;
    399        case EOR:
    400          Mvn(rd, rn);
    401          return;
    402        case ANDS:
    403          VIXL_FALLTHROUGH();
    404        case BICS:
    405          break;
    406        default:
    407          VIXL_UNREACHABLE();
    408      }
    409    }
    410 
    411    unsigned n, imm_s, imm_r;
    412    if (IsImmLogical(immediate, reg_size, &n, &imm_s, &imm_r)) {
    413      // Immediate can be encoded in the instruction.
    414      LogicalImmediate(rd, rn, n, imm_s, imm_r, op);
    415    } else {
    416      // Immediate can't be encoded: synthesize using move immediate.
    417      Register temp = temps.AcquireSameSizeAs(rn);
    418 
    419      // If the left-hand input is the stack pointer, we can't pre-shift the
    420      // immediate, as the encoding won't allow the subsequent post shift.
    421      PreShiftImmMode mode = rn.IsSP() ? kNoShift : kAnyShift;
    422      Operand imm_operand = MoveImmediateForShiftedOp(temp, immediate, mode);
    423 
    424      // VIXL can acquire temp registers. Assert that the caller is aware.
    425      VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn));
    426      VIXL_ASSERT(!temp.Is(operand.maybeReg()));
    427 
    428      if (rd.Is(sp)) {
    429        // If rd is the stack pointer we cannot use it as the destination
    430        // register so we use the temp register as an intermediate again.
    431        Logical(temp, rn, imm_operand, op);
    432        Mov(sp, temp);
    433      } else {
    434        Logical(rd, rn, imm_operand, op);
    435      }
    436    }
    437  } else if (operand.IsExtendedRegister()) {
    438    VIXL_ASSERT(operand.reg().size() <= rd.size());
    439    // Add/sub extended supports shift <= 4. We want to support exactly the
    440    // same modes here.
    441    VIXL_ASSERT(operand.shift_amount() <= 4);
    442    VIXL_ASSERT(operand.reg().Is64Bits() ||
    443           ((operand.extend() != UXTX) && (operand.extend() != SXTX)));
    444 
    445    temps.Exclude(operand.reg());
    446    Register temp = temps.AcquireSameSizeAs(rn);
    447 
    448    // VIXL can acquire temp registers. Assert that the caller is aware.
    449    VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn));
    450    VIXL_ASSERT(!temp.Is(operand.maybeReg()));
    451 
    452    EmitExtendShift(temp, operand.reg(), operand.extend(),
    453                    operand.shift_amount());
    454    Logical(rd, rn, Operand(temp), op);
    455  } else {
    456    // The operand can be encoded in the instruction.
    457    VIXL_ASSERT(operand.IsShiftedRegister());
    458    Logical(rd, rn, operand, op);
    459  }
    460 }
    461 
    462 
    463 void MacroAssembler::Mov(const Register& rd,
    464                         const Operand& operand,
    465                         DiscardMoveMode discard_mode) {
    466  // The worst case for size is mov immediate with up to 4 instructions.
    467  MacroEmissionCheckScope guard(this);
    468 
    469  if (operand.IsImmediate()) {
    470    // Call the macro assembler for generic immediates.
    471    Mov(rd, operand.immediate());
    472  } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) {
    473    // Emit a shift instruction if moving a shifted register. This operation
    474    // could also be achieved using an orr instruction (like orn used by Mvn),
    475    // but using a shift instruction makes the disassembly clearer.
    476    EmitShift(rd, operand.reg(), operand.shift(), operand.shift_amount());
    477  } else if (operand.IsExtendedRegister()) {
    478    // Emit an extend instruction if moving an extended register. This handles
    479    // extend with post-shift operations, too.
    480    EmitExtendShift(rd, operand.reg(), operand.extend(),
    481                    operand.shift_amount());
    482  } else {
    483    Mov(rd, operand.reg(), discard_mode);
    484  }
    485 }
    486 
    487 
    488 void MacroAssembler::Movi16bitHelper(const VRegister& vd, uint64_t imm) {
    489  VIXL_ASSERT(IsUint16(imm));
    490  int byte1 = (imm & 0xff);
    491  int byte2 = ((imm >> 8) & 0xff);
    492  if (byte1 == byte2) {
    493    movi(vd.Is64Bits() ? vd.V8B() : vd.V16B(), byte1);
    494  } else if (byte1 == 0) {
    495    movi(vd, byte2, LSL, 8);
    496  } else if (byte2 == 0) {
    497    movi(vd, byte1);
    498  } else if (byte1 == 0xff) {
    499    mvni(vd, ~byte2 & 0xff, LSL, 8);
    500  } else if (byte2 == 0xff) {
    501    mvni(vd, ~byte1 & 0xff);
    502  } else {
    503    UseScratchRegisterScope temps(this);
    504    Register temp = temps.AcquireW();
    505    movz(temp, imm);
    506    dup(vd, temp);
    507  }
    508 }
    509 
    510 
    511 void MacroAssembler::Movi32bitHelper(const VRegister& vd, uint64_t imm) {
    512  VIXL_ASSERT(IsUint32(imm));
    513 
    514  uint8_t bytes[sizeof(imm)];
    515  memcpy(bytes, &imm, sizeof(imm));
    516 
    517  // All bytes are either 0x00 or 0xff.
    518  {
    519    bool all0orff = true;
    520    for (int i = 0; i < 4; ++i) {
    521      if ((bytes[i] != 0) && (bytes[i] != 0xff)) {
    522        all0orff = false;
    523        break;
    524      }
    525    }
    526 
    527    if (all0orff == true) {
    528      movi(vd.Is64Bits() ? vd.V1D() : vd.V2D(), ((imm << 32) | imm));
    529      return;
    530    }
    531  }
    532 
    533  // Of the 4 bytes, only one byte is non-zero.
    534  for (int i = 0; i < 4; i++) {
    535    if ((imm & (0xff << (i * 8))) == imm) {
    536      movi(vd, bytes[i], LSL, i * 8);
    537      return;
    538    }
    539  }
    540 
    541  // Of the 4 bytes, only one byte is not 0xff.
    542  for (int i = 0; i < 4; i++) {
    543    uint32_t mask = ~(0xff << (i * 8));
    544    if ((imm & mask) == mask) {
    545      mvni(vd, ~bytes[i] & 0xff, LSL, i * 8);
    546      return;
    547    }
    548  }
    549 
    550  // Immediate is of the form 0x00MMFFFF.
    551  if ((imm & 0xff00ffff) == 0x0000ffff) {
    552    movi(vd, bytes[2], MSL, 16);
    553    return;
    554  }
    555 
    556  // Immediate is of the form 0x0000MMFF.
    557  if ((imm & 0xffff00ff) == 0x000000ff) {
    558    movi(vd, bytes[1], MSL, 8);
    559    return;
    560  }
    561 
    562  // Immediate is of the form 0xFFMM0000.
    563  if ((imm & 0xff00ffff) == 0xff000000) {
    564    mvni(vd, ~bytes[2] & 0xff, MSL, 16);
    565    return;
    566  }
    567  // Immediate is of the form 0xFFFFMM00.
    568  if ((imm & 0xffff00ff) == 0xffff0000) {
    569    mvni(vd, ~bytes[1] & 0xff, MSL, 8);
    570    return;
    571  }
    572 
    573  // Top and bottom 16-bits are equal.
    574  if (((imm >> 16) & 0xffff) == (imm & 0xffff)) {
    575    Movi16bitHelper(vd.Is64Bits() ? vd.V4H() : vd.V8H(), imm & 0xffff);
    576    return;
    577  }
    578 
    579  // Default case.
    580  {
    581    UseScratchRegisterScope temps(this);
    582    Register temp = temps.AcquireW();
    583    Mov(temp, imm);
    584    dup(vd, temp);
    585  }
    586 }
    587 
    588 
    589 void MacroAssembler::Movi64bitHelper(const VRegister& vd, uint64_t imm) {
    590  // All bytes are either 0x00 or 0xff.
    591  {
    592    bool all0orff = true;
    593    for (int i = 0; i < 8; ++i) {
    594      int byteval = (imm >> (i * 8)) & 0xff;
    595      if (byteval != 0 && byteval != 0xff) {
    596        all0orff = false;
    597        break;
    598      }
    599    }
    600    if (all0orff == true) {
    601      movi(vd, imm);
    602      return;
    603    }
    604  }
    605 
    606  // Top and bottom 32-bits are equal.
    607  if (((imm >> 32) & 0xffffffff) == (imm & 0xffffffff)) {
    608    Movi32bitHelper(vd.Is64Bits() ? vd.V2S() : vd.V4S(), imm & 0xffffffff);
    609    return;
    610  }
    611 
    612  // Default case.
    613  {
    614    UseScratchRegisterScope temps(this);
    615    Register temp = temps.AcquireX();
    616    Mov(temp, imm);
    617    if (vd.Is1D()) {
    618      mov(vd.D(), 0, temp);
    619    } else {
    620      dup(vd.V2D(), temp);
    621    }
    622  }
    623 }
    624 
    625 
    626 void MacroAssembler::Movi(const VRegister& vd,
    627                          uint64_t imm,
    628                          Shift shift,
    629                          int shift_amount) {
    630  MacroEmissionCheckScope guard(this);
    631  if (shift_amount != 0 || shift != LSL) {
    632    movi(vd, imm, shift, shift_amount);
    633  } else if (vd.Is8B() || vd.Is16B()) {
    634    // 8-bit immediate.
    635    VIXL_ASSERT(IsUint8(imm));
    636    movi(vd, imm);
    637  } else if (vd.Is4H() || vd.Is8H()) {
    638    // 16-bit immediate.
    639    Movi16bitHelper(vd, imm);
    640  } else if (vd.Is2S() || vd.Is4S()) {
    641    // 32-bit immediate.
    642    Movi32bitHelper(vd, imm);
    643  } else {
    644    // 64-bit immediate.
    645    Movi64bitHelper(vd, imm);
    646  }
    647 }
    648 
    649 
    650 void MacroAssembler::Movi(const VRegister& vd,
    651                          uint64_t hi,
    652                          uint64_t lo) {
    653  VIXL_ASSERT(vd.Is128Bits());
    654  UseScratchRegisterScope temps(this);
    655 
    656  // When hi == lo, the following generates good code.
    657  //
    658  // In situations where the constants are complex and hi != lo, the following
    659  // can turn into up to 10 instructions: 2*(mov + 3*movk + dup/insert).  To do
    660  // any better, we could try to estimate whether splatting the high value and
    661  // updating the low value would generate fewer instructions than vice versa
    662  // (what we do now).
    663  //
    664  // (A PC-relative load from memory to the vector register (ADR + LD2) is going
    665  // to have fairly high latency but is fairly compact; not clear what the best
    666  // tradeoff is.)
    667 
    668  Movi(vd.V2D(), lo);
    669  if (hi != lo) {
    670    Register temp = temps.AcquireX();
    671    Mov(temp, hi);
    672    Ins(vd.V2D(), 1, temp);
    673  }
    674 }
    675 
    676 
    677 void MacroAssembler::Mvn(const Register& rd, const Operand& operand) {
    678  // The worst case for size is mvn immediate with up to 4 instructions.
    679  MacroEmissionCheckScope guard(this);
    680 
    681  if (operand.IsImmediate()) {
    682    // Call the macro assembler for generic immediates.
    683    Mvn(rd, operand.immediate());
    684  } else if (operand.IsExtendedRegister()) {
    685    UseScratchRegisterScope temps(this);
    686    temps.Exclude(operand.reg());
    687 
    688    // Emit two instructions for the extend case. This differs from Mov, as
    689    // the extend and invert can't be achieved in one instruction.
    690    Register temp = temps.AcquireSameSizeAs(rd);
    691 
    692    // VIXL can acquire temp registers. Assert that the caller is aware.
    693    VIXL_ASSERT(!temp.Is(rd) && !temp.Is(operand.maybeReg()));
    694 
    695    EmitExtendShift(temp, operand.reg(), operand.extend(),
    696                    operand.shift_amount());
    697    mvn(rd, Operand(temp));
    698  } else {
    699    // Otherwise, register and shifted register cases can be handled by the
    700    // assembler directly, using orn.
    701    mvn(rd, operand);
    702  }
    703 }
    704 
    705 
    706 void MacroAssembler::Mov(const Register& rd, uint64_t imm) {
    707  MoveImmediateHelper(this, rd, imm);
    708 }
    709 
    710 
    711 void MacroAssembler::Ccmp(const Register& rn,
    712                          const Operand& operand,
    713                          StatusFlags nzcv,
    714                          Condition cond) {
    715  if (operand.IsImmediate()) {
    716    int64_t imm = operand.immediate();
    717    if (imm < 0 && imm != std::numeric_limits<int64_t>::min()) {
    718      ConditionalCompareMacro(rn, -imm, nzcv, cond, CCMN);
    719      return;
    720    }
    721  }
    722  ConditionalCompareMacro(rn, operand, nzcv, cond, CCMP);
    723 }
    724 
    725 
    726 void MacroAssembler::Ccmn(const Register& rn,
    727                          const Operand& operand,
    728                          StatusFlags nzcv,
    729                          Condition cond) {
    730  if (operand.IsImmediate()) {
    731    int64_t imm = operand.immediate();
    732    if (imm < 0 && imm != std::numeric_limits<int64_t>::min()) {
    733      ConditionalCompareMacro(rn, -imm, nzcv, cond, CCMP);
    734      return;
    735    }
    736  }
    737  ConditionalCompareMacro(rn, operand, nzcv, cond, CCMN);
    738 }
    739 
    740 
    741 void MacroAssembler::ConditionalCompareMacro(const Register& rn,
    742                                             const Operand& operand,
    743                                             StatusFlags nzcv,
    744                                             Condition cond,
    745                                             ConditionalCompareOp op) {
    746  VIXL_ASSERT((cond != al) && (cond != nv));
    747  // The worst case for size is ccmp immediate:
    748  //  * up to 4 instructions to materialise the constant
    749  //  * 1 instruction for ccmp
    750  MacroEmissionCheckScope guard(this);
    751 
    752  if ((operand.IsShiftedRegister() && (operand.shift_amount() == 0)) ||
    753      (operand.IsImmediate() && IsImmConditionalCompare(operand.immediate()))) {
    754    // The immediate can be encoded in the instruction, or the operand is an
    755    // unshifted register: call the assembler.
    756    ConditionalCompare(rn, operand, nzcv, cond, op);
    757  } else {
    758    UseScratchRegisterScope temps(this);
    759    // The operand isn't directly supported by the instruction: perform the
    760    // operation on a temporary register.
    761    Register temp = temps.AcquireSameSizeAs(rn);
    762    VIXL_ASSERT(!temp.Is(rn) && !temp.Is(operand.maybeReg()));
    763    Mov(temp, operand);
    764    ConditionalCompare(rn, temp, nzcv, cond, op);
    765  }
    766 }
    767 
    768 
    769 void MacroAssembler::Csel(const Register& rd,
    770                          const Register& rn,
    771                          const Operand& operand,
    772                          Condition cond) {
    773  VIXL_ASSERT(!rd.IsZero());
    774  VIXL_ASSERT(!rn.IsZero());
    775  VIXL_ASSERT((cond != al) && (cond != nv));
    776  // The worst case for size is csel immediate:
    777  //  * up to 4 instructions to materialise the constant
    778  //  * 1 instruction for csel
    779  MacroEmissionCheckScope guard(this);
    780 
    781  if (operand.IsImmediate()) {
    782    // Immediate argument. Handle special cases of 0, 1 and -1 using zero
    783    // register.
    784    int64_t imm = operand.immediate();
    785    Register zr = AppropriateZeroRegFor(rn);
    786    if (imm == 0) {
    787      csel(rd, rn, zr, cond);
    788    } else if (imm == 1) {
    789      csinc(rd, rn, zr, cond);
    790    } else if (imm == -1) {
    791      csinv(rd, rn, zr, cond);
    792    } else {
    793      UseScratchRegisterScope temps(this);
    794      Register temp = temps.AcquireSameSizeAs(rn);
    795      VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn));
    796      VIXL_ASSERT(!temp.Is(operand.maybeReg()));
    797      Mov(temp, operand.immediate());
    798      csel(rd, rn, temp, cond);
    799    }
    800  } else if (operand.IsShiftedRegister() && (operand.shift_amount() == 0)) {
    801    // Unshifted register argument.
    802    csel(rd, rn, operand.reg(), cond);
    803  } else {
    804    // All other arguments.
    805    UseScratchRegisterScope temps(this);
    806    Register temp = temps.AcquireSameSizeAs(rn);
    807    VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn));
    808    VIXL_ASSERT(!temp.Is(operand.maybeReg()));
    809    Mov(temp, operand);
    810    csel(rd, rn, temp, cond);
    811  }
    812 }
    813 
    814 
    815 void MacroAssembler::Add(const Register& rd,
    816                         const Register& rn,
    817                         const Operand& operand,
    818                         FlagsUpdate S) {
    819  if (operand.IsImmediate()) {
    820    int64_t imm = operand.immediate();
    821    if (imm < 0 && imm != std::numeric_limits<int64_t>::min() &&
    822        IsImmAddSub(-imm)) {
    823      AddSubMacro(rd, rn, -imm, S, SUB);
    824      return;
    825    }
    826  }
    827  AddSubMacro(rd, rn, operand, S, ADD);
    828 }
    829 
    830 
    831 void MacroAssembler::Adds(const Register& rd,
    832                          const Register& rn,
    833                          const Operand& operand) {
    834  Add(rd, rn, operand, SetFlags);
    835 }
    836 
    837 #define MINMAX(V)        \
    838  V(Smax, smax, IsInt8)  \
    839  V(Smin, smin, IsInt8)  \
    840  V(Umax, umax, IsUint8) \
    841  V(Umin, umin, IsUint8)
    842 
    843 #define VIXL_DEFINE_MASM_FUNC(MASM, ASM, RANGE)      \
    844  void MacroAssembler::MASM(const Register& rd,      \
    845                            const Register& rn,      \
    846                            const Operand& op) {     \
    847    if (op.IsImmediate()) {                          \
    848      int64_t imm = op.GetImmediate();               \
    849      if (!RANGE(imm)) {                             \
    850        UseScratchRegisterScope temps(this);         \
    851        Register temp = temps.AcquireSameSizeAs(rd); \
    852        Mov(temp, imm);                              \
    853        MASM(rd, rn, temp);                          \
    854        return;                                      \
    855      }                                              \
    856    }                                                \
    857    SingleEmissionCheckScope guard(this);            \
    858    ASM(rd, rn, op);                                 \
    859  }
    860 MINMAX(VIXL_DEFINE_MASM_FUNC)
    861 #undef VIXL_DEFINE_MASM_FUNC
    862 
    863 // Mozilla change: Undefine MINMAX
    864 #undef MINMAX
    865 
    866 void MacroAssembler::Sub(const Register& rd,
    867                         const Register& rn,
    868                         const Operand& operand,
    869                         FlagsUpdate S) {
    870  if (operand.IsImmediate()) {
    871    int64_t imm = operand.immediate();
    872    if (imm < 0 && imm != std::numeric_limits<int64_t>::min() &&
    873        IsImmAddSub(-imm)) {
    874      AddSubMacro(rd, rn, -imm, S, ADD);
    875      return;
    876    }
    877  }
    878  AddSubMacro(rd, rn, operand, S, SUB);
    879 }
    880 
    881 
    882 void MacroAssembler::Subs(const Register& rd,
    883                          const Register& rn,
    884                          const Operand& operand) {
    885  Sub(rd, rn, operand, SetFlags);
    886 }
    887 
    888 
    889 void MacroAssembler::Cmn(const Register& rn, const Operand& operand) {
    890  Adds(AppropriateZeroRegFor(rn), rn, operand);
    891 }
    892 
    893 
    894 void MacroAssembler::Cmp(const Register& rn, const Operand& operand) {
    895  Subs(AppropriateZeroRegFor(rn), rn, operand);
    896 }
    897 
    898 
    899 void MacroAssembler::Fcmp(const FPRegister& fn, double value,
    900                          FPTrapFlags trap) {
    901  // The worst case for size is:
    902  //  * 1 to materialise the constant, using literal pool if necessary
    903  //  * 1 instruction for fcmp{e}
    904  MacroEmissionCheckScope guard(this);
    905  if (value != 0.0) {
    906    UseScratchRegisterScope temps(this);
    907    FPRegister tmp = temps.AcquireSameSizeAs(fn);
    908    VIXL_ASSERT(!tmp.Is(fn));
    909    Fmov(tmp, value);
    910    FPCompareMacro(fn, tmp, trap);
    911  } else {
    912    FPCompareMacro(fn, value, trap);
    913  }
    914 }
    915 
    916 
    917 void MacroAssembler::Fcmpe(const FPRegister& fn, double value) {
    918  Fcmp(fn, value, EnableTrap);
    919 }
    920 
    921 
    922 void MacroAssembler::Fmov(VRegister vd, double imm) {
    923  // Floating point immediates are loaded through the literal pool.
    924  MacroEmissionCheckScope guard(this);
    925 
    926  if (vd.Is1S() || vd.Is2S() || vd.Is4S()) {
    927    Fmov(vd, static_cast<float>(imm));
    928    return;
    929  }
    930 
    931  VIXL_ASSERT(vd.Is1D() || vd.Is2D());
    932  if (IsImmFP64(imm)) {
    933    fmov(vd, imm);
    934  } else {
    935    uint64_t rawbits = DoubleToRawbits(imm);
    936    if (vd.IsScalar()) {
    937      if (rawbits == 0) {
    938        fmov(vd, xzr);
    939      } else {
    940        Assembler::fImmPool64(vd, imm);
    941      }
    942    } else {
    943      // TODO: consider NEON support for load literal.
    944      Movi(vd, rawbits);
    945    }
    946  }
    947 }
    948 
    949 
    950 void MacroAssembler::Fmov(VRegister vd, float imm) {
    951  // Floating point immediates are loaded through the literal pool.
    952  MacroEmissionCheckScope guard(this);
    953 
    954  if (vd.Is1D() || vd.Is2D()) {
    955    Fmov(vd, static_cast<double>(imm));
    956    return;
    957  }
    958 
    959  VIXL_ASSERT(vd.Is1S() || vd.Is2S() || vd.Is4S());
    960  if (IsImmFP32(imm)) {
    961    fmov(vd, imm);
    962  } else {
    963    uint32_t rawbits = FloatToRawbits(imm);
    964    if (vd.IsScalar()) {
    965      if (rawbits == 0) {
    966        fmov(vd, wzr);
    967      } else {
    968        Assembler::fImmPool32(vd, imm);
    969      }
    970    } else {
    971      // TODO: consider NEON support for load literal.
    972      Movi(vd, rawbits);
    973    }
    974  }
    975 }
    976 
    977 
    978 
    979 void MacroAssembler::Neg(const Register& rd,
    980                         const Operand& operand) {
    981  if (operand.IsImmediate()) {
    982    int64_t imm = operand.immediate();
    983    if (imm != std::numeric_limits<int64_t>::min()) {
    984      Mov(rd, -imm);
    985      return;
    986    }
    987  }
    988  Sub(rd, AppropriateZeroRegFor(rd), operand);
    989 }
    990 
    991 
    992 void MacroAssembler::Negs(const Register& rd,
    993                          const Operand& operand) {
    994  Subs(rd, AppropriateZeroRegFor(rd), operand);
    995 }
    996 
    997 
    998 bool MacroAssembler::TryOneInstrMoveImmediate(const Register& dst,
    999                                              int64_t imm) {
   1000  return OneInstrMoveImmediateHelper(this, dst, imm);
   1001 }
   1002 
   1003 
   1004 Operand MacroAssembler::MoveImmediateForShiftedOp(const Register& dst,
   1005                                                  int64_t imm,
   1006                                                  PreShiftImmMode mode) {
   1007  int reg_size = dst.size();
   1008 
   1009  // Encode the immediate in a single move instruction, if possible.
   1010  if (TryOneInstrMoveImmediate(dst, imm)) {
   1011    // The move was successful; nothing to do here.
   1012  } else {
   1013    // Pre-shift the immediate to the least-significant bits of the register.
   1014    int shift_low = CountTrailingZeros(imm, reg_size);
   1015    if (mode == kLimitShiftForSP) {
   1016      // When applied to the stack pointer, the subsequent arithmetic operation
   1017      // can use the extend form to shift left by a maximum of four bits. Right
   1018      // shifts are not allowed, so we filter them out later before the new
   1019      // immediate is tested.
   1020      shift_low = std::min(shift_low, 4);
   1021    }
   1022 
   1023    int64_t imm_low = imm >> shift_low;
   1024 
   1025    // Pre-shift the immediate to the most-significant bits of the register,
   1026    // inserting set bits in the least-significant bits.
   1027    int shift_high = CountLeadingZeros(imm, reg_size);
   1028    int64_t imm_high = (imm << shift_high) | ((INT64_C(1) << shift_high) - 1);
   1029 
   1030    if ((mode != kNoShift) && TryOneInstrMoveImmediate(dst, imm_low)) {
   1031      // The new immediate has been moved into the destination's low bits:
   1032      // return a new leftward-shifting operand.
   1033      return Operand(dst, LSL, shift_low);
   1034    } else if ((mode == kAnyShift) && TryOneInstrMoveImmediate(dst, imm_high)) {
   1035      // The new immediate has been moved into the destination's high bits:
   1036      // return a new rightward-shifting operand.
   1037      return Operand(dst, LSR, shift_high);
   1038    } else {
   1039      Mov(dst, imm);
   1040    }
   1041  }
   1042  return Operand(dst);
   1043 }
   1044 
   1045 
   1046 void MacroAssembler::ComputeAddress(const Register& dst,
   1047                                    const MemOperand& mem_op) {
   1048  // We cannot handle pre-indexing or post-indexing.
   1049  VIXL_ASSERT(mem_op.addrmode() == Offset);
   1050  Register base = mem_op.base();
   1051  if (mem_op.IsImmediateOffset()) {
   1052    Add(dst, base, mem_op.offset());
   1053  } else {
   1054    VIXL_ASSERT(mem_op.IsRegisterOffset());
   1055    Register reg_offset = mem_op.regoffset();
   1056    Shift shift = mem_op.shift();
   1057    Extend extend = mem_op.extend();
   1058    if (shift == NO_SHIFT) {
   1059      VIXL_ASSERT(extend != NO_EXTEND);
   1060      Add(dst, base, Operand(reg_offset, extend, mem_op.shift_amount()));
   1061    } else {
   1062      VIXL_ASSERT(extend == NO_EXTEND);
   1063      Add(dst, base, Operand(reg_offset, shift, mem_op.shift_amount()));
   1064    }
   1065  }
   1066 }
   1067 
   1068 
   1069 void MacroAssembler::AddSubMacro(const Register& rd,
   1070                                 const Register& rn,
   1071                                 const Operand& operand,
   1072                                 FlagsUpdate S,
   1073                                 AddSubOp op) {
   1074  // Worst case is add/sub immediate:
   1075  //  * up to 4 instructions to materialise the constant
   1076  //  * 1 instruction for add/sub
   1077  MacroEmissionCheckScope guard(this);
   1078 
   1079  if (operand.IsZero() && rd.Is(rn) && rd.Is64Bits() && rn.Is64Bits() &&
   1080      (S == LeaveFlags)) {
   1081    // The instruction would be a nop. Avoid generating useless code.
   1082    return;
   1083  }
   1084 
   1085  if ((operand.IsImmediate() && !IsImmAddSub(operand.immediate())) ||
   1086      (rn.IsZero() && !operand.IsShiftedRegister()) ||
   1087      (operand.IsShiftedRegister() && (operand.shift() == ROR))) {
   1088    UseScratchRegisterScope temps(this);
   1089    Register temp = temps.AcquireSameSizeAs(rn);
   1090    if (operand.IsImmediate()) {
   1091      PreShiftImmMode mode = kAnyShift;
   1092 
   1093      // If the destination or source register is the stack pointer, we can
   1094      // only pre-shift the immediate right by values supported in the add/sub
   1095      // extend encoding.
   1096      if (rd.IsSP()) {
   1097        // If the destination is SP and flags will be set, we can't pre-shift
   1098        // the immediate at all. 
   1099        mode = (S == SetFlags) ? kNoShift : kLimitShiftForSP;
   1100      } else if (rn.IsSP()) {
   1101        mode = kLimitShiftForSP;
   1102      } 
   1103 
   1104      Operand imm_operand =
   1105          MoveImmediateForShiftedOp(temp, operand.immediate(), mode);
   1106      AddSub(rd, rn, imm_operand, S, op); 
   1107    } else {
   1108      Mov(temp, operand);
   1109      AddSub(rd, rn, temp, S, op);
   1110    }
   1111  } else {
   1112    AddSub(rd, rn, operand, S, op);
   1113  }
   1114 }
   1115 
   1116 
   1117 void MacroAssembler::Adc(const Register& rd,
   1118                         const Register& rn,
   1119                         const Operand& operand) {
   1120  AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, ADC);
   1121 }
   1122 
   1123 
   1124 void MacroAssembler::Adcs(const Register& rd,
   1125                          const Register& rn,
   1126                          const Operand& operand) {
   1127  AddSubWithCarryMacro(rd, rn, operand, SetFlags, ADC);
   1128 }
   1129 
   1130 
   1131 void MacroAssembler::Sbc(const Register& rd,
   1132                         const Register& rn,
   1133                         const Operand& operand) {
   1134  AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, SBC);
   1135 }
   1136 
   1137 
   1138 void MacroAssembler::Sbcs(const Register& rd,
   1139                          const Register& rn,
   1140                          const Operand& operand) {
   1141  AddSubWithCarryMacro(rd, rn, operand, SetFlags, SBC);
   1142 }
   1143 
   1144 
   1145 void MacroAssembler::Ngc(const Register& rd,
   1146                         const Operand& operand) {
   1147  Register zr = AppropriateZeroRegFor(rd);
   1148  Sbc(rd, zr, operand);
   1149 }
   1150 
   1151 
   1152 void MacroAssembler::Ngcs(const Register& rd,
   1153                         const Operand& operand) {
   1154  Register zr = AppropriateZeroRegFor(rd);
   1155  Sbcs(rd, zr, operand);
   1156 }
   1157 
   1158 
   1159 void MacroAssembler::AddSubWithCarryMacro(const Register& rd,
   1160                                          const Register& rn,
   1161                                          const Operand& operand,
   1162                                          FlagsUpdate S,
   1163                                          AddSubWithCarryOp op) {
   1164  VIXL_ASSERT(rd.size() == rn.size());
   1165  // Worst case is addc/subc immediate:
   1166  //  * up to 4 instructions to materialise the constant
   1167  //  * 1 instruction for add/sub
   1168  MacroEmissionCheckScope guard(this);
   1169  UseScratchRegisterScope temps(this);
   1170 
   1171  if (operand.IsImmediate() ||
   1172      (operand.IsShiftedRegister() && (operand.shift() == ROR))) {
   1173    // Add/sub with carry (immediate or ROR shifted register.)
   1174    Register temp = temps.AcquireSameSizeAs(rn);
   1175    VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn) && !temp.Is(operand.maybeReg()));
   1176    Mov(temp, operand);
   1177    AddSubWithCarry(rd, rn, Operand(temp), S, op);
   1178  } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) {
   1179    // Add/sub with carry (shifted register).
   1180    VIXL_ASSERT(operand.reg().size() == rd.size());
   1181    VIXL_ASSERT(operand.shift() != ROR);
   1182    VIXL_ASSERT(IsUintN(rd.size() == kXRegSize ? kXRegSizeLog2 : kWRegSizeLog2,
   1183                    operand.shift_amount()));
   1184    temps.Exclude(operand.reg());
   1185    Register temp = temps.AcquireSameSizeAs(rn);
   1186    VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn) && !temp.Is(operand.maybeReg()));
   1187    EmitShift(temp, operand.reg(), operand.shift(), operand.shift_amount());
   1188    AddSubWithCarry(rd, rn, Operand(temp), S, op);
   1189  } else if (operand.IsExtendedRegister()) {
   1190    // Add/sub with carry (extended register).
   1191    VIXL_ASSERT(operand.reg().size() <= rd.size());
   1192    // Add/sub extended supports a shift <= 4. We want to support exactly the
   1193    // same modes.
   1194    VIXL_ASSERT(operand.shift_amount() <= 4);
   1195    VIXL_ASSERT(operand.reg().Is64Bits() ||
   1196           ((operand.extend() != UXTX) && (operand.extend() != SXTX)));
   1197    temps.Exclude(operand.reg());
   1198    Register temp = temps.AcquireSameSizeAs(rn);
   1199    VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn) && !temp.Is(operand.maybeReg()));
   1200    EmitExtendShift(temp, operand.reg(), operand.extend(),
   1201                    operand.shift_amount());
   1202    AddSubWithCarry(rd, rn, Operand(temp), S, op);
   1203  } else {
   1204    // The addressing mode is directly supported by the instruction.
   1205    AddSubWithCarry(rd, rn, operand, S, op);
   1206  }
   1207 }
   1208 
   1209 #define DEFINE_FUNCTION(FN, REGTYPE, REG, OP)                               \
   1210  js::wasm::FaultingCodeOffset MacroAssembler::FN(const REGTYPE REG,        \
   1211                                                  const MemOperand& addr) { \
   1212    return LoadStoreMacro(REG, addr, OP);                                   \
   1213  }
   1214 LS_MACRO_LIST(DEFINE_FUNCTION)
   1215 #undef DEFINE_FUNCTION
   1216 
   1217 js::wasm::FaultingCodeOffset MacroAssembler::LoadStoreMacro(
   1218    const CPURegister& rt,
   1219    const MemOperand& addr,
   1220    LoadStoreOp op) {
   1221  // Worst case is ldr/str pre/post index:
   1222  //  * 1 instruction for ldr/str
   1223  //  * up to 4 instructions to materialise the constant
   1224  //  * 1 instruction to update the base
   1225  MacroEmissionCheckScope guard(this);
   1226 
   1227  int64_t offset = addr.offset();
   1228  unsigned access_size = CalcLSDataSize(op);
   1229 
   1230  // Check if an immediate offset fits in the immediate field of the
   1231  // appropriate instruction. If not, emit two instructions to perform
   1232  // the operation.
   1233  js::wasm::FaultingCodeOffset fco;
   1234  if (addr.IsImmediateOffset() && !IsImmLSScaled(offset, access_size) &&
   1235      !IsImmLSUnscaled(offset)) {
   1236    // Immediate offset that can't be encoded using unsigned or unscaled
   1237    // addressing modes.
   1238    VIXL_ASSERT(addr.regoffset().Is(NoReg));
   1239    UseScratchRegisterScope temps(this);
   1240    int64_t offset = addr.offset();
   1241    Register recycle0 = NoReg;
   1242    Register recycle1 = NoReg;
   1243    if (!temps.HasAvailableRegister()) {
   1244      // Recycle the first pair of registers which are not aliasing the operands.
   1245      js::jit::AllocatableGeneralRegisterSet
   1246          freeRegs(js::jit::GeneralRegisterSet::Volatile());
   1247      freeRegs.takeUnchecked(addr.base().asUnsized());
   1248      freeRegs.takeUnchecked(rt.X().asUnsized());
   1249      recycle0 = Register(freeRegs.takeFirst(), kXRegSize);
   1250      recycle1 = Register(freeRegs.takeFirst(), kXRegSize);
   1251      Push(recycle0, recycle1);
   1252      temps.Include(recycle0, recycle1);
   1253      if (addr.base().Is(GetStackPointer64())) {
   1254        offset += 2 * sizeof(uintptr_t);
   1255      }
   1256    }
   1257    Register temp = temps.AcquireSameSizeAs(addr.base());
   1258    VIXL_ASSERT(!temp.Is(rt));
   1259    VIXL_ASSERT(!temp.Is(addr.base()));
   1260    Mov(temp, offset);
   1261    {
   1262      js::jit::AutoForbidPoolsAndNops afp(this, 1);
   1263      fco = js::wasm::FaultingCodeOffset(currentOffset());
   1264      LoadStore(rt, MemOperand(addr.base(), temp), op);
   1265    }
   1266    if (!recycle0.Is(NoReg)) {
   1267      temps.Exclude(recycle0, recycle1);
   1268      Pop(recycle1, recycle0);
   1269    }
   1270  } else if (addr.IsPostIndex() && !IsImmLSUnscaled(offset)) {
   1271    // Post-index beyond unscaled addressing range.
   1272    {
   1273      js::jit::AutoForbidPoolsAndNops afp(this, 1);
   1274      fco = js::wasm::FaultingCodeOffset(currentOffset());
   1275      LoadStore(rt, MemOperand(addr.base()), op);
   1276    }
   1277    Add(addr.base(), addr.base(), Operand(offset));
   1278  } else if (addr.IsPreIndex() && !IsImmLSUnscaled(offset)) {
   1279    // Pre-index beyond unscaled addressing range.
   1280    Add(addr.base(), addr.base(), Operand(offset));
   1281    {
   1282      js::jit::AutoForbidPoolsAndNops afp(this, 1);
   1283      fco = js::wasm::FaultingCodeOffset(currentOffset());
   1284      LoadStore(rt, MemOperand(addr.base()), op);
   1285    }
   1286  } else {
   1287    // Encodable in one load/store instruction.
   1288    js::jit::AutoForbidPoolsAndNops afp(this, 1);
   1289    fco = js::wasm::FaultingCodeOffset(currentOffset());
   1290    LoadStore(rt, addr, op);
   1291  }
   1292 
   1293  return fco;
   1294 }
   1295 
   1296 #define DEFINE_FUNCTION(FN, REGTYPE, REG, REG2, OP)  \
   1297 void MacroAssembler::FN(const REGTYPE REG,           \
   1298                        const REGTYPE REG2,          \
   1299                        const MemOperand& addr) {    \
   1300  LoadStorePairMacro(REG, REG2, addr, OP);           \
   1301 }
   1302 LSPAIR_MACRO_LIST(DEFINE_FUNCTION)
   1303 #undef DEFINE_FUNCTION
   1304 
   1305 void MacroAssembler::LoadStorePairMacro(const CPURegister& rt,
   1306                                        const CPURegister& rt2,
   1307                                        const MemOperand& addr,
   1308                                        LoadStorePairOp op) {
   1309  // TODO(all): Should we support register offset for load-store-pair?
   1310  VIXL_ASSERT(!addr.IsRegisterOffset());
   1311  // Worst case is ldp/stp immediate:
   1312  //  * 1 instruction for ldp/stp
   1313  //  * up to 4 instructions to materialise the constant
   1314  //  * 1 instruction to update the base
   1315  MacroEmissionCheckScope guard(this);
   1316 
   1317  int64_t offset = addr.offset();
   1318  unsigned access_size = CalcLSPairDataSize(op);
   1319 
   1320  // Check if the offset fits in the immediate field of the appropriate
   1321  // instruction. If not, emit two instructions to perform the operation.
   1322  if (IsImmLSPair(offset, access_size)) {
   1323    // Encodable in one load/store pair instruction.
   1324    LoadStorePair(rt, rt2, addr, op);
   1325  } else {
   1326    Register base = addr.base();
   1327    if (addr.IsImmediateOffset()) {
   1328      UseScratchRegisterScope temps(this);
   1329      Register temp = temps.AcquireSameSizeAs(base);
   1330      Add(temp, base, offset);
   1331      LoadStorePair(rt, rt2, MemOperand(temp), op);
   1332    } else if (addr.IsPostIndex()) {
   1333      LoadStorePair(rt, rt2, MemOperand(base), op);
   1334      Add(base, base, offset);
   1335    } else {
   1336      VIXL_ASSERT(addr.IsPreIndex());
   1337      Add(base, base, offset);
   1338      LoadStorePair(rt, rt2, MemOperand(base), op);
   1339    }
   1340  }
   1341 }
   1342 
   1343 
   1344 void MacroAssembler::Prfm(PrefetchOperation op, const MemOperand& addr) {
   1345  MacroEmissionCheckScope guard(this);
   1346 
   1347  // There are no pre- or post-index modes for prfm.
   1348  VIXL_ASSERT(addr.IsImmediateOffset() || addr.IsRegisterOffset());
   1349 
   1350  // The access size is implicitly 8 bytes for all prefetch operations.
   1351  unsigned size = kXRegSizeInBytesLog2;
   1352 
   1353  // Check if an immediate offset fits in the immediate field of the
   1354  // appropriate instruction. If not, emit two instructions to perform
   1355  // the operation.
   1356  if (addr.IsImmediateOffset() && !IsImmLSScaled(addr.offset(), size) &&
   1357      !IsImmLSUnscaled(addr.offset())) {
   1358    // Immediate offset that can't be encoded using unsigned or unscaled
   1359    // addressing modes.
   1360    UseScratchRegisterScope temps(this);
   1361    Register temp = temps.AcquireSameSizeAs(addr.base());
   1362    Mov(temp, addr.offset());
   1363    Prefetch(op, MemOperand(addr.base(), temp));
   1364  } else {
   1365    // Simple register-offsets are encodable in one instruction.
   1366    Prefetch(op, addr);
   1367  }
   1368 }
   1369 
   1370 
   1371 void MacroAssembler::PushStackPointer() {
   1372  PrepareForPush(1, 8);
   1373 
   1374  // Pushing a stack pointer leads to implementation-defined
   1375  // behavior, which may be surprising. In particular,
   1376  //   str x28, [x28, #-8]!
   1377  // pre-decrements the stack pointer, storing the decremented value.
   1378  // Additionally, sp is read as xzr in this context, so it cannot be pushed.
   1379  // So we must use a scratch register.
   1380  UseScratchRegisterScope temps(this);
   1381  Register scratch = temps.AcquireX();
   1382 
   1383  Mov(scratch, GetStackPointer64());
   1384  str(scratch, MemOperand(GetStackPointer64(), -8, PreIndex));
   1385 }
   1386 
   1387 
   1388 void MacroAssembler::Push(const CPURegister& src0, const CPURegister& src1,
   1389                          const CPURegister& src2, const CPURegister& src3) {
   1390  VIXL_ASSERT(AreSameSizeAndType(src0, src1, src2, src3));
   1391  VIXL_ASSERT(src0.IsValid());
   1392 
   1393  int count = 1 + src1.IsValid() + src2.IsValid() + src3.IsValid();
   1394  int size = src0.SizeInBytes();
   1395 
   1396  if (src0.Is(GetStackPointer64())) {
   1397    VIXL_ASSERT(count == 1);
   1398    VIXL_ASSERT(size == 8);
   1399    PushStackPointer();
   1400    return;
   1401  }
   1402 
   1403  PrepareForPush(count, size);
   1404  PushHelper(count, size, src0, src1, src2, src3);
   1405 }
   1406 
   1407 
   1408 void MacroAssembler::Pop(const CPURegister& dst0, const CPURegister& dst1,
   1409                         const CPURegister& dst2, const CPURegister& dst3) {
   1410  // It is not valid to pop into the same register more than once in one
   1411  // instruction, not even into the zero register.
   1412  VIXL_ASSERT(!AreAliased(dst0, dst1, dst2, dst3));
   1413  VIXL_ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3));
   1414  VIXL_ASSERT(dst0.IsValid());
   1415 
   1416  int count = 1 + dst1.IsValid() + dst2.IsValid() + dst3.IsValid();
   1417  int size = dst0.SizeInBytes();
   1418 
   1419  PrepareForPop(count, size);
   1420  PopHelper(count, size, dst0, dst1, dst2, dst3);
   1421 }
   1422 
   1423 
   1424 void MacroAssembler::PushCPURegList(CPURegList registers) {
   1425  VIXL_ASSERT(!registers.Overlaps(*TmpList()));
   1426  VIXL_ASSERT(!registers.Overlaps(*FPTmpList()));
   1427 
   1428  int reg_size = registers.RegisterSizeInBytes();
   1429  PrepareForPush(registers.Count(), reg_size);
   1430 
   1431  // Bump the stack pointer and store two registers at the bottom.
   1432  int size = registers.TotalSizeInBytes();
   1433  const CPURegister& bottom_0 = registers.PopLowestIndex();
   1434  const CPURegister& bottom_1 = registers.PopLowestIndex();
   1435  if (bottom_0.IsValid() && bottom_1.IsValid()) {
   1436    Stp(bottom_0, bottom_1, MemOperand(GetStackPointer64(), -size, PreIndex));
   1437  } else if (bottom_0.IsValid()) {
   1438    Str(bottom_0, MemOperand(GetStackPointer64(), -size, PreIndex));
   1439  }
   1440 
   1441  int offset = 2 * reg_size;
   1442  while (!registers.IsEmpty()) {
   1443    const CPURegister& src0 = registers.PopLowestIndex();
   1444    const CPURegister& src1 = registers.PopLowestIndex();
   1445    if (src1.IsValid()) {
   1446      Stp(src0, src1, MemOperand(GetStackPointer64(), offset));
   1447    } else {
   1448      Str(src0, MemOperand(GetStackPointer64(), offset));
   1449    }
   1450    offset += 2 * reg_size;
   1451  }
   1452 }
   1453 
   1454 
   1455 void MacroAssembler::PopCPURegList(CPURegList registers) {
   1456  VIXL_ASSERT(!registers.Overlaps(*TmpList()));
   1457  VIXL_ASSERT(!registers.Overlaps(*FPTmpList()));
   1458 
   1459  int reg_size = registers.RegisterSizeInBytes();
   1460  PrepareForPop(registers.Count(), reg_size);
   1461 
   1462 
   1463  int size = registers.TotalSizeInBytes();
   1464  const CPURegister& bottom_0 = registers.PopLowestIndex();
   1465  const CPURegister& bottom_1 = registers.PopLowestIndex();
   1466 
   1467  int offset = 2 * reg_size;
   1468  while (!registers.IsEmpty()) {
   1469    const CPURegister& dst0 = registers.PopLowestIndex();
   1470    const CPURegister& dst1 = registers.PopLowestIndex();
   1471    if (dst1.IsValid()) {
   1472      Ldp(dst0, dst1, MemOperand(GetStackPointer64(), offset));
   1473    } else {
   1474      Ldr(dst0, MemOperand(GetStackPointer64(), offset));
   1475    }
   1476    offset += 2 * reg_size;
   1477  }
   1478 
   1479  // Load the two registers at the bottom and drop the stack pointer.
   1480  if (bottom_0.IsValid() && bottom_1.IsValid()) {
   1481    Ldp(bottom_0, bottom_1, MemOperand(GetStackPointer64(), size, PostIndex));
   1482  } else if (bottom_0.IsValid()) {
   1483    Ldr(bottom_0, MemOperand(GetStackPointer64(), size, PostIndex));
   1484  }
   1485 }
   1486 
   1487 
   1488 void MacroAssembler::PushMultipleTimes(int count, Register src) {
   1489  int size = src.SizeInBytes();
   1490 
   1491  PrepareForPush(count, size);
   1492  // Push up to four registers at a time if possible because if the current
   1493  // stack pointer is sp and the register size is 32, registers must be pushed
   1494  // in blocks of four in order to maintain the 16-byte alignment for sp.
   1495  while (count >= 4) {
   1496    PushHelper(4, size, src, src, src, src);
   1497    count -= 4;
   1498  }
   1499  if (count >= 2) {
   1500    PushHelper(2, size, src, src, NoReg, NoReg);
   1501    count -= 2;
   1502  }
   1503  if (count == 1) {
   1504    PushHelper(1, size, src, NoReg, NoReg, NoReg);
   1505    count -= 1;
   1506  }
   1507  VIXL_ASSERT(count == 0);
   1508 }
   1509 
   1510 
   1511 void MacroAssembler::PushHelper(int count, int size,
   1512                                const CPURegister& src0,
   1513                                const CPURegister& src1,
   1514                                const CPURegister& src2,
   1515                                const CPURegister& src3) {
   1516  // Ensure that we don't unintentionally modify scratch or debug registers.
   1517  // Worst case for size is 2 stp.
   1518  InstructionAccurateScope scope(this, 2,
   1519                                 InstructionAccurateScope::kMaximumSize);
   1520 
   1521  VIXL_ASSERT(AreSameSizeAndType(src0, src1, src2, src3));
   1522  VIXL_ASSERT(size == src0.SizeInBytes());
   1523 
   1524  // Pushing the stack pointer has unexpected behavior. See PushStackPointer().
   1525  VIXL_ASSERT(!src0.Is(GetStackPointer64()) && !src0.Is(sp));
   1526  VIXL_ASSERT(!src1.Is(GetStackPointer64()) && !src1.Is(sp));
   1527  VIXL_ASSERT(!src2.Is(GetStackPointer64()) && !src2.Is(sp));
   1528  VIXL_ASSERT(!src3.Is(GetStackPointer64()) && !src3.Is(sp));
   1529 
   1530  // The JS engine should never push 4 bytes.
   1531  VIXL_ASSERT(size >= 8);
   1532 
   1533  // When pushing multiple registers, the store order is chosen such that
   1534  // Push(a, b) is equivalent to Push(a) followed by Push(b).
   1535  switch (count) {
   1536    case 1:
   1537      VIXL_ASSERT(src1.IsNone() && src2.IsNone() && src3.IsNone());
   1538      str(src0, MemOperand(GetStackPointer64(), -1 * size, PreIndex));
   1539      break;
   1540    case 2:
   1541      VIXL_ASSERT(src2.IsNone() && src3.IsNone());
   1542      stp(src1, src0, MemOperand(GetStackPointer64(), -2 * size, PreIndex));
   1543      break;
   1544    case 3:
   1545      VIXL_ASSERT(src3.IsNone());
   1546      stp(src2, src1, MemOperand(GetStackPointer64(), -3 * size, PreIndex));
   1547      str(src0, MemOperand(GetStackPointer64(), 2 * size));
   1548      break;
   1549    case 4:
   1550      // Skip over 4 * size, then fill in the gap. This allows four W registers
   1551      // to be pushed using sp, whilst maintaining 16-byte alignment for sp at
   1552      // all times.
   1553      stp(src3, src2, MemOperand(GetStackPointer64(), -4 * size, PreIndex));
   1554      stp(src1, src0, MemOperand(GetStackPointer64(), 2 * size));
   1555      break;
   1556    default:
   1557      VIXL_UNREACHABLE();
   1558  }
   1559 }
   1560 
   1561 
   1562 void MacroAssembler::PopHelper(int count, int size,
   1563                               const CPURegister& dst0,
   1564                               const CPURegister& dst1,
   1565                               const CPURegister& dst2,
   1566                               const CPURegister& dst3) {
   1567  // Ensure that we don't unintentionally modify scratch or debug registers.
   1568  // Worst case for size is 2 ldp.
   1569  InstructionAccurateScope scope(this, 2,
   1570                                 InstructionAccurateScope::kMaximumSize);
   1571 
   1572  VIXL_ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3));
   1573  VIXL_ASSERT(size == dst0.SizeInBytes());
   1574 
   1575  // When popping multiple registers, the load order is chosen such that
   1576  // Pop(a, b) is equivalent to Pop(a) followed by Pop(b).
   1577  switch (count) {
   1578    case 1:
   1579      VIXL_ASSERT(dst1.IsNone() && dst2.IsNone() && dst3.IsNone());
   1580      ldr(dst0, MemOperand(GetStackPointer64(), 1 * size, PostIndex));
   1581      break;
   1582    case 2:
   1583      VIXL_ASSERT(dst2.IsNone() && dst3.IsNone());
   1584      ldp(dst0, dst1, MemOperand(GetStackPointer64(), 2 * size, PostIndex));
   1585      break;
   1586    case 3:
   1587      VIXL_ASSERT(dst3.IsNone());
   1588      ldr(dst2, MemOperand(GetStackPointer64(), 2 * size));
   1589      ldp(dst0, dst1, MemOperand(GetStackPointer64(), 3 * size, PostIndex));
   1590      break;
   1591    case 4:
   1592      // Load the higher addresses first, then load the lower addresses and skip
   1593      // the whole block in the second instruction. This allows four W registers
   1594      // to be popped using sp, whilst maintaining 16-byte alignment for sp at
   1595      // all times.
   1596      ldp(dst2, dst3, MemOperand(GetStackPointer64(), 2 * size));
   1597      ldp(dst0, dst1, MemOperand(GetStackPointer64(), 4 * size, PostIndex));
   1598      break;
   1599    default:
   1600      VIXL_UNREACHABLE();
   1601  }
   1602 }
   1603 
   1604 
   1605 void MacroAssembler::PrepareForPush(int count, int size) {
   1606  if (sp.Is(GetStackPointer64())) {
   1607    // If the current stack pointer is sp, then it must be aligned to 16 bytes
   1608    // on entry and the total size of the specified registers must also be a
   1609    // multiple of 16 bytes.
   1610    VIXL_ASSERT((count * size) % 16 == 0);
   1611  } else {
   1612    // Even if the current stack pointer is not the system stack pointer (sp),
   1613    // the system stack pointer will still be modified in order to comply with
   1614    // ABI rules about accessing memory below the system stack pointer.
   1615    BumpSystemStackPointer(count * size);
   1616  }
   1617 }
   1618 
   1619 
   1620 void MacroAssembler::PrepareForPop(int count, int size) {
   1621  USE(count, size);
   1622  if (sp.Is(GetStackPointer64())) {
   1623    // If the current stack pointer is sp, then it must be aligned to 16 bytes
   1624    // on entry and the total size of the specified registers must also be a
   1625    // multiple of 16 bytes.
   1626    VIXL_ASSERT((count * size) % 16 == 0);
   1627  }
   1628 }
   1629 
   1630 void MacroAssembler::Poke(const Register& src, const Operand& offset) {
   1631  if (offset.IsImmediate()) {
   1632    VIXL_ASSERT(offset.immediate() >= 0);
   1633  }
   1634 
   1635  Str(src, MemOperand(GetStackPointer64(), offset));
   1636 }
   1637 
   1638 
   1639 void MacroAssembler::Peek(const Register& dst, const Operand& offset) {
   1640  if (offset.IsImmediate()) {
   1641    VIXL_ASSERT(offset.immediate() >= 0);
   1642  }
   1643 
   1644  Ldr(dst, MemOperand(GetStackPointer64(), offset));
   1645 }
   1646 
   1647 
   1648 void MacroAssembler::Claim(const Operand& size) {
   1649 
   1650  if (size.IsZero()) {
   1651    return;
   1652  }
   1653 
   1654  if (size.IsImmediate()) {
   1655    VIXL_ASSERT(size.immediate() > 0);
   1656    if (sp.Is(GetStackPointer64())) {
   1657      VIXL_ASSERT((size.immediate() % 16) == 0);
   1658    }
   1659  }
   1660 
   1661  Sub(GetStackPointer64(), GetStackPointer64(), size);
   1662 
   1663  // Make sure the real stack pointer reflects the claimed stack space.
   1664  // We can't use stack memory below the stack pointer, it could be clobbered by
   1665  // interupts and signal handlers.
   1666  if (!sp.Is(GetStackPointer64())) {
   1667    Mov(sp, GetStackPointer64());
   1668  }
   1669 }
   1670 
   1671 
   1672 void MacroAssembler::Drop(const Operand& size) {
   1673 
   1674  if (size.IsZero()) {
   1675    return;
   1676  }
   1677 
   1678  if (size.IsImmediate()) {
   1679    VIXL_ASSERT(size.immediate() > 0);
   1680    if (sp.Is(GetStackPointer64())) {
   1681      VIXL_ASSERT((size.immediate() % 16) == 0);
   1682    }
   1683  }
   1684 
   1685  Add(GetStackPointer64(), GetStackPointer64(), size);
   1686 }
   1687 
   1688 
   1689 void MacroAssembler::PushCalleeSavedRegisters() {
   1690  // Ensure that the macro-assembler doesn't use any scratch registers.
   1691  // 10 stp will be emitted.
   1692  // TODO(all): Should we use GetCalleeSaved and SavedFP.
   1693  InstructionAccurateScope scope(this, 10);
   1694 
   1695  // This method must not be called unless the current stack pointer is sp.
   1696  VIXL_ASSERT(sp.Is(GetStackPointer64()));
   1697 
   1698  MemOperand tos(sp, -2 * static_cast<int>(kXRegSizeInBytes), PreIndex);
   1699 
   1700  stp(x29, x30, tos);
   1701  stp(x27, x28, tos);
   1702  stp(x25, x26, tos);
   1703  stp(x23, x24, tos);
   1704  stp(x21, x22, tos);
   1705  stp(x19, x20, tos);
   1706 
   1707  stp(d14, d15, tos);
   1708  stp(d12, d13, tos);
   1709  stp(d10, d11, tos);
   1710  stp(d8, d9, tos);
   1711 }
   1712 
   1713 
   1714 void MacroAssembler::PopCalleeSavedRegisters() {
   1715  // Ensure that the macro-assembler doesn't use any scratch registers.
   1716  // 10 ldp will be emitted.
   1717  // TODO(all): Should we use GetCalleeSaved and SavedFP.
   1718  InstructionAccurateScope scope(this, 10);
   1719 
   1720  // This method must not be called unless the current stack pointer is sp.
   1721  VIXL_ASSERT(sp.Is(GetStackPointer64()));
   1722 
   1723  MemOperand tos(sp, 2 * kXRegSizeInBytes, PostIndex);
   1724 
   1725  ldp(d8, d9, tos);
   1726  ldp(d10, d11, tos);
   1727  ldp(d12, d13, tos);
   1728  ldp(d14, d15, tos);
   1729 
   1730  ldp(x19, x20, tos);
   1731  ldp(x21, x22, tos);
   1732  ldp(x23, x24, tos);
   1733  ldp(x25, x26, tos);
   1734  ldp(x27, x28, tos);
   1735  ldp(x29, x30, tos);
   1736 }
   1737 
   1738 void MacroAssembler::LoadCPURegList(CPURegList registers,
   1739                                    const MemOperand& src) {
   1740  LoadStoreCPURegListHelper(kLoad, registers, src);
   1741 }
   1742 
   1743 void MacroAssembler::StoreCPURegList(CPURegList registers,
   1744                                     const MemOperand& dst) {
   1745  LoadStoreCPURegListHelper(kStore, registers, dst);
   1746 }
   1747 
   1748 
   1749 void MacroAssembler::LoadStoreCPURegListHelper(LoadStoreCPURegListAction op,
   1750                                               CPURegList registers,
   1751                                               const MemOperand& mem) {
   1752  // We do not handle pre-indexing or post-indexing.
   1753  VIXL_ASSERT(!(mem.IsPreIndex() || mem.IsPostIndex()));
   1754  VIXL_ASSERT(!registers.Overlaps(tmp_list_));
   1755  VIXL_ASSERT(!registers.Overlaps(fptmp_list_));
   1756  VIXL_ASSERT(!registers.IncludesAliasOf(sp));
   1757 
   1758  UseScratchRegisterScope temps(this);
   1759 
   1760  MemOperand loc = BaseMemOperandForLoadStoreCPURegList(registers,
   1761                                                        mem,
   1762                                                        &temps);
   1763 
   1764  while (registers.Count() >= 2) {
   1765    const CPURegister& dst0 = registers.PopLowestIndex();
   1766    const CPURegister& dst1 = registers.PopLowestIndex();
   1767    if (op == kStore) {
   1768      Stp(dst0, dst1, loc);
   1769    } else {
   1770      VIXL_ASSERT(op == kLoad);
   1771      Ldp(dst0, dst1, loc);
   1772    }
   1773    loc.AddOffset(2 * registers.RegisterSizeInBytes());
   1774  }
   1775  if (!registers.IsEmpty()) {
   1776    if (op == kStore) {
   1777      Str(registers.PopLowestIndex(), loc);
   1778    } else {
   1779      VIXL_ASSERT(op == kLoad);
   1780      Ldr(registers.PopLowestIndex(), loc);
   1781    }
   1782  }
   1783 }
   1784 
   1785 MemOperand MacroAssembler::BaseMemOperandForLoadStoreCPURegList(
   1786    const CPURegList& registers,
   1787    const MemOperand& mem,
   1788    UseScratchRegisterScope* scratch_scope) {
   1789  // If necessary, pre-compute the base address for the accesses.
   1790  if (mem.IsRegisterOffset()) {
   1791    Register reg_base = scratch_scope->AcquireX();
   1792    ComputeAddress(reg_base, mem);
   1793    return MemOperand(reg_base);
   1794 
   1795  } else if (mem.IsImmediateOffset()) {
   1796    int reg_size = registers.RegisterSizeInBytes();
   1797    int total_size = registers.TotalSizeInBytes();
   1798    int64_t min_offset = mem.offset();
   1799    int64_t max_offset = mem.offset() + std::max(0, total_size - 2 * reg_size);
   1800    if ((registers.Count() >= 2) &&
   1801        (!Assembler::IsImmLSPair(min_offset, WhichPowerOf2(reg_size)) ||
   1802         !Assembler::IsImmLSPair(max_offset, WhichPowerOf2(reg_size)))) {
   1803      Register reg_base = scratch_scope->AcquireX();
   1804      ComputeAddress(reg_base, mem);
   1805      return MemOperand(reg_base);
   1806    }
   1807  }
   1808 
   1809  return mem;
   1810 }
   1811 
   1812 void MacroAssembler::BumpSystemStackPointer(const Operand& space) {
   1813  VIXL_ASSERT(!sp.Is(GetStackPointer64()));
   1814  // TODO: Several callers rely on this not using scratch registers, so we use
   1815  // the assembler directly here. However, this means that large immediate
   1816  // values of 'space' cannot be handled.
   1817  InstructionAccurateScope scope(this, 1);
   1818  sub(sp, GetStackPointer64(), space);
   1819 }
   1820 
   1821 
   1822 void MacroAssembler::Trace(TraceParameters parameters, TraceCommand command) {
   1823 
   1824 #ifdef JS_SIMULATOR_ARM64
   1825  // The arguments to the trace pseudo instruction need to be contiguous in
   1826  // memory, so make sure we don't try to emit a literal pool.
   1827  InstructionAccurateScope scope(this, kTraceLength / kInstructionSize);
   1828 
   1829  Label start;
   1830  bind(&start);
   1831 
   1832  // Refer to simulator-a64.h for a description of the marker and its
   1833  // arguments.
   1834  hlt(kTraceOpcode);
   1835 
   1836  // VIXL_ASSERT(SizeOfCodeGeneratedSince(&start) == kTraceParamsOffset);
   1837  dc32(parameters);
   1838 
   1839  // VIXL_ASSERT(SizeOfCodeGeneratedSince(&start) == kTraceCommandOffset);
   1840  dc32(command);
   1841 #else
   1842  // Emit nothing on real hardware.
   1843  USE(parameters, command);
   1844 #endif
   1845 }
   1846 
   1847 
   1848 void MacroAssembler::Log(TraceParameters parameters) {
   1849 
   1850 #ifdef JS_SIMULATOR_ARM64
   1851  // The arguments to the log pseudo instruction need to be contiguous in
   1852  // memory, so make sure we don't try to emit a literal pool.
   1853  InstructionAccurateScope scope(this, kLogLength / kInstructionSize);
   1854 
   1855  Label start;
   1856  bind(&start);
   1857 
   1858  // Refer to simulator-a64.h for a description of the marker and its
   1859  // arguments.
   1860  hlt(kLogOpcode);
   1861 
   1862  // VIXL_ASSERT(SizeOfCodeGeneratedSince(&start) == kLogParamsOffset);
   1863  dc32(parameters);
   1864 #else
   1865  // Emit nothing on real hardware.
   1866  USE(parameters);
   1867 #endif
   1868 }
   1869 
   1870 
   1871 void MacroAssembler::EnableInstrumentation() {
   1872  VIXL_ASSERT(!isprint(InstrumentStateEnable));
   1873  InstructionAccurateScope scope(this, 1);
   1874  movn(xzr, InstrumentStateEnable);
   1875 }
   1876 
   1877 
   1878 void MacroAssembler::DisableInstrumentation() {
   1879  VIXL_ASSERT(!isprint(InstrumentStateDisable));
   1880  InstructionAccurateScope scope(this, 1);
   1881  movn(xzr, InstrumentStateDisable);
   1882 }
   1883 
   1884 
   1885 void MacroAssembler::AnnotateInstrumentation(const char* marker_name) {
   1886  VIXL_ASSERT(strlen(marker_name) == 2);
   1887 
   1888  // We allow only printable characters in the marker names. Unprintable
   1889  // characters are reserved for controlling features of the instrumentation.
   1890  VIXL_ASSERT(isprint(marker_name[0]) && isprint(marker_name[1]));
   1891 
   1892  InstructionAccurateScope scope(this, 1);
   1893  movn(xzr, (marker_name[1] << 8) | marker_name[0]);
   1894 }
   1895 
   1896 
   1897 void UseScratchRegisterScope::Open(MacroAssembler* masm) {
   1898  VIXL_ASSERT(!initialised_);
   1899  available_ = masm->TmpList();
   1900  availablefp_ = masm->FPTmpList();
   1901  old_available_ = available_->list();
   1902  old_availablefp_ = availablefp_->list();
   1903  VIXL_ASSERT(available_->type() == CPURegister::kRegister);
   1904  VIXL_ASSERT(availablefp_->type() == CPURegister::kVRegister);
   1905 #ifdef DEBUG
   1906  initialised_ = true;
   1907 #endif
   1908 }
   1909 
   1910 
   1911 void UseScratchRegisterScope::Close() {
   1912  if (available_) {
   1913    available_->set_list(old_available_);
   1914    available_ = NULL;
   1915  }
   1916  if (availablefp_) {
   1917    availablefp_->set_list(old_availablefp_);
   1918    availablefp_ = NULL;
   1919  }
   1920 #ifdef DEBUG
   1921  initialised_ = false;
   1922 #endif
   1923 }
   1924 
   1925 
   1926 UseScratchRegisterScope::UseScratchRegisterScope(MacroAssembler* masm) {
   1927 #ifdef DEBUG
   1928  initialised_ = false;
   1929 #endif
   1930  Open(masm);
   1931 }
   1932 
   1933 // This allows deferred (and optional) initialisation of the scope.
   1934 UseScratchRegisterScope::UseScratchRegisterScope()
   1935    : available_(NULL), availablefp_(NULL),
   1936      old_available_(0), old_availablefp_(0) {
   1937 #ifdef DEBUG
   1938  initialised_ = false;
   1939 #endif
   1940 }
   1941 
   1942 UseScratchRegisterScope::~UseScratchRegisterScope() {
   1943  Close();
   1944 }
   1945 
   1946 
   1947 bool UseScratchRegisterScope::IsAvailable(const CPURegister& reg) const {
   1948  return available_->IncludesAliasOf(reg) || availablefp_->IncludesAliasOf(reg);
   1949 }
   1950 
   1951 bool UseScratchRegisterScope::HasAvailableRegister() const {
   1952  return !available_->IsEmpty();
   1953 }
   1954 
   1955 Register UseScratchRegisterScope::AcquireSameSizeAs(const Register& reg) {
   1956  int code = AcquireNextAvailable(available_).code();
   1957  return Register(code, reg.size());
   1958 }
   1959 
   1960 
   1961 FPRegister UseScratchRegisterScope::AcquireSameSizeAs(const FPRegister& reg) {
   1962  int code = AcquireNextAvailable(availablefp_).code();
   1963  return FPRegister(code, reg.size());
   1964 }
   1965 
   1966 
   1967 void UseScratchRegisterScope::Release(const CPURegister& reg) {
   1968  VIXL_ASSERT(initialised_);
   1969  if (reg.IsRegister()) {
   1970    ReleaseByCode(available_, reg.code());
   1971  } else if (reg.IsFPRegister()) {
   1972    ReleaseByCode(availablefp_, reg.code());
   1973  } else {
   1974    VIXL_ASSERT(reg.IsNone());
   1975  }
   1976 }
   1977 
   1978 
   1979 void UseScratchRegisterScope::Include(const CPURegList& list) {
   1980  VIXL_ASSERT(initialised_);
   1981  if (list.type() == CPURegister::kRegister) {
   1982    // Make sure that neither sp nor xzr are included the list.
   1983    IncludeByRegList(available_, list.list() & ~(xzr.Bit() | sp.Bit()));
   1984  } else {
   1985    VIXL_ASSERT(list.type() == CPURegister::kVRegister);
   1986    IncludeByRegList(availablefp_, list.list());
   1987  }
   1988 }
   1989 
   1990 
   1991 void UseScratchRegisterScope::Include(const Register& reg1,
   1992                                      const Register& reg2,
   1993                                      const Register& reg3,
   1994                                      const Register& reg4) {
   1995  VIXL_ASSERT(initialised_);
   1996  RegList include = reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit();
   1997  // Make sure that neither sp nor xzr are included the list.
   1998  include &= ~(xzr.Bit() | sp.Bit());
   1999 
   2000  IncludeByRegList(available_, include);
   2001 }
   2002 
   2003 
   2004 void UseScratchRegisterScope::Include(const FPRegister& reg1,
   2005                                      const FPRegister& reg2,
   2006                                      const FPRegister& reg3,
   2007                                      const FPRegister& reg4) {
   2008  RegList include = reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit();
   2009  IncludeByRegList(availablefp_, include);
   2010 }
   2011 
   2012 
   2013 void UseScratchRegisterScope::Exclude(const CPURegList& list) {
   2014  if (list.type() == CPURegister::kRegister) {
   2015    ExcludeByRegList(available_, list.list());
   2016  } else {
   2017    VIXL_ASSERT(list.type() == CPURegister::kVRegister);
   2018    ExcludeByRegList(availablefp_, list.list());
   2019  }
   2020 }
   2021 
   2022 
   2023 void UseScratchRegisterScope::Exclude(const Register& reg1,
   2024                                      const Register& reg2,
   2025                                      const Register& reg3,
   2026                                      const Register& reg4) {
   2027  RegList exclude = reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit();
   2028  ExcludeByRegList(available_, exclude);
   2029 }
   2030 
   2031 
   2032 void UseScratchRegisterScope::Exclude(const FPRegister& reg1,
   2033                                      const FPRegister& reg2,
   2034                                      const FPRegister& reg3,
   2035                                      const FPRegister& reg4) {
   2036  RegList excludefp = reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit();
   2037  ExcludeByRegList(availablefp_, excludefp);
   2038 }
   2039 
   2040 
   2041 void UseScratchRegisterScope::Exclude(const CPURegister& reg1,
   2042                                      const CPURegister& reg2,
   2043                                      const CPURegister& reg3,
   2044                                      const CPURegister& reg4) {
   2045  RegList exclude = 0;
   2046  RegList excludefp = 0;
   2047 
   2048  const CPURegister regs[] = {reg1, reg2, reg3, reg4};
   2049 
   2050  for (unsigned i = 0; i < (sizeof(regs) / sizeof(regs[0])); i++) {
   2051    if (regs[i].IsRegister()) {
   2052      exclude |= regs[i].Bit();
   2053    } else if (regs[i].IsFPRegister()) {
   2054      excludefp |= regs[i].Bit();
   2055    } else {
   2056      VIXL_ASSERT(regs[i].IsNone());
   2057    }
   2058  }
   2059 
   2060  ExcludeByRegList(available_, exclude);
   2061  ExcludeByRegList(availablefp_, excludefp);
   2062 }
   2063 
   2064 
   2065 void UseScratchRegisterScope::ExcludeAll() {
   2066  ExcludeByRegList(available_, available_->list());
   2067  ExcludeByRegList(availablefp_, availablefp_->list());
   2068 }
   2069 
   2070 
   2071 CPURegister UseScratchRegisterScope::AcquireNextAvailable(
   2072    CPURegList* available) {
   2073  VIXL_CHECK(!available->IsEmpty());
   2074  CPURegister result = available->PopLowestIndex();
   2075  VIXL_ASSERT(!AreAliased(result, xzr, sp));
   2076  return result;
   2077 }
   2078 
   2079 
   2080 void UseScratchRegisterScope::ReleaseByCode(CPURegList* available, int code) {
   2081  ReleaseByRegList(available, static_cast<RegList>(1) << code);
   2082 }
   2083 
   2084 
   2085 void UseScratchRegisterScope::ReleaseByRegList(CPURegList* available,
   2086                                               RegList regs) {
   2087  available->set_list(available->list() | regs);
   2088 }
   2089 
   2090 
   2091 void UseScratchRegisterScope::IncludeByRegList(CPURegList* available,
   2092                                               RegList regs) {
   2093  available->set_list(available->list() | regs);
   2094 }
   2095 
   2096 
   2097 void UseScratchRegisterScope::ExcludeByRegList(CPURegList* available,
   2098                                               RegList exclude) {
   2099  available->set_list(available->list() & ~exclude);
   2100 }
   2101 
   2102 }  // namespace vixl