tor-browser

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

LIR.cpp (23367B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "jit/LIR.h"
      8 
      9 #include "mozilla/ScopeExit.h"
     10 
     11 #include <type_traits>
     12 
     13 #include "jit/JitSpewer.h"
     14 #include "jit/MIR-wasm.h"
     15 #include "jit/MIR.h"
     16 #include "jit/MIRGenerator.h"
     17 #include "js/Printf.h"
     18 #include "util/Unicode.h"
     19 
     20 using namespace js;
     21 using namespace js::jit;
     22 
     23 const char* const js::jit::LIROpNames[] = {
     24 #define OPNAME(op, ...) #op,
     25    LIR_OPCODE_LIST(OPNAME)
     26 #undef OPNAME
     27 };
     28 
     29 LIRGraph::LIRGraph(MIRGraph* mir)
     30    : constantPool_(mir->alloc()),
     31      constantPoolMap_(mir->alloc()),
     32      numVirtualRegisters_(0),
     33      numInstructions_(1),  // First id is 1.
     34      localSlotsSize_(0),
     35      argumentSlotCount_(0),
     36      extraSafepointUses_(0),
     37      mir_(*mir) {}
     38 
     39 bool LIRGraph::addConstantToPool(const Value& v, uint32_t* index) {
     40  ConstantPoolMap::AddPtr p = constantPoolMap_.lookupForAdd(v);
     41  if (p) {
     42    *index = p->value();
     43    return true;
     44  }
     45  *index = constantPool_.length();
     46  return constantPool_.append(v) && constantPoolMap_.add(p, v, *index);
     47 }
     48 
     49 #ifdef JS_JITSPEW
     50 void LIRGraph::dump(GenericPrinter& out) {
     51  for (size_t i = 0; i < numBlocks(); i++) {
     52    getBlock(i)->dump(out);
     53    out.printf("\n");
     54  }
     55 }
     56 
     57 void LIRGraph::dump() {
     58  Fprinter out(stderr);
     59  dump(out);
     60  out.finish();
     61 }
     62 #endif
     63 
     64 LBlock::LBlock(MBasicBlock* from)
     65    : block_(from),
     66      entryMoveGroup_(nullptr),
     67      exitMoveGroup_(nullptr),
     68      isOutOfLine_(false) {
     69  from->assignLir(this);
     70 
     71  // If branch hinting is enabled, and this block is unlikely to be executed,
     72  // it will be generated out of line.
     73  if (from->info().branchHintingEnabled() && from->isUnlikelyFrequency()) {
     74    isOutOfLine_ = true;
     75  }
     76 }
     77 
     78 bool LBlock::init(TempAllocator& alloc) {
     79  // Count the number of LPhis we'll need.
     80  size_t numLPhis = 0;
     81  for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
     82    MPhi* phi = *i;
     83    switch (phi->type()) {
     84      case MIRType::Value:
     85        numLPhis += BOX_PIECES;
     86        break;
     87      case MIRType::Int64:
     88        numLPhis += INT64_PIECES;
     89        break;
     90      default:
     91        numLPhis += 1;
     92        break;
     93    }
     94  }
     95 
     96  // Allocate space for the LPhis.
     97  if (!phis_.init(alloc, numLPhis)) {
     98    return false;
     99  }
    100 
    101  // For each MIR phi, set up LIR phis as appropriate. We'll fill in their
    102  // operands on each incoming edge, and set their definitions at the start of
    103  // their defining block.
    104  size_t phiIndex = 0;
    105  size_t numPreds = block_->numPredecessors();
    106  for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
    107    MPhi* phi = *i;
    108    MOZ_ASSERT(phi->numOperands() == numPreds);
    109 
    110    int numPhis;
    111    switch (phi->type()) {
    112      case MIRType::Value:
    113        numPhis = BOX_PIECES;
    114        break;
    115      case MIRType::Int64:
    116        numPhis = INT64_PIECES;
    117        break;
    118      default:
    119        numPhis = 1;
    120        break;
    121    }
    122    for (int i = 0; i < numPhis; i++) {
    123      LAllocation* inputs = alloc.allocateArray<LAllocation>(numPreds);
    124      if (!inputs) {
    125        return false;
    126      }
    127 
    128      void* addr = &phis_[phiIndex++];
    129      LPhi* lphi = new (addr) LPhi(phi, inputs);
    130      lphi->setBlock(this);
    131    }
    132  }
    133  return true;
    134 }
    135 
    136 const LInstruction* LBlock::firstInstructionWithId() const {
    137  for (LInstructionIterator i(instructions_.begin()); i != instructions_.end();
    138       ++i) {
    139    if (i->id()) {
    140      return *i;
    141    }
    142  }
    143  return 0;
    144 }
    145 
    146 LMoveGroup* LBlock::getEntryMoveGroup(TempAllocator& alloc) {
    147  if (entryMoveGroup_) {
    148    return entryMoveGroup_;
    149  }
    150  entryMoveGroup_ = LMoveGroup::New(alloc);
    151  insertBefore(*begin(), entryMoveGroup_);
    152  return entryMoveGroup_;
    153 }
    154 
    155 LMoveGroup* LBlock::getExitMoveGroup(TempAllocator& alloc) {
    156  if (exitMoveGroup_) {
    157    return exitMoveGroup_;
    158  }
    159  exitMoveGroup_ = LMoveGroup::New(alloc);
    160  insertBefore(*rbegin(), exitMoveGroup_);
    161  return exitMoveGroup_;
    162 }
    163 
    164 LBlock* LBlock::isMoveGroupsThenGoto() {
    165  if (mir()->isLoopHeader()) {
    166    return nullptr;
    167  }
    168  auto riter = rbegin();
    169  if (!riter->isGoto()) {
    170    return nullptr;
    171  }
    172  riter++;
    173  // This loop doesn't iterate much.  Its highest trip-count for all of
    174  // JetStream3 is 3.
    175  while (riter != rend()) {
    176    if (!(*riter)->isMoveGroup()) {
    177      return nullptr;
    178    }
    179    riter++;
    180  }
    181  LGoto* ins = rbegin()->toGoto();
    182  MOZ_ASSERT(ins->numSuccessors() == 1);
    183  return ins->getSuccessor(0)->lir();
    184 }
    185 
    186 #ifdef JS_JITSPEW
    187 void LBlock::dump(GenericPrinter& out) {
    188  out.printf("block%u:\n", mir()->id());
    189  for (size_t i = 0; i < numPhis(); ++i) {
    190    out.printf("  ");
    191    getPhi(i)->dump(out);
    192    out.printf("\n");
    193  }
    194  for (LInstructionIterator iter = begin(); iter != end(); iter++) {
    195    out.printf("  ");
    196    iter->dump(out);
    197    if (iter->safepoint()) {
    198      out.printf(" SAFEPOINT(0x%p) ", iter->safepoint());
    199    }
    200    out.printf("\n");
    201  }
    202 }
    203 
    204 void LBlock::dump() {
    205  Fprinter out(stderr);
    206  dump(out);
    207  out.finish();
    208 }
    209 #endif
    210 
    211 static size_t TotalOperandCount(LRecoverInfo* recoverInfo) {
    212  size_t accum = 0;
    213  for (LRecoverInfo::OperandIter it(recoverInfo); !it; ++it) {
    214    if (!it->isRecoveredOnBailout()) {
    215      accum++;
    216    }
    217  }
    218  return accum;
    219 }
    220 
    221 LRecoverInfo::LRecoverInfo(TempAllocator& alloc)
    222    : instructions_(alloc), recoverOffset_(INVALID_RECOVER_OFFSET) {}
    223 
    224 LRecoverInfo* LRecoverInfo::New(MIRGenerator* gen, MResumePoint* mir) {
    225  LRecoverInfo* recoverInfo = new (gen->alloc()) LRecoverInfo(gen->alloc());
    226  if (!recoverInfo || !recoverInfo->init(mir)) {
    227    return nullptr;
    228  }
    229 
    230  JitSpew(JitSpew_IonSnapshots, "Generating LIR recover info %p from MIR (%p)",
    231          (void*)recoverInfo, (void*)mir);
    232 
    233  return recoverInfo;
    234 }
    235 
    236 // de-virtualise MResumePoint::getOperand calls.
    237 template <typename Node>
    238 bool LRecoverInfo::appendOperands(Node* ins) {
    239  for (size_t i = 0, end = ins->numOperands(); i < end; i++) {
    240    MDefinition* def = ins->getOperand(i);
    241 
    242    // As there is no cycle in the data-flow (without MPhi), checking for
    243    // isInWorkList implies that the definition is already in the
    244    // instruction vector, and not processed by a caller of the current
    245    // function.
    246    if (def->isRecoveredOnBailout() && !def->isInWorklist()) {
    247      if (!appendDefinition(def)) {
    248        return false;
    249      }
    250    }
    251  }
    252 
    253  return true;
    254 }
    255 
    256 bool LRecoverInfo::appendDefinition(MDefinition* def) {
    257  MOZ_ASSERT(def->isRecoveredOnBailout());
    258  def->setInWorklist();
    259  auto clearWorklistFlagOnFailure =
    260      mozilla::MakeScopeExit([&] { def->setNotInWorklist(); });
    261 
    262  if (!appendOperands(def)) {
    263    return false;
    264  }
    265 
    266  if (!instructions_.append(def)) {
    267    return false;
    268  }
    269 
    270  clearWorklistFlagOnFailure.release();
    271  return true;
    272 }
    273 
    274 bool LRecoverInfo::appendResumePoint(MResumePoint* rp) {
    275  // Stores should be recovered first.
    276  if (!rp->storesEmpty()) {
    277    hasSideEffects_ = true;
    278  }
    279  for (auto iter(rp->storesBegin()), end(rp->storesEnd()); iter != end;
    280       ++iter) {
    281    if (!appendDefinition(iter->operand)) {
    282      return false;
    283    }
    284  }
    285 
    286  if (rp->caller() && !appendResumePoint(rp->caller())) {
    287    return false;
    288  }
    289 
    290  if (!appendOperands(rp)) {
    291    return false;
    292  }
    293 
    294  return instructions_.append(rp);
    295 }
    296 
    297 bool LRecoverInfo::init(MResumePoint* rp) {
    298  // Before exiting this function, remove temporary flags from all definitions
    299  // added in the vector.
    300  auto clearWorklistFlags = mozilla::MakeScopeExit([&] {
    301    for (MNode** it = begin(); it != end(); it++) {
    302      if (!(*it)->isDefinition()) {
    303        continue;
    304      }
    305      (*it)->toDefinition()->setNotInWorklist();
    306    }
    307  });
    308 
    309  // Sort operations in the order in which we need to restore the stack. This
    310  // implies that outer frames, as well as operations needed to recover the
    311  // current frame, are located before the current frame. The inner-most
    312  // resume point should be the last element in the list.
    313  if (!appendResumePoint(rp)) {
    314    return false;
    315  }
    316 
    317  MOZ_ASSERT(mir() == rp);
    318  return true;
    319 }
    320 
    321 LSnapshot::LSnapshot(LRecoverInfo* recoverInfo, BailoutKind kind)
    322    : slots_(nullptr),
    323      recoverInfo_(recoverInfo),
    324      snapshotOffset_(INVALID_SNAPSHOT_OFFSET),
    325      numSlots_(TotalOperandCount(recoverInfo) * BOX_PIECES),
    326      bailoutKind_(kind) {}
    327 
    328 bool LSnapshot::init(MIRGenerator* gen) {
    329  slots_ = gen->allocate<LAllocation>(numSlots_);
    330  return !!slots_;
    331 }
    332 
    333 LSnapshot* LSnapshot::New(MIRGenerator* gen, LRecoverInfo* recover,
    334                          BailoutKind kind) {
    335  LSnapshot* snapshot = new (gen->alloc()) LSnapshot(recover, kind);
    336  if (!snapshot || !snapshot->init(gen)) {
    337    return nullptr;
    338  }
    339 
    340  JitSpew(JitSpew_IonSnapshots, "Generating LIR snapshot %p from recover (%p)",
    341          (void*)snapshot, (void*)recover);
    342 
    343  return snapshot;
    344 }
    345 
    346 void LSnapshot::rewriteRecoveredInput(LUse input) {
    347  // Mark any operands to this snapshot with the same value as input as being
    348  // equal to the instruction's result.
    349  for (size_t i = 0; i < numEntries(); i++) {
    350    if (getEntry(i)->isUse() &&
    351        getEntry(i)->toUse()->virtualRegister() == input.virtualRegister()) {
    352      setEntry(i, LUse(input.virtualRegister(), LUse::RECOVERED_INPUT));
    353    }
    354  }
    355 }
    356 
    357 #ifdef JS_JITSPEW
    358 void LNode::printName(GenericPrinter& out, Opcode op) {
    359  static const char* const names[] = {
    360 #  define LIROP(x) #x,
    361      LIR_OPCODE_LIST(LIROP)
    362 #  undef LIROP
    363  };
    364  const char* name = names[uint32_t(op)];
    365  size_t len = strlen(name);
    366  for (size_t i = 0; i < len; i++) {
    367    out.printf("%c", unicode::ToLowerCase(name[i]));
    368  }
    369 }
    370 
    371 void LNode::printName(GenericPrinter& out) { printName(out, op()); }
    372 #endif
    373 
    374 bool LAllocation::aliases(const LAllocation& other) const {
    375  if (isFloatReg() && other.isFloatReg()) {
    376    return toFloatReg()->reg().aliases(other.toFloatReg()->reg());
    377  }
    378  return *this == other;
    379 }
    380 
    381 #ifdef JS_JITSPEW
    382 static const char* DefTypeName(LDefinition::Type type) {
    383  switch (type) {
    384    case LDefinition::GENERAL:
    385      return "g";
    386    case LDefinition::INT32:
    387      return "i";
    388    case LDefinition::OBJECT:
    389      return "o";
    390    case LDefinition::SLOTS:
    391      return "s";
    392    case LDefinition::WASM_ANYREF:
    393      return "wr";
    394    case LDefinition::WASM_STRUCT_DATA:
    395      return "wsd";
    396    case LDefinition::WASM_ARRAY_DATA:
    397      return "wad";
    398    case LDefinition::FLOAT32:
    399      return "f";
    400    case LDefinition::DOUBLE:
    401      return "d";
    402    case LDefinition::SIMD128:
    403      return "simd128";
    404    case LDefinition::STACKRESULTS:
    405      return "stackresults";
    406 #  ifdef JS_NUNBOX32
    407    case LDefinition::TYPE:
    408      return "t";
    409    case LDefinition::PAYLOAD:
    410      return "p";
    411 #  else
    412    case LDefinition::BOX:
    413      return "x";
    414 #  endif
    415  }
    416  MOZ_CRASH("Invalid type");
    417 }
    418 
    419 UniqueChars LDefinition::toString() const {
    420  AutoEnterOOMUnsafeRegion oomUnsafe;
    421 
    422  UniqueChars buf;
    423  if (isBogusTemp()) {
    424    buf = JS_smprintf("bogus");
    425  } else {
    426    buf = JS_smprintf("v%u<%s>", virtualRegister(), DefTypeName(type()));
    427    if (buf) {
    428      if (policy() == LDefinition::FIXED) {
    429        buf = JS_sprintf_append(std::move(buf), ":%s",
    430                                output()->toString().get());
    431      } else if (policy() == LDefinition::MUST_REUSE_INPUT) {
    432        buf = JS_sprintf_append(std::move(buf), ":tied(%u)", getReusedInput());
    433      }
    434    }
    435  }
    436 
    437  if (!buf) {
    438    oomUnsafe.crash("LDefinition::toString()");
    439  }
    440 
    441  return buf;
    442 }
    443 
    444 static UniqueChars PrintUse(const LUse* use) {
    445  switch (use->policy()) {
    446    case LUse::REGISTER:
    447      return JS_smprintf("v%u:R", use->virtualRegister());
    448    case LUse::FIXED:
    449      return JS_smprintf("v%u:F:%s", use->virtualRegister(),
    450                         AnyRegister::FromCode(use->registerCode()).name());
    451    case LUse::ANY:
    452      return JS_smprintf("v%u:A", use->virtualRegister());
    453    case LUse::KEEPALIVE:
    454      return JS_smprintf("v%u:KA", use->virtualRegister());
    455    case LUse::STACK:
    456      return JS_smprintf("v%u:S", use->virtualRegister());
    457    case LUse::RECOVERED_INPUT:
    458      return JS_smprintf("v%u:RI", use->virtualRegister());
    459    default:
    460      MOZ_CRASH("invalid use policy");
    461  }
    462 }
    463 
    464 UniqueChars LAllocation::toString() const {
    465  AutoEnterOOMUnsafeRegion oomUnsafe;
    466 
    467  UniqueChars buf;
    468  if (isBogus()) {
    469    buf = JS_smprintf("bogus");
    470  } else {
    471    switch (kind()) {
    472      case LAllocation::CONSTANT_VALUE:
    473      case LAllocation::CONSTANT_INDEX: {
    474        const MConstant* c = toConstant();
    475        switch (c->type()) {
    476          case MIRType::Int32:
    477            buf = JS_smprintf("%d", c->toInt32());
    478            break;
    479          case MIRType::Int64:
    480            buf = JS_smprintf("%" PRId64, c->toInt64());
    481            break;
    482          case MIRType::IntPtr:
    483            buf = JS_smprintf("%" PRIxPTR, c->toIntPtr());
    484            break;
    485          case MIRType::String:
    486            // If a JSContext is a available, output the actual string
    487            if (JSContext* cx = TlsContext.get()) {
    488              Sprinter spr(cx);
    489              if (!spr.init()) {
    490                oomUnsafe.crash("LAllocation::toString()");
    491              }
    492              spr.putString(cx, c->toString()->unwrap());
    493              buf = spr.release();
    494            } else {
    495              buf = JS_smprintf("string");
    496            }
    497            break;
    498          case MIRType::Symbol:
    499            buf = JS_smprintf("sym");
    500            break;
    501          case MIRType::Object:
    502          case MIRType::Null:
    503            buf = JS_smprintf("obj %p", c->toObjectOrNull());
    504            break;
    505          case MIRType::Shape:
    506            buf = JS_smprintf("shape");
    507            break;
    508          default:
    509            if (c->isTypeRepresentableAsDouble()) {
    510              buf = JS_smprintf("%g", c->numberToDouble());
    511            } else {
    512              buf = JS_smprintf("const");
    513            }
    514        }
    515      } break;
    516      case LAllocation::GPR:
    517        buf = JS_smprintf("%s", toGeneralReg()->reg().name());
    518        break;
    519      case LAllocation::FPU:
    520        buf = JS_smprintf("%s", toFloatReg()->reg().name());
    521        break;
    522      case LAllocation::STACK_SLOT:
    523        buf = JS_smprintf("stack:%u(%u)", toStackSlot()->slot(),
    524                          LStackSlot::ByteWidth(toStackSlot()->width()));
    525        break;
    526      case LAllocation::ARGUMENT_SLOT:
    527        buf = JS_smprintf("arg:%u", toArgument()->index());
    528        break;
    529      case LAllocation::STACK_AREA:
    530        buf = JS_smprintf("stackarea:%u+%u", toStackArea()->base(),
    531                          toStackArea()->size());
    532        break;
    533      case LAllocation::USE:
    534        buf = PrintUse(toUse());
    535        break;
    536      default:
    537        MOZ_CRASH("what?");
    538    }
    539  }
    540 
    541  if (!buf) {
    542    oomUnsafe.crash("LAllocation::toString()");
    543  }
    544 
    545  return buf;
    546 }
    547 
    548 void LAllocation::dump() const { fprintf(stderr, "%s\n", toString().get()); }
    549 
    550 void LDefinition::dump() const { fprintf(stderr, "%s\n", toString().get()); }
    551 
    552 template <typename T>
    553 static void PrintOperands(GenericPrinter& out, T* node) {
    554  size_t numOperands = node->numOperands();
    555 
    556  for (size_t i = 0; i < numOperands; i++) {
    557    out.printf(" (%s)", node->getOperand(i)->toString().get());
    558    if (i != numOperands - 1) {
    559      out.printf(",");
    560    }
    561  }
    562 }
    563 
    564 void LNode::printOperands(GenericPrinter& out) {
    565  if (isMoveGroup()) {
    566    toMoveGroup()->printOperands(out);
    567    return;
    568  }
    569  if (isInteger()) {
    570    out.printf(" (%d)", toInteger()->i32());
    571    return;
    572  }
    573  if (isInteger64()) {
    574    out.printf(" (%" PRId64 ")", toInteger64()->i64());
    575    return;
    576  }
    577 
    578  if (isPhi()) {
    579    PrintOperands(out, toPhi());
    580  } else {
    581    PrintOperands(out, toInstruction());
    582  }
    583 }
    584 #endif
    585 
    586 void LInstruction::assignSnapshot(LSnapshot* snapshot) {
    587  MOZ_ASSERT(!snapshot_);
    588  snapshot_ = snapshot;
    589 
    590 #ifdef JS_JITSPEW
    591  if (JitSpewEnabled(JitSpew_IonSnapshots)) {
    592    JitSpewHeader(JitSpew_IonSnapshots);
    593    Fprinter& out = JitSpewPrinter();
    594    out.printf("Assigning snapshot %p to instruction %p (", (void*)snapshot,
    595               (void*)this);
    596    printName(out);
    597    out.printf(")\n");
    598  }
    599 #endif
    600 }
    601 
    602 #ifdef JS_JITSPEW
    603 static size_t NumSuccessorsHelper(const LNode* ins) { return 0; }
    604 
    605 template <size_t Succs, size_t Operands, size_t Temps>
    606 static size_t NumSuccessorsHelper(
    607    const LControlInstructionHelper<Succs, Operands, Temps>* ins) {
    608  return Succs;
    609 }
    610 
    611 static size_t NumSuccessors(const LInstruction* ins) {
    612  switch (ins->op()) {
    613    default:
    614      MOZ_CRASH("Unexpected LIR op");
    615 #  define LIROP(x)         \
    616    case LNode::Opcode::x: \
    617      return NumSuccessorsHelper(ins->to##x());
    618      LIR_OPCODE_LIST(LIROP)
    619 #  undef LIROP
    620  }
    621 }
    622 
    623 static MBasicBlock* GetSuccessorHelper(const LNode* ins, size_t i) {
    624  MOZ_CRASH("Unexpected instruction with successors");
    625 }
    626 
    627 template <size_t Succs, size_t Operands, size_t Temps>
    628 static MBasicBlock* GetSuccessorHelper(
    629    const LControlInstructionHelper<Succs, Operands, Temps>* ins, size_t i) {
    630  return ins->getSuccessor(i);
    631 }
    632 
    633 static MBasicBlock* GetSuccessor(const LInstruction* ins, size_t i) {
    634  MOZ_ASSERT(i < NumSuccessors(ins));
    635 
    636  switch (ins->op()) {
    637    default:
    638      MOZ_CRASH("Unexpected LIR op");
    639 #  define LIROP(x)         \
    640    case LNode::Opcode::x: \
    641      return GetSuccessorHelper(ins->to##x(), i);
    642      LIR_OPCODE_LIST(LIROP)
    643 #  undef LIROP
    644  }
    645 }
    646 #endif
    647 
    648 #ifdef JS_JITSPEW
    649 void LNode::dump(GenericPrinter& out) {
    650  if (numDefs() != 0) {
    651    out.printf("{");
    652    for (size_t i = 0; i < numDefs(); i++) {
    653      const LDefinition* def =
    654          isPhi() ? toPhi()->getDef(i) : toInstruction()->getDef(i);
    655      out.printf("%s", def->toString().get());
    656      if (i != numDefs() - 1) {
    657        out.printf(", ");
    658      }
    659    }
    660    out.printf("} <- ");
    661  }
    662 
    663  printName(out);
    664  printOperands(out);
    665 
    666  if (isInstruction()) {
    667    LInstruction* ins = toInstruction();
    668    size_t numTemps = ins->numTemps();
    669    if (numTemps > 0) {
    670      out.printf(" t=(");
    671      for (size_t i = 0; i < numTemps; i++) {
    672        out.printf("%s", ins->getTemp(i)->toString().get());
    673        if (i != numTemps - 1) {
    674          out.printf(", ");
    675        }
    676      }
    677      out.printf(")");
    678    }
    679 
    680    size_t numSuccessors = NumSuccessors(ins);
    681    if (numSuccessors > 0) {
    682      out.printf(" s=(");
    683      for (size_t i = 0; i < numSuccessors; i++) {
    684        MBasicBlock* succ = GetSuccessor(ins, i);
    685        out.printf("block %u", succ->id());
    686        if (i != numSuccessors - 1) {
    687          out.printf(", ");
    688        }
    689      }
    690      out.printf(")");
    691    }
    692  }
    693 }
    694 
    695 void LNode::dump() {
    696  Fprinter out(stderr);
    697  dump(out);
    698  out.printf("\n");
    699  out.finish();
    700 }
    701 
    702 const char* LNode::getExtraName() const {
    703  switch (op()) {
    704    default:
    705      MOZ_CRASH("Unexpected LIR op");
    706 #  define LIROP(x)         \
    707    case LNode::Opcode::x: \
    708      return to##x()->extraName();
    709      LIR_OPCODE_LIST(LIROP)
    710 #  undef LIROP
    711  }
    712 }
    713 #endif
    714 
    715 void LInstruction::initSafepoint(TempAllocator& alloc) {
    716  MOZ_ASSERT(!safepoint_);
    717  safepoint_ = new (alloc) LSafepoint(alloc);
    718  MOZ_ASSERT(safepoint_);
    719 }
    720 
    721 bool LSafepoint::addGCAllocation(uint32_t vregId, LDefinition* def,
    722                                 LAllocation a) {
    723  switch (def->type()) {
    724    case LDefinition::OBJECT:
    725      return addGcPointer(a);
    726 
    727    case LDefinition::SLOTS:
    728      return addSlotsOrElementsPointer(a);
    729 
    730    case LDefinition::WASM_ANYREF:
    731      return addWasmAnyRef(a);
    732    case LDefinition::WASM_STRUCT_DATA:
    733      return addWasmStructDataPointer(a);
    734    case LDefinition::WASM_ARRAY_DATA:
    735      return addWasmArrayDataPointer(a);
    736 
    737 #ifdef JS_NUNBOX32
    738    case LDefinition::TYPE:
    739      return addNunboxPart(/* isType = */ true, vregId, a);
    740 
    741    case LDefinition::PAYLOAD:
    742      return addNunboxPart(/* isType = */ false, vregId, a);
    743 #else
    744    case LDefinition::BOX:
    745      return addBoxedValue(a);
    746 #endif
    747 
    748    case LDefinition::STACKRESULTS: {
    749      MOZ_ASSERT(a.isStackArea());
    750      for (auto iter = a.toStackArea()->results(); iter; iter.next()) {
    751        if (iter.isWasmAnyRef()) {
    752          if (!addWasmAnyRef(iter.alloc())) {
    753            return false;
    754          }
    755        }
    756      }
    757      return true;
    758    }
    759 
    760    case LDefinition::GENERAL:
    761    case LDefinition::INT32:
    762    case LDefinition::FLOAT32:
    763    case LDefinition::DOUBLE:
    764    case LDefinition::SIMD128:
    765      break;
    766  }
    767  MOZ_CRASH("Bad register type");
    768 }
    769 
    770 bool LMoveGroup::add(LAllocation from, LAllocation to, LDefinition::Type type) {
    771 #ifdef DEBUG
    772  MOZ_ASSERT(from != to);
    773  for (size_t i = 0; i < moves_.length(); i++) {
    774    MOZ_ASSERT(to != moves_[i].to());
    775  }
    776 
    777  // Check that SIMD moves are aligned according to ABI requirements.
    778  // clang-format off
    779 # ifdef ENABLE_WASM_SIMD
    780    // Alignment is not currently required for SIMD on x86/x64/arm64.  See also
    781    // CodeGeneratorShared::CodeGeneratorShared and in general everywhere
    782    // SimdMemoryAignment is used.  Likely, alignment requirements will return.
    783 #   if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || \
    784       defined(JS_CODEGEN_ARM64)
    785      // No need for any check on x86/x64/arm64.
    786 #   else
    787 #     error "Need to consider SIMD alignment on this target."
    788      // The following code may be of use if we need alignment checks on
    789      // some future target.
    790      //if (LDefinition(type).type() == LDefinition::SIMD128) {
    791      //  MOZ_ASSERT(from.isMemory() || from.isFloatReg());
    792      //  if (from.isMemory()) {
    793      //    if (from.isArgument()) {
    794      //      MOZ_ASSERT(from.toArgument()->index() % SimdMemoryAlignment == 0);
    795      //    } else {
    796      //      MOZ_ASSERT(from.toStackSlot()->slot() % SimdMemoryAlignment == 0);
    797      //    }
    798      //  }
    799      //  MOZ_ASSERT(to.isMemory() || to.isFloatReg());
    800      //  if (to.isMemory()) {
    801      //    if (to.isArgument()) {
    802      //      MOZ_ASSERT(to.toArgument()->index() % SimdMemoryAlignment == 0);
    803      //    } else {
    804      //      MOZ_ASSERT(to.toStackSlot()->slot() % SimdMemoryAlignment == 0);
    805      //    }
    806      //  }
    807      //}
    808 #   endif
    809 # endif
    810  // clang-format on
    811 
    812 #endif
    813  return moves_.append(LMove(from, to, type));
    814 }
    815 
    816 bool LMoveGroup::addAfter(LAllocation from, LAllocation to,
    817                          LDefinition::Type type) {
    818  // Transform the operands to this move so that performing the result
    819  // simultaneously with existing moves in the group will have the same
    820  // effect as if the original move took place after the existing moves.
    821 
    822  for (size_t i = 0; i < moves_.length(); i++) {
    823    if (moves_[i].to() == from) {
    824      from = moves_[i].from();
    825      break;
    826    }
    827  }
    828 
    829  if (from == to) {
    830    return true;
    831  }
    832 
    833  for (size_t i = 0; i < moves_.length(); i++) {
    834    if (to == moves_[i].to()) {
    835      moves_[i] = LMove(from, to, type);
    836      return true;
    837    }
    838  }
    839 
    840  return add(from, to, type);
    841 }
    842 
    843 #ifdef JS_JITSPEW
    844 void LMoveGroup::printOperands(GenericPrinter& out) {
    845  for (size_t i = 0; i < numMoves(); i++) {
    846    const LMove& move = getMove(i);
    847    out.printf(" [%s -> %s", move.from().toString().get(),
    848               move.to().toString().get());
    849    out.printf(", %s", DefTypeName(move.type()));
    850    out.printf("]");
    851    if (i != numMoves() - 1) {
    852      out.printf(",");
    853    }
    854  }
    855 }
    856 #endif
    857 
    858 #define LIROP(x)                              \
    859  static_assert(!std::is_polymorphic_v<L##x>, \
    860                "LIR instructions should not have virtual methods");
    861 LIR_OPCODE_LIST(LIROP)
    862 #undef LIROP