Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quellcode-Bibliothek Lowering.cpp

  Sprache: C
 

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "jit/Lowering.h"

#include "mozilla/DebugOnly.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/MathAlgorithms.h"

#include <type_traits>

#include "jit/ABIArgGenerator.h"
#include "jit/IonGenericCallStub.h"
#include "jit/IonOptimizationLevels.h"
#include "jit/JitSpewer.h"
#include "jit/LIR.h"
#include "jit/MacroAssembler.h"
#include "jit/MIR-wasm.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
#include "jit/SharedICRegisters.h"
#include "js/experimental/JitInfo.h"  // JSJitInfo
#include "util/Memory.h"
#include "wasm/WasmCodegenTypes.h"
#include "wasm/WasmFeatures.h"  // for wasm::ReportSimdAnalysis
#include "wasm/WasmInstanceData.h"

#include "jit/shared/Lowering-shared-inl.h"
#include "vm/BytecodeUtil-inl.h"

using namespace js;
using namespace jit;

using JS::GenericNaN;
using mozilla::DebugOnly;

LBoxAllocation LIRGenerator::useBoxFixedAtStart(MDefinition* mir,
                                                ValueOperand op) {
#if defined(JS_NUNBOX32)
  return useBoxFixed(mir, op.typeReg(), op.payloadReg(), true);
#elif defined(JS_PUNBOX64)
  return useBoxFixed(mir, op.valueReg(), op.scratchReg(), true);
#endif
}

LBoxAllocation LIRGenerator::useBoxAtStart(MDefinition* mir,
                                           LUse::Policy policy) {
  return useBox(mir, policy, /* useAtStart = */ true);
}

void LIRGenerator::visitParameter(MParameter* param) {
  ptrdiff_t offset;
  if (param->index() == MParameter::THIS_SLOT) {
    offset = THIS_FRAME_ARGSLOT;
  } else {
    offset = 1 + param->index();
  }

  LParameter* ins = new (alloc()) LParameter;
  defineBox(ins, param, LDefinition::FIXED);

  offset *= sizeof(Value);
#if defined(JS_NUNBOX32)
#  if MOZ_BIG_ENDIAN()
  ins->getDef(0)->setOutput(LArgument(offset));
  ins->getDef(1)->setOutput(LArgument(offset + 4));
#  else
  ins->getDef(0)->setOutput(LArgument(offset + 4));
  ins->getDef(1)->setOutput(LArgument(offset));
#  endif
#elif defined(JS_PUNBOX64)
  ins->getDef(0)->setOutput(LArgument(offset));
#endif
}

void LIRGenerator::visitCallee(MCallee* ins) {
  define(new (alloc()) LCallee(), ins);
}

void LIRGenerator::visitIsConstructing(MIsConstructing* ins) {
  define(new (alloc()) LIsConstructing(), ins);
}

void LIRGenerator::visitGoto(MGoto* ins) {
  add(new (alloc()) LGoto(ins->target()));
}

void LIRGenerator::visitTableSwitch(MTableSwitch* tableswitch) {
  MDefinition* opd = tableswitch->getOperand(0);

  // There should be at least 1 successor. The default case!
  MOZ_ASSERT(tableswitch->numSuccessors() > 0);

  // If there are no cases, the default case is always taken.
  if (tableswitch->numSuccessors() == 1) {
    add(new (alloc()) LGoto(tableswitch->getDefault()));
    return;
  }

  // If we don't know the type.
  if (opd->type() == MIRType::Value) {
    add(newLTableSwitchV(useBox(opd)), tableswitch);
    return;
  }

  // Case indices are numeric, so other types will always go to the default
  // case.
  if (opd->type() != MIRType::Int32 && opd->type() != MIRType::Double) {
    add(new (alloc()) LGoto(tableswitch->getDefault()));
    return;
  }

  // Return an LTableSwitch, capable of handling either an integer or
  // floating-point index.
  LAllocation index;
  LDefinition tempInt;
  if (opd->type() == MIRType::Int32) {
    index = useRegisterAtStart(opd);
    tempInt = tempCopy(opd, 0);
  } else {
    index = useRegister(opd);
    tempInt = temp();
  }
  add(newLTableSwitch(index, tempInt), tableswitch);
}

void LIRGenerator::visitCheckOverRecursed(MCheckOverRecursed* ins) {
  LCheckOverRecursed* lir = new (alloc()) LCheckOverRecursed();
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewArray(MNewArray* ins) {
  LNewArray* lir = new (alloc()) LNewArray(temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewArrayDynamicLength(MNewArrayDynamicLength* ins) {
  MDefinition* length = ins->length();
  MOZ_ASSERT(length->type() == MIRType::Int32);

  LNewArrayDynamicLength* lir =
      new (alloc()) LNewArrayDynamicLength(useRegister(length), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewIterator(MNewIterator* ins) {
  LNewIterator* lir = new (alloc()) LNewIterator(temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewTypedArray(MNewTypedArray* ins) {
  LNewTypedArray* lir = new (alloc()) LNewTypedArray(temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewTypedArrayDynamicLength(
    MNewTypedArrayDynamicLength* ins) {
  MDefinition* length = ins->length();
  MOZ_ASSERT(length->type() == MIRType::Int32);

  LNewTypedArrayDynamicLength* lir =
      new (alloc()) LNewTypedArrayDynamicLength(useRegister(length), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewTypedArrayFromArray(MNewTypedArrayFromArray* ins) {
  MDefinition* array = ins->array();
  MOZ_ASSERT(array->type() == MIRType::Object);

  auto* lir = new (alloc()) LNewTypedArrayFromArray(useRegisterAtStart(array));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewTypedArrayFromArrayBuffer(
    MNewTypedArrayFromArrayBuffer* ins) {
  MDefinition* arrayBuffer = ins->arrayBuffer();
  MDefinition* byteOffset = ins->byteOffset();
  MDefinition* length = ins->length();
  MOZ_ASSERT(arrayBuffer->type() == MIRType::Object);
  MOZ_ASSERT(byteOffset->type() == MIRType::Value);
  MOZ_ASSERT(length->type() == MIRType::Value);

  auto* lir = new (alloc()) LNewTypedArrayFromArrayBuffer(
      useRegisterAtStart(arrayBuffer), useBoxAtStart(byteOffset),
      useBoxAtStart(length));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewObject(MNewObject* ins) {
  LNewObject* lir = new (alloc()) LNewObject(temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBindFunction(MBindFunction* ins) {
  MDefinition* target = ins->target();
  MOZ_ASSERT(target->type() == MIRType::Object);

  if (!lowerCallArguments(ins)) {
    abort(AbortReason::Alloc, "OOM: LIRGenerator::visitBindFunction");
    return;
  }

  auto* lir = new (alloc())
      LBindFunction(useFixedAtStart(target, CallTempReg0),
                    tempFixed(CallTempReg1), tempFixed(CallTempReg2));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewBoundFunction(MNewBoundFunction* ins) {
  auto* lir = new (alloc()) LNewBoundFunction(temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewPlainObject(MNewPlainObject* ins) {
  LNewPlainObject* lir = new (alloc()) LNewPlainObject(temp(), temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewArrayObject(MNewArrayObject* ins) {
  LNewArrayObject* lir = new (alloc()) LNewArrayObject(temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewNamedLambdaObject(MNewNamedLambdaObject* ins) {
  LNewNamedLambdaObject* lir = new (alloc()) LNewNamedLambdaObject(temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewCallObject(MNewCallObject* ins) {
  LNewCallObject* lir = new (alloc()) LNewCallObject(temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewMapObject(MNewMapObject* ins) {
  auto* lir = new (alloc()) LNewMapObject(temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewSetObject(MNewSetObject* ins) {
  auto* lir = new (alloc()) LNewSetObject(temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewMapObjectFromIterable(
    MNewMapObjectFromIterable* ins) {
  MOZ_ASSERT(ins->iterable()->type() == MIRType::Value);
  auto* lir = new (alloc()) LNewMapObjectFromIterable(
      useBoxFixedAtStart(ins->iterable(), CallTempReg0, CallTempReg1),
      tempFixed(CallTempReg2), tempFixed(CallTempReg3));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewSetObjectFromIterable(
    MNewSetObjectFromIterable* ins) {
  MOZ_ASSERT(ins->iterable()->type() == MIRType::Value);
  auto* lir = new (alloc()) LNewSetObjectFromIterable(
      useBoxFixedAtStart(ins->iterable(), CallTempReg0, CallTempReg1),
      tempFixed(CallTempReg2), tempFixed(CallTempReg3));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewStringObject(MNewStringObject* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::String);

  LNewStringObject* lir =
      new (alloc()) LNewStringObject(useRegister(ins->input()), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitInitElemGetterSetter(MInitElemGetterSetter* ins) {
  LInitElemGetterSetter* lir = new (alloc()) LInitElemGetterSetter(
      useRegisterAtStart(ins->object()), useBoxAtStart(ins->id()),
      useRegisterAtStart(ins->value()));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMutateProto(MMutateProto* ins) {
  LMutateProto* lir = new (alloc()) LMutateProto(
      useRegisterAtStart(ins->object()), useBoxAtStart(ins->value()));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitInitPropGetterSetter(MInitPropGetterSetter* ins) {
  LInitPropGetterSetter* lir = new (alloc()) LInitPropGetterSetter(
      useRegisterAtStart(ins->object()), useRegisterAtStart(ins->value()));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCreateThis(MCreateThis* ins) {
  LCreateThis* lir =
      new (alloc()) LCreateThis(useRegisterOrConstantAtStart(ins->callee()),
                                useRegisterOrConstantAtStart(ins->newTarget()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCreateArgumentsObject(MCreateArgumentsObject* ins) {
  LAllocation callObj = useRegisterAtStart(ins->getCallObject());
  LCreateArgumentsObject* lir = new (alloc())
      LCreateArgumentsObject(callObj, tempFixed(CallTempReg0),
                             tempFixed(CallTempReg1), tempFixed(CallTempReg2));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCreateInlinedArgumentsObject(
    MCreateInlinedArgumentsObject* ins) {
  LAllocation callObj = useRegisterAtStart(ins->getCallObject());
  LAllocation callee = useRegisterAtStart(ins->getCallee());
  uint32_t numActuals = ins->numActuals();
  uint32_t numOperands = numActuals * BOX_PIECES +
                         LCreateInlinedArgumentsObject::NumNonArgumentOperands;

  auto* lir = allocateVariadic<LCreateInlinedArgumentsObject>(
      numOperands, tempFixed(CallTempReg0), tempFixed(CallTempReg1));
  if (!lir) {
    abort(AbortReason::Alloc,
          "OOM: LIRGenerator::visitCreateInlinedArgumentsObject");
    return;
  }

  lir->setOperand(LCreateInlinedArgumentsObject::CallObj, callObj);
  lir->setOperand(LCreateInlinedArgumentsObject::Callee, callee);
  for (uint32_t i = 0; i < numActuals; i++) {
    MDefinition* arg = ins->getArg(i);
    uint32_t index = LCreateInlinedArgumentsObject::ArgIndex(i);
    lir->setBoxOperand(index, useBoxOrTypedOrConstant(arg,
                                                      /*useConstant = */ true,
                                                      /*useAtStart = */ true));
  }

  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGetInlinedArgument(MGetInlinedArgument* ins) {
#if defined(JS_PUNBOX64)
  // On 64-bit architectures, we don't support boxing a typed register
  // in-place without using a scratch register, so the result register
  // can't be the same as any of the inputs. Fortunately, those
  // architectures have registers to spare.
  const bool useAtStart = false;
#else
  const bool useAtStart = true;
#endif

  LAllocation index =
      useAtStart ? useRegisterAtStart(ins->index()) : useRegister(ins->index());
  uint32_t numActuals = ins->numActuals();
  uint32_t numOperands =
      numActuals * BOX_PIECES + LGetInlinedArgument::NumNonArgumentOperands;

  auto* lir = allocateVariadic<LGetInlinedArgument>(numOperands);
  if (!lir) {
    abort(AbortReason::Alloc, "OOM: LIRGenerator::visitGetInlinedArgument");
    return;
  }

  lir->setOperand(LGetInlinedArgument::Index, index);
  for (uint32_t i = 0; i < numActuals; i++) {
    MDefinition* arg = ins->getArg(i);
    uint32_t index = LGetInlinedArgument::ArgIndex(i);
    lir->setBoxOperand(
        index, useBoxOrTypedOrConstant(arg,
                                       /*useConstant = */ true, useAtStart));
  }
  defineBox(lir, ins);
}

void LIRGenerator::visitGetInlinedArgumentHole(MGetInlinedArgumentHole* ins) {
#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_MIPS64)
  // On some 64-bit architectures, we don't support boxing a typed
  // register in-place without using a scratch register, so the result
  // register can't be the same as any of the inputs. Fortunately,
  // those architectures have registers to spare.
  const bool useAtStart = false;
#else
  const bool useAtStart = true;
#endif

  LAllocation index =
      useAtStart ? useRegisterAtStart(ins->index()) : useRegister(ins->index());
  uint32_t numActuals = ins->numActuals();
  uint32_t numOperands =
      numActuals * BOX_PIECES + LGetInlinedArgumentHole::NumNonArgumentOperands;

  auto* lir = allocateVariadic<LGetInlinedArgumentHole>(numOperands);
  if (!lir) {
    abort(AbortReason::Alloc, "OOM: LIRGenerator::visitGetInlinedArgumentHole");
    return;
  }

  lir->setOperand(LGetInlinedArgumentHole::Index, index);
  for (uint32_t i = 0; i < numActuals; i++) {
    MDefinition* arg = ins->getArg(i);
    uint32_t index = LGetInlinedArgumentHole::ArgIndex(i);
    lir->setBoxOperand(
        index, useBoxOrTypedOrConstant(arg,
                                       /*useConstant = */ true, useAtStart));
  }
  assignSnapshot(lir, ins->bailoutKind());
  defineBox(lir, ins);
}

void LIRGenerator::visitGetArgumentsObjectArg(MGetArgumentsObjectArg* ins) {
  LAllocation argsObj = useRegister(ins->argsObject());
  LGetArgumentsObjectArg* lir =
      new (alloc()) LGetArgumentsObjectArg(argsObj, temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitSetArgumentsObjectArg(MSetArgumentsObjectArg* ins) {
  LAllocation argsObj = useRegister(ins->argsObject());
  LSetArgumentsObjectArg* lir = new (alloc())
      LSetArgumentsObjectArg(argsObj, useBox(ins->value()), temp());
  add(lir, ins);
}

void LIRGenerator::visitLoadArgumentsObjectArg(MLoadArgumentsObjectArg* ins) {
  MDefinition* argsObj = ins->argsObject();
  MOZ_ASSERT(argsObj->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  auto* lir = new (alloc())
      LLoadArgumentsObjectArg(useRegister(argsObj), useRegister(index), temp());
  assignSnapshot(lir, ins->bailoutKind());
  defineBox(lir, ins);
}

void LIRGenerator::visitLoadArgumentsObjectArgHole(
    MLoadArgumentsObjectArgHole* ins) {
  MDefinition* argsObj = ins->argsObject();
  MOZ_ASSERT(argsObj->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  auto* lir = new (alloc()) LLoadArgumentsObjectArgHole(
      useRegister(argsObj), useRegister(index), temp());
  assignSnapshot(lir, ins->bailoutKind());
  defineBox(lir, ins);
}

void LIRGenerator::visitInArgumentsObjectArg(MInArgumentsObjectArg* ins) {
  MDefinition* argsObj = ins->argsObject();
  MOZ_ASSERT(argsObj->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  auto* lir = new (alloc())
      LInArgumentsObjectArg(useRegister(argsObj), useRegister(index), temp());
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitArgumentsObjectLength(MArgumentsObjectLength* ins) {
  MDefinition* argsObj = ins->argsObject();
  MOZ_ASSERT(argsObj->type() == MIRType::Object);

  auto* lir = new (alloc()) LArgumentsObjectLength(useRegister(argsObj));
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitArrayFromArgumentsObject(
    MArrayFromArgumentsObject* ins) {
  MDefinition* argsObj = ins->argsObject();
  MOZ_ASSERT(argsObj->type() == MIRType::Object);

  auto* lir =
      new (alloc()) LArrayFromArgumentsObject(useRegisterAtStart(argsObj));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGuardArgumentsObjectFlags(
    MGuardArgumentsObjectFlags* ins) {
  MDefinition* argsObj = ins->argsObject();
  MOZ_ASSERT(argsObj->type() == MIRType::Object);

  auto* lir =
      new (alloc()) LGuardArgumentsObjectFlags(useRegister(argsObj), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, argsObj);
}

void LIRGenerator::visitBoundFunctionNumArgs(MBoundFunctionNumArgs* ins) {
  MDefinition* obj = ins->object();
  MOZ_ASSERT(obj->type() == MIRType::Object);

  auto* lir = new (alloc()) LBoundFunctionNumArgs(useRegisterAtStart(obj));
  define(lir, ins);
}

void LIRGenerator::visitGuardBoundFunctionIsConstructor(
    MGuardBoundFunctionIsConstructor* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc())
      LGuardBoundFunctionIsConstructor(useRegister(ins->object()));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitReturnFromCtor(MReturnFromCtor* ins) {
  LReturnFromCtor* lir = new (alloc())
      LReturnFromCtor(useBox(ins->value()), useRegister(ins->object()));
  define(lir, ins);
}

void LIRGenerator::visitBoxNonStrictThis(MBoxNonStrictThis* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Object);
  MOZ_ASSERT(ins->input()->type() == MIRType::Value);

  auto* lir = new (alloc()) LBoxNonStrictThis(useBox(ins->input()));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitImplicitThis(MImplicitThis* ins) {
  MDefinition* env = ins->env();
  MOZ_ASSERT(env->type() == MIRType::Object);

  auto* lir = new (alloc()) LImplicitThis(useRegister(env));
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

template <typename T>
bool LIRGenerator::lowerCallArguments(T* call) {
  uint32_t argc = call->numStackArgs();

  // Align the arguments of a call such that the callee would keep the same
  // alignment as the caller.
  uint32_t baseSlot = 0;
  if (JitStackValueAlignment > 1) {
    baseSlot = AlignBytes(argc, JitStackValueAlignment);
  } else {
    baseSlot = argc;
  }

  // Save the maximum number of argument, such that we can have one unique
  // frame size.
  if (baseSlot > maxargslots_) {
    maxargslots_ = baseSlot;
  }

  for (size_t i = 0; i < argc; i++) {
    MDefinition* arg = call->getArg(i);
    uint32_t argslot = baseSlot - i;

    // Values take a slow path.
    if (arg->type() == MIRType::Value) {
      LStackArgV* stack = new (alloc()) LStackArgV(useBox(arg), argslot);
      add(stack);
    } else {
      // Known types can move constant types and/or payloads.
      LStackArgT* stack = new (alloc())
          LStackArgT(useRegisterOrConstant(arg), argslot, arg->type());
      add(stack);
    }

    if (!alloc().ensureBallast()) {
      return false;
    }
  }
  return true;
}

void LIRGenerator::visitCall(MCall* call) {
  MOZ_ASSERT(call->getCallee()->type() == MIRType::Object);

  // In case of oom, skip the rest of the allocations.
  if (!lowerCallArguments(call)) {
    abort(AbortReason::Alloc, "OOM: LIRGenerator::visitCall");
    return;
  }

  WrappedFunction* target = call->getSingleTarget();

  LInstruction* lir;

  if (call->isCallDOMNative()) {
    // Call DOM functions.
    MOZ_ASSERT(target && target->isNativeWithoutJitEntry());
    Register cxReg, objReg, privReg, argsReg;
    GetTempRegForIntArg(0, 0, &cxReg);
    GetTempRegForIntArg(1, 0, &objReg);
    GetTempRegForIntArg(2, 0, &privReg);
    mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(3, 0, &argsReg);
    MOZ_ASSERT(ok, "How can we not have four temp registers?");
    lir = new (alloc()) LCallDOMNative(tempFixed(cxReg), tempFixed(objReg),
                                       tempFixed(privReg), tempFixed(argsReg));
  } else if (target) {
    // Call known functions.
    if (target->isNativeWithoutJitEntry()) {
      Register cxReg, numReg, vpReg, tmpReg;
      GetTempRegForIntArg(0, 0, &cxReg);
      GetTempRegForIntArg(1, 0, &numReg);
      GetTempRegForIntArg(2, 0, &vpReg);

      // Even though this is just a temp reg, use the same API to avoid
      // register collisions.
      mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(3, 0, &tmpReg);
      MOZ_ASSERT(ok, "How can we not have four temp registers?");

      lir = new (alloc()) LCallNative(tempFixed(cxReg), tempFixed(numReg),
                                      tempFixed(vpReg), tempFixed(tmpReg));
    } else {
      lir = new (alloc()) LCallKnown(useRegisterAtStart(call->getCallee()),
                                     tempFixed(CallTempReg0));
    }
  } else {
    // Call anything, using the most generic code.
    lir = new (alloc()) LCallGeneric(
        useFixedAtStart(call->getCallee(), IonGenericCallCalleeReg),
        tempFixed(IonGenericCallArgcReg));
  }
  defineReturn(lir, call);
  assignSafepoint(lir, call);
}

void LIRGenerator::visitCallClassHook(MCallClassHook* call) {
  MDefinition* callee = call->getCallee();
  MOZ_ASSERT(callee->type() == MIRType::Object);

  // In case of oom, skip the rest of the allocations.
  if (!lowerCallArguments(call)) {
    abort(AbortReason::Alloc, "OOM: LIRGenerator::visitCallClassHook");
    return;
  }

  Register cxReg, numReg, vpReg, tmpReg;
  GetTempRegForIntArg(0, 0, &cxReg);
  GetTempRegForIntArg(1, 0, &numReg);
  GetTempRegForIntArg(2, 0, &vpReg);

  // Even though this is just a temp reg, use the same API to avoid
  // register collisions.
  mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(3, 0, &tmpReg);
  MOZ_ASSERT(ok, "How can we not have four temp registers?");

  auto* lir = new (alloc())
      LCallClassHook(useRegisterAtStart(callee), tempFixed(cxReg),
                     tempFixed(numReg), tempFixed(vpReg), tempFixed(tmpReg));
  defineReturn(lir, call);
  assignSafepoint(lir, call);
}

void LIRGenerator::visitApplyArgs(MApplyArgs* apply) {
  MOZ_ASSERT(apply->getFunction()->type() == MIRType::Object);

  // Assert if the return value is already erased.
  static_assert(CallTempReg2 != JSReturnReg_Type);
  static_assert(CallTempReg2 != JSReturnReg_Data);

  auto argc = useFixedAtStart(apply->getArgc(), CallTempReg0);
  auto thisValue =
      useBoxFixedAtStart(apply->getThis(), CallTempReg4, CallTempReg5);
  auto tempObj = tempFixed(CallTempReg1);   // object register
  auto tempCopy = tempFixed(CallTempReg2);  // copy register

  auto* target = apply->getSingleTarget();

  LInstruction* lir;
  if (target && target->isNativeWithoutJitEntry()) {
    auto temp = tempFixed(CallTempReg3);

    lir = new (alloc())
        LApplyArgsNative(argc, thisValue, tempObj, tempCopy, temp);
  } else {
    auto function = useFixedAtStart(apply->getFunction(), CallTempReg3);

    lir = new (alloc())
        LApplyArgsGeneric(function, argc, thisValue, tempObj, tempCopy);
    lirGraph_.addExtraSafepointUses(1);
  }

  // Bailout is needed in the case of too many values in the arguments array.
  assignSnapshot(lir, apply->bailoutKind());

  defineReturn(lir, apply);
  assignSafepoint(lir, apply);
}

void LIRGenerator::visitApplyArgsObj(MApplyArgsObj* apply) {
  MOZ_ASSERT(apply->getFunction()->type() == MIRType::Object);

  // Assert if the return value is already erased.
  static_assert(CallTempReg2 != JSReturnReg_Type);
  static_assert(CallTempReg2 != JSReturnReg_Data);

  auto argsObj = useFixedAtStart(apply->getArgsObj(), CallTempReg0);
  auto thisValue =
      useBoxFixedAtStart(apply->getThis(), CallTempReg4, CallTempReg5);
  auto tempObj = tempFixed(CallTempReg1);   // object register
  auto tempCopy = tempFixed(CallTempReg2);  // copy register

  auto* target = apply->getSingleTarget();

  LInstruction* lir;
  if (target && target->isNativeWithoutJitEntry()) {
    auto temp = tempFixed(CallTempReg3);

    lir = new (alloc())
        LApplyArgsObjNative(argsObj, thisValue, tempObj, tempCopy, temp);
  } else {
    auto function = useFixedAtStart(apply->getFunction(), CallTempReg3);

    lir = new (alloc())
        LApplyArgsObj(function, argsObj, thisValue, tempObj, tempCopy);
    lirGraph_.addExtraSafepointUses(1);
  }

  // Bailout is needed in the case of too many values in the arguments array.
  assignSnapshot(lir, apply->bailoutKind());

  defineReturn(lir, apply);
  assignSafepoint(lir, apply);
}

void LIRGenerator::visitApplyArray(MApplyArray* apply) {
  MOZ_ASSERT(apply->getFunction()->type() == MIRType::Object);

  // Assert if the return value is already erased.
  static_assert(CallTempReg2 != JSReturnReg_Type);
  static_assert(CallTempReg2 != JSReturnReg_Data);

  auto elements = useFixedAtStart(apply->getElements(), CallTempReg0);
  auto thisValue =
      useBoxFixedAtStart(apply->getThis(), CallTempReg4, CallTempReg5);
  auto tempObj = tempFixed(CallTempReg1);   // object register
  auto tempCopy = tempFixed(CallTempReg2);  // copy register

  auto* target = apply->getSingleTarget();

  LInstruction* lir;
  if (target && target->isNativeWithoutJitEntry()) {
    auto temp = tempFixed(CallTempReg3);

    lir = new (alloc())
        LApplyArrayNative(elements, thisValue, tempObj, tempCopy, temp);
  } else {
    auto function = useFixedAtStart(apply->getFunction(), CallTempReg3);

    lir = new (alloc())
        LApplyArrayGeneric(function, elements, thisValue, tempObj, tempCopy);
    lirGraph_.addExtraSafepointUses(1);
  }

  // Bailout is needed in the case of too many values in the array, or empty
  // space at the end of the array.
  assignSnapshot(lir, apply->bailoutKind());

  defineReturn(lir, apply);
  assignSafepoint(lir, apply);
}

void LIRGenerator::visitConstructArgs(MConstructArgs* mir) {
  MOZ_ASSERT(mir->getFunction()->type() == MIRType::Object);
  MOZ_ASSERT(mir->getArgc()->type() == MIRType::Int32);
  MOZ_ASSERT(mir->getNewTarget()->type() == MIRType::Object);
  MOZ_ASSERT(mir->getThis()->type() == MIRType::Value);

  // Assert if the return value is already erased.
  static_assert(CallTempReg2 != JSReturnReg_Type);
  static_assert(CallTempReg2 != JSReturnReg_Data);

  auto argc = useFixedAtStart(mir->getArgc(), CallTempReg0);
  auto newTarget = useFixedAtStart(mir->getNewTarget(), CallTempReg1);
  auto temp = tempFixed(CallTempReg2);

  auto* target = mir->getSingleTarget();

  LInstruction* lir;
  if (target && target->isNativeWithoutJitEntry()) {
    auto temp2 = tempFixed(CallTempReg3);
    auto temp3 = tempFixed(CallTempReg4);

    lir =
        new (alloc()) LConstructArgsNative(argc, newTarget, temp, temp2, temp3);
  } else {
    auto function = useFixedAtStart(mir->getFunction(), CallTempReg3);
    auto thisValue =
        useBoxFixedAtStart(mir->getThis(), CallTempReg4, CallTempReg5);

    lir = new (alloc())
        LConstructArgsGeneric(function, argc, newTarget, thisValue, temp);
    lirGraph_.addExtraSafepointUses(1);
  }

  // Bailout is needed in the case of too many values in the arguments array.
  assignSnapshot(lir, mir->bailoutKind());

  defineReturn(lir, mir);
  assignSafepoint(lir, mir);
}

void LIRGenerator::visitConstructArray(MConstructArray* mir) {
  MOZ_ASSERT(mir->getFunction()->type() == MIRType::Object);
  MOZ_ASSERT(mir->getElements()->type() == MIRType::Elements);
  MOZ_ASSERT(mir->getNewTarget()->type() == MIRType::Object);
  MOZ_ASSERT(mir->getThis()->type() == MIRType::Value);

  // Assert if the return value is already erased.
  static_assert(CallTempReg2 != JSReturnReg_Type);
  static_assert(CallTempReg2 != JSReturnReg_Data);

  auto elements = useFixedAtStart(mir->getElements(), CallTempReg0);
  auto newTarget = useFixedAtStart(mir->getNewTarget(), CallTempReg1);
  auto temp = tempFixed(CallTempReg2);

  auto* target = mir->getSingleTarget();

  LInstruction* lir;
  if (target && target->isNativeWithoutJitEntry()) {
    auto temp2 = tempFixed(CallTempReg3);
    auto temp3 = tempFixed(CallTempReg4);

    lir = new (alloc())
        LConstructArrayNative(elements, newTarget, temp, temp2, temp3);
  } else {
    auto function = useFixedAtStart(mir->getFunction(), CallTempReg3);
    auto thisValue =
        useBoxFixedAtStart(mir->getThis(), CallTempReg4, CallTempReg5);

    lir = new (alloc())
        LConstructArrayGeneric(function, elements, newTarget, thisValue, temp);
    lirGraph_.addExtraSafepointUses(1);
  }

  // Bailout is needed in the case of too many values in the array, or empty
  // space at the end of the array.
  assignSnapshot(lir, mir->bailoutKind());

  defineReturn(lir, mir);
  assignSafepoint(lir, mir);
}

void LIRGenerator::visitBail(MBail* bail) {
  LBail* lir = new (alloc()) LBail();
  assignSnapshot(lir, bail->bailoutKind());
  add(lir, bail);
}

void LIRGenerator::visitUnreachable(MUnreachable* unreachable) {
  LUnreachable* lir = new (alloc()) LUnreachable();
  add(lir, unreachable);
}

void LIRGenerator::visitEncodeSnapshot(MEncodeSnapshot* mir) {
  LEncodeSnapshot* lir = new (alloc()) LEncodeSnapshot();
  assignSnapshot(lir, mir->bailoutKind());
  add(lir, mir);
}

void LIRGenerator::visitUnreachableResult(MUnreachableResult* mir) {
  if (mir->type() == MIRType::Value) {
    auto* lir = new (alloc()) LUnreachableResultV();
    defineBox(lir, mir);
  } else {
    auto* lir = new (alloc()) LUnreachableResultT();
    define(lir, mir);
  }
}

void LIRGenerator::visitAssertFloat32(MAssertFloat32* assertion) {
  MIRType type = assertion->input()->type();
  DebugOnly<bool> checkIsFloat32 = assertion->mustBeFloat32();

  if (type != MIRType::Value && !JitOptions.eagerIonCompilation()) {
    MOZ_ASSERT_IF(checkIsFloat32, type == MIRType::Float32);
    MOZ_ASSERT_IF(!checkIsFloat32, type != MIRType::Float32);
  }
}

void LIRGenerator::visitAssertRecoveredOnBailout(
    MAssertRecoveredOnBailout* assertion) {
  MOZ_CRASH("AssertRecoveredOnBailout nodes are always recovered on bailouts.");
}

[[nodiscard]] static JSOp ReorderComparison(JSOp op, MDefinition** lhsp,
                                            MDefinition** rhsp) {
  MDefinition* lhs = *lhsp;
  MDefinition* rhs = *rhsp;

  if (lhs->maybeConstantValue()) {
    *rhsp = lhs;
    *lhsp = rhs;
    return ReverseCompareOp(op);
  }
  return op;
}

void LIRGenerator::visitTest(MTest* test) {
  MDefinition* opd = test->getOperand(0);
  MBasicBlock* ifTrue = test->ifTrue();
  MBasicBlock* ifFalse = test->ifFalse();

  // String is converted to length of string in the type analysis phase (see
  // TestPolicy).
  MOZ_ASSERT(opd->type() != MIRType::String);

  // Testing a constant.
  if (MConstant* constant = opd->maybeConstantValue()) {
    bool b;
    if (constant->valueToBoolean(&b)) {
      add(new (alloc()) LGoto(b ? ifTrue : ifFalse));
      return;
    }
  }

  if (opd->type() == MIRType::Value) {
    auto* lir = new (alloc()) LTestVAndBranch(
        ifTrue, ifFalse, useBox(opd), tempDouble(), tempToUnbox(), temp());
    add(lir, test);
    return;
  }

  // Objects are truthy, except if it might emulate undefined.
  if (opd->type() == MIRType::Object) {
    add(new (alloc())
            LTestOAndBranch(ifTrue, ifFalse, useRegister(opd), temp()),
        test);
    return;
  }

  // These must be explicitly sniffed out since they are constants and have
  // no payload.
  if (opd->type() == MIRType::Undefined || opd->type() == MIRType::Null) {
    add(new (alloc()) LGoto(ifFalse));
    return;
  }

  // All symbols are truthy.
  if (opd->type() == MIRType::Symbol) {
    add(new (alloc()) LGoto(ifTrue));
    return;
  }

  // Try to match the pattern
  //   test=MTest(
  //          comp=MCompare(
  //                 {EQ,NE} for {Int,UInt}{32,64},
  //                 bitAnd={MBitAnd,MWasmBinaryBitwise(And{32,64})}(x, y),
  //                 MConstant(0)
  //               )
  //        )
  // and produce a single LBitAnd{,64}AndBranch node. This requires both `comp`
  // and `bitAnd` to be marked emit-at-uses.
  if (opd->isCompare() && opd->isEmittedAtUses()) {
    MCompare* comp = opd->toCompare();
    Assembler::Condition compCond =
        JSOpToCondition(comp->compareType(), comp->jsop());
    MDefinition* compL = comp->lhs();
    MDefinition* compR = comp->rhs();
    if ((comp->compareType() == MCompare::Compare_Int32 ||
         comp->compareType() == MCompare::Compare_UInt32 ||
         (comp->compareType() == MCompare::Compare_Int64) ||
         (comp->compareType() == MCompare::Compare_UInt64)) &&
        (compCond == Assembler::Equal || compCond == Assembler::NotEqual) &&
        compR->isConstant() &&
        (compR->toConstant()->isInt32(0) ||
         (compR->toConstant()->isInt64(0))) &&
        (compL->isBitAnd() || (compL->isWasmBinaryBitwise() &&
                               compL->toWasmBinaryBitwise()->subOpcode() ==
                                   MWasmBinaryBitwise::SubOpcode::And))) {
      // The MCompare is OK; now check its first operand (the and-ish node).
      MDefinition* bitAnd = compL;
      MDefinition* bitAndL = bitAnd->getOperand(0);
      MDefinition* bitAndR = bitAnd->getOperand(1);
      MIRType bitAndLTy = bitAndL->type();
      MIRType bitAndRTy = bitAndR->type();
      if (bitAnd->isEmittedAtUses() && bitAndLTy == bitAndRTy &&
          (bitAndLTy == MIRType::Int32 || (bitAndLTy == MIRType::Int64))) {
        // Pattern match succeeded.
        ReorderCommutative(&bitAndL, &bitAndR, test);
        if (compCond == Assembler::Equal) {
          compCond = Assembler::Zero;
        } else if (compCond == Assembler::NotEqual) {
          compCond = Assembler::NonZero;
        } else {
          MOZ_ASSERT_UNREACHABLE("inequality operators cannot be folded");
        }

        if (bitAndLTy == MIRType::Int64) {
          auto lhs = useInt64RegisterAtStart(bitAndL);
          auto rhs = useInt64RegisterOrConstantAtStart(bitAndR);
          auto* lir = new (alloc())
              LBitAnd64AndBranch(ifTrue, ifFalse, lhs, rhs, compCond);
          add(lir, test);
          return;
        }

        LAllocation lhs = useRegisterAtStart(bitAndL);
        LAllocation rhs = useRegisterOrConstantAtStart(bitAndR);
        auto* lir =
            new (alloc()) LBitAndAndBranch(ifTrue, ifFalse, lhs, rhs, compCond);
        add(lir, test);
        return;
      }
    }
  }

  // Check if the operand for this test is a compare operation. If it is, we
  // want to emit an LCompare*AndBranch rather than an LTest*AndBranch, to fuse
  // the compare and jump instructions.
  if (opd->isCompare() && opd->isEmittedAtUses()) {
    MCompare* comp = opd->toCompare();
    MDefinition* left = comp->lhs();
    MDefinition* right = comp->rhs();

    // Try to fold the comparison so that we don't have to handle all cases.
    bool result;
    if (comp->tryFold(&result)) {
      add(new (alloc()) LGoto(result ? ifTrue : ifFalse));
      return;
    }

    // Emit LCompare*AndBranch.

    // Compare and branch null/undefined.
    // The second operand has known null/undefined type,
    // so just test the first operand.
    if (comp->compareType() == MCompare::Compare_Null ||
        comp->compareType() == MCompare::Compare_Undefined) {
      if (left->type() == MIRType::Object) {
        auto* lir = new (alloc()) LIsNullOrLikeUndefinedAndBranchT(
            ifTrue, ifFalse, useRegister(left), temp(), comp);
        add(lir, test);
        return;
      }

      if (IsLooseEqualityOp(comp->jsop())) {
        auto* lir = new (alloc()) LIsNullOrLikeUndefinedAndBranchV(
            ifTrue, ifFalse, useBox(left), temp(), tempToUnbox(), comp);
        add(lir, test);
        return;
      }

      if (comp->compareType() == MCompare::Compare_Null) {
        auto* lir =
            new (alloc()) LIsNullAndBranch(ifTrue, ifFalse, useBox(left), comp);
        add(lir, test);
        return;
      }

      auto* lir = new (alloc())
          LIsUndefinedAndBranch(ifTrue, ifFalse, useBox(left), comp);
      add(lir, test);
      return;
    }

    // Compare and branch Int32, Symbol, Object, or WasmAnyRef pointers.
    if (comp->isInt32Comparison() ||
        comp->compareType() == MCompare::Compare_UInt32 ||
        comp->compareType() == MCompare::Compare_IntPtr ||
        comp->compareType() == MCompare::Compare_UIntPtr ||
        comp->compareType() == MCompare::Compare_Object ||
        comp->compareType() == MCompare::Compare_Symbol ||
        comp->compareType() == MCompare::Compare_WasmAnyRef) {
      JSOp op = ReorderComparison(comp->jsop(), &left, &right);
      LAllocation lhs = useRegister(left);
      LAllocation rhs;
      if (comp->isInt32Comparison() ||
          comp->compareType() == MCompare::Compare_UInt32 ||
          comp->compareType() == MCompare::Compare_IntPtr ||
          comp->compareType() == MCompare::Compare_UIntPtr) {
        rhs = useAnyOrInt32Constant(right);
      } else {
        rhs = useAny(right);
      }
      auto* lir =
          new (alloc()) LCompareAndBranch(ifTrue, ifFalse, lhs, rhs, comp, op);
      add(lir, test);
      return;
    }

    // Compare and branch Int64.
    if (comp->compareType() == MCompare::Compare_Int64 ||
        comp->compareType() == MCompare::Compare_UInt64) {
      JSOp op = ReorderComparison(comp->jsop(), &left, &right);
      LInt64Allocation lhs = useInt64Register(left);
      LInt64Allocation rhs = useInt64OrConstant(right);
      auto* lir = new (alloc())
          LCompareI64AndBranch(ifTrue, ifFalse, lhs, rhs, comp, op);
      add(lir, test);
      return;
    }

    // Compare and branch doubles.
    if (comp->isDoubleComparison()) {
      LAllocation lhs = useRegister(left);
      LAllocation rhs = useRegister(right);
      auto* lir =
          new (alloc()) LCompareDAndBranch(ifTrue, ifFalse, lhs, rhs, comp);
      add(lir, test);
      return;
    }

    // Compare and branch floats.
    if (comp->isFloat32Comparison()) {
      LAllocation lhs = useRegister(left);
      LAllocation rhs = useRegister(right);
      auto* lir =
          new (alloc()) LCompareFAndBranch(ifTrue, ifFalse, lhs, rhs, comp);
      add(lir, test);
      return;
    }

    // Compare and branch BigInt with Int32.
    if (comp->compareType() == MCompare::Compare_BigInt_Int32) {
      LAllocation lhs = useRegister(left);
      LAllocation rhs = useRegisterOrInt32Constant(right);
      LDefinition temp1 = temp();
      LDefinition temp2 = !rhs.isConstant() ? temp() : LDefinition::BogusTemp();
      auto* lir = new (alloc()) LCompareBigIntInt32AndBranch(
          ifTrue, ifFalse, lhs, rhs, temp1, temp2, comp);
      add(lir, test);
      return;
    }
  }

  // Check if the operand for this test is a bitand operation. If it is, we want
  // to emit an LBitAndAndBranch rather than an LTest*AndBranch.
  if (opd->isBitAnd() && opd->isEmittedAtUses()) {
    MBitAnd* bitAnd = opd->toBitAnd();
    MDefinition* left = bitAnd->lhs();
    MDefinition* right = bitAnd->rhs();
    if (left->type() == MIRType::Int32 && right->type() == MIRType::Int32) {
      ReorderCommutative(&left, &right, test);
      LAllocation lhs = useRegisterAtStart(left);
      LAllocation rhs = useRegisterOrConstantAtStart(right);
      auto* lir = new (alloc())
          LBitAndAndBranch(ifTrue, ifFalse, lhs, rhs, Assembler::NonZero);
      add(lir, test);
      return;
    }
  }

#if defined(ENABLE_WASM_SIMD) &&                           \
    (defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || \
     defined(JS_CODEGEN_ARM64))
  // Check if the operand for this test is an any_true/all_true SIMD operation.
  // If it is, we want to emit an LWasmReduceAndBranchSimd128 node to avoid
  // generating an intermediate boolean result.
  if (opd->isWasmReduceSimd128() && opd->isEmittedAtUses()) {
    MWasmReduceSimd128* node = opd->toWasmReduceSimd128();
    if (canFoldReduceSimd128AndBranch(node->simdOp())) {
#  ifdef DEBUG
      js::wasm::ReportSimdAnalysis("simd128-to-scalar-and-branch -> folded");
#  endif
      auto* lir = new (alloc()) LWasmReduceAndBranchSimd128(
          ifTrue, ifFalse, useRegister(node->input()), node->simdOp());
      add(lir, test);
      return;
    }
  }
#endif

  if (opd->isIsObject() && opd->isEmittedAtUses()) {
    MDefinition* input = opd->toIsObject()->input();
    MOZ_ASSERT(input->type() == MIRType::Value);

    LIsObjectAndBranch* lir =
        new (alloc()) LIsObjectAndBranch(ifTrue, ifFalse, useBoxAtStart(input));
    add(lir, test);
    return;
  }

  if (opd->isWasmRefIsSubtypeOfAbstract() && opd->isEmittedAtUses()) {
    MWasmRefIsSubtypeOfAbstract* isSubTypeOf =
        opd->toWasmRefIsSubtypeOfAbstract();

    LAllocation ref = useRegister(isSubTypeOf->ref());
    WasmRefIsSubtypeDefs regs =
        useWasmRefIsSubtype(isSubTypeOf->destType(), /*superSTV=*/nullptr);
    add(new (alloc()) LWasmRefIsSubtypeOfAbstractAndBranch(
            ifTrue, ifFalse, ref, regs.scratch1, isSubTypeOf->sourceType(),
            isSubTypeOf->destType()),
        test);
    return;
  }

  if (opd->isWasmRefIsSubtypeOfConcrete() && opd->isEmittedAtUses()) {
    MWasmRefIsSubtypeOfConcrete* isSubTypeOf =
        opd->toWasmRefIsSubtypeOfConcrete();

    LAllocation ref = useRegister(isSubTypeOf->ref());
    WasmRefIsSubtypeDefs regs =
        useWasmRefIsSubtype(isSubTypeOf->destType(), isSubTypeOf->superSTV());
    add(new (alloc()) LWasmRefIsSubtypeOfConcreteAndBranch(
            ifTrue, ifFalse, ref, regs.superSTV, regs.scratch1, regs.scratch2,
            isSubTypeOf->sourceType(), isSubTypeOf->destType()),
        test);
    return;
  }

  if (opd->isIsNullOrUndefined() && opd->isEmittedAtUses()) {
    MIsNullOrUndefined* isNullOrUndefined = opd->toIsNullOrUndefined();
    MDefinition* input = isNullOrUndefined->value();

    if (input->type() == MIRType::Value) {
      auto* lir = new (alloc())
          LIsNullOrUndefinedAndBranch(ifTrue, ifFalse, useBoxAtStart(input));
      add(lir, test);
    } else {
      auto* target = IsNullOrUndefined(input->type()) ? ifTrue : ifFalse;
      add(new (alloc()) LGoto(target));
    }
    return;
  }

  if (opd->isIsNoIter()) {
    MOZ_ASSERT(opd->isEmittedAtUses());

    MDefinition* input = opd->toIsNoIter()->input();
    MOZ_ASSERT(input->type() == MIRType::Value);

    LIsNoIterAndBranch* lir =
        new (alloc()) LIsNoIterAndBranch(ifTrue, ifFalse, useBox(input));
    add(lir, test);
    return;
  }

  if (opd->isIteratorHasIndices()) {
    MOZ_ASSERT(opd->isEmittedAtUses());

    MDefinition* object = opd->toIteratorHasIndices()->object();
    MDefinition* iterator = opd->toIteratorHasIndices()->iterator();
    LIteratorHasIndicesAndBranch* lir = new (alloc())
        LIteratorHasIndicesAndBranch(ifTrue, ifFalse, useRegister(object),
                                     useRegister(iterator), temp(), temp());
    add(lir, test);
    return;
  }

  switch (opd->type()) {
    case MIRType::Double:
      add(new (alloc()) LTestDAndBranch(ifTrue, ifFalse, useRegister(opd)));
      break;
    case MIRType::Float32:
      add(new (alloc()) LTestFAndBranch(ifTrue, ifFalse, useRegister(opd)));
      break;
    case MIRType::Int32:
    case MIRType::Boolean:
      add(new (alloc()) LTestIAndBranch(ifTrue, ifFalse, useRegister(opd)));
      break;
    case MIRType::IntPtr:
      add(new (alloc()) LTestIPtrAndBranch(ifTrue, ifFalse, useRegister(opd)));
      break;
    case MIRType::Int64:
      add(new (alloc())
              LTestI64AndBranch(ifTrue, ifFalse, useInt64Register(opd)));
      break;
    case MIRType::BigInt:
      add(new (alloc()) LTestBIAndBranch(ifTrue, ifFalse, useRegister(opd)));
      break;
    default:
      MOZ_CRASH("Bad type");
  }
}

static inline bool CanEmitCompareAtUses(MInstruction* ins) {
  if (!ins->canEmitAtUses()) {
    return false;
  }

  // If the result is never used, we can usefully defer emission to the use
  // point, since that will never happen.
  MUseIterator iter(ins->usesBegin());
  if (iter == ins->usesEnd()) {
    return true;
  }

  // If the first use isn't of the expected form, the answer is No.
  MNode* node = iter->consumer();
  if (!node->isDefinition()) {
    return false;
  }

  MDefinition* use = node->toDefinition();
  if (!use->isTest() && !use->isWasmSelect()) {
    return false;
  }

  // Emission can be deferred to the first use point, but only if there are no
  // other use points.
  iter++;
  return iter == ins->usesEnd();
}

void LIRGenerator::visitCompare(MCompare* comp) {
  MDefinition* left = comp->lhs();
  MDefinition* right = comp->rhs();

  // Try to fold the comparison so that we don't have to handle all cases.
  bool result;
  if (comp->tryFold(&result)) {
    define(new (alloc()) LInteger(result), comp);
    return;
  }

  // Move below the emitAtUses call if we ever implement
  // LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't
  // make sense and avoids confusion.
  if (comp->compareType() == MCompare::Compare_String) {
    MConstant* constant = nullptr;
    MDefinition* input = nullptr;
    if (left->isConstant()) {
      constant = left->toConstant();
      input = right;
    } else if (right->isConstant()) {
      constant = right->toConstant();
      input = left;
    }

    if (constant) {
      JSLinearString* linear = &constant->toString()->asLinear();

      if (IsEqualityOp(comp->jsop())) {
        if (MacroAssembler::canCompareStringCharsInline(linear)) {
          auto* lir = new (alloc()) LCompareSInline(useRegister(input), linear);
          define(lir, comp);
          assignSafepoint(lir, comp);
          return;
        }
      } else {
        MOZ_ASSERT(IsRelationalOp(comp->jsop()));

        if (linear->length() == 1) {
          // Move the constant value into the right-hand side operand.
          JSOp op = comp->jsop();
          if (left == constant) {
            op = ReverseCompareOp(op);
          }

          auto* lir = new (alloc())
              LCompareSSingle(useRegister(input), temp(), op, linear);
          define(lir, comp);
          return;
        }
      }
    }

    LCompareS* lir =
        new (alloc()) LCompareS(useRegister(left), useRegister(right));
    define(lir, comp);
    assignSafepoint(lir, comp);
    return;
  }

  // Compare two BigInts.
  if (comp->compareType() == MCompare::Compare_BigInt) {
    auto* lir = new (alloc()) LCompareBigInt(
        useRegister(left), useRegister(right), temp(), temp(), temp());
    define(lir, comp);
    return;
  }

  // Compare BigInt with Double.
  if (comp->compareType() == MCompare::Compare_BigInt_Double) {
    auto* lir = new (alloc()) LCompareBigIntDouble(useRegisterAtStart(left),
                                                   useRegisterAtStart(right));
    defineReturn(lir, comp);
    return;
  }

  // Compare BigInt with String.
  if (comp->compareType() == MCompare::Compare_BigInt_String) {
    auto* lir = new (alloc()) LCompareBigIntString(useRegisterAtStart(left),
                                                   useRegisterAtStart(right));
    defineReturn(lir, comp);
    assignSafepoint(lir, comp);
    return;
  }

  // Sniff out if the output of this compare is used only for a branching.
  // If it is, then we will emit an LCompare*AndBranch instruction in place
  // of this compare and any test that uses this compare. Thus, we can
  // ignore this Compare.
  if (CanEmitCompareAtUses(comp)) {
    emitAtUses(comp);
    return;
  }

  // Compare Null and Undefined.
  if (comp->compareType() == MCompare::Compare_Null ||
      comp->compareType() == MCompare::Compare_Undefined) {
    if (left->type() == MIRType::Object) {
      define(new (alloc()) LIsNullOrLikeUndefinedT(useRegister(left)), comp);
      return;
    }

    if (IsLooseEqualityOp(comp->jsop())) {
      auto* lir =
          new (alloc()) LIsNullOrLikeUndefinedV(useBox(left), tempToUnbox());
      define(lir, comp);
      return;
    }

    if (comp->compareType() == MCompare::Compare_Null) {
      auto* lir = new (alloc()) LIsNull(useBox(left));
      define(lir, comp);
      return;
    }

    auto* lir = new (alloc()) LIsUndefined(useBox(left));
    define(lir, comp);
    return;
  }

  // Compare Int32, Symbol, Object or Wasm pointers.
  if (comp->isInt32Comparison() ||
      comp->compareType() == MCompare::Compare_UInt32 ||
      comp->compareType() == MCompare::Compare_IntPtr ||
      comp->compareType() == MCompare::Compare_UIntPtr ||
      comp->compareType() == MCompare::Compare_Object ||
      comp->compareType() == MCompare::Compare_Symbol ||
      comp->compareType() == MCompare::Compare_WasmAnyRef) {
    JSOp op = ReorderComparison(comp->jsop(), &left, &right);
    LAllocation lhs = useRegister(left);
    LAllocation rhs;
    if (comp->isInt32Comparison() ||
        comp->compareType() == MCompare::Compare_UInt32 ||
        comp->compareType() == MCompare::Compare_IntPtr ||
        comp->compareType() == MCompare::Compare_UIntPtr) {
      rhs = useAnyOrInt32Constant(right);
    } else {
      rhs = useAny(right);
    }
    define(new (alloc()) LCompare(lhs, rhs, op), comp);
    return;
  }

  // Compare Int64.
  if (comp->compareType() == MCompare::Compare_Int64 ||
      comp->compareType() == MCompare::Compare_UInt64) {
    JSOp op = ReorderComparison(comp->jsop(), &left, &right);
    define(new (alloc()) LCompareI64(useInt64Register(left),
                                     useInt64OrConstant(right), op),
           comp);
    return;
  }

  // Compare doubles.
  if (comp->isDoubleComparison()) {
    define(new (alloc()) LCompareD(useRegister(left), useRegister(right)),
           comp);
    return;
  }

  // Compare float32.
  if (comp->isFloat32Comparison()) {
    define(new (alloc()) LCompareF(useRegister(left), useRegister(right)),
           comp);
    return;
  }

  // Compare BigInt with Int32.
  if (comp->compareType() == MCompare::Compare_BigInt_Int32) {
    LAllocation lhs = useRegister(left);
    LAllocation rhs = useRegisterOrInt32Constant(right);
    LDefinition temp1 = temp();
    LDefinition temp2 = !rhs.isConstant() ? temp() : LDefinition::BogusTemp();
    auto* lir = new (alloc()) LCompareBigIntInt32(lhs, rhs, temp1, temp2);
    define(lir, comp);
    return;
  }

  MOZ_CRASH("Unrecognized compare type.");
}

void LIRGenerator::visitSameValueDouble(MSameValueDouble* ins) {
  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();

  MOZ_ASSERT(lhs->type() == MIRType::Double);
  MOZ_ASSERT(rhs->type() == MIRType::Double);

  auto* lir = new (alloc())
      LSameValueDouble(useRegister(lhs), useRegister(rhs), tempDouble());
  define(lir, ins);
}

void LIRGenerator::visitSameValue(MSameValue* ins) {
  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();

  MOZ_ASSERT(lhs->type() == MIRType::Value);
  MOZ_ASSERT(rhs->type() == MIRType::Value);

  auto* lir = new (alloc()) LSameValue(useBox(lhs), useBox(rhs));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::lowerBitOp(JSOp op, MBinaryInstruction* ins) {
  MDefinition* lhs = ins->getOperand(0);
  MDefinition* rhs = ins->getOperand(1);
  MOZ_ASSERT(IsIntType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    MOZ_ASSERT(lhs->type() == MIRType::Int32);
    MOZ_ASSERT(rhs->type() == MIRType::Int32);
    ReorderCommutative(&lhs, &rhs, ins);
    lowerForALU(new (alloc()) LBitOpI(op), ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Int64) {
    MOZ_ASSERT(lhs->type() == MIRType::Int64);
    MOZ_ASSERT(rhs->type() == MIRType::Int64);
    ReorderCommutative(&lhs, &rhs, ins);
    lowerForALUInt64(new (alloc()) LBitOpI64(op), ins, lhs, rhs);
    return;
  }

  MOZ_CRASH("Unhandled integer specialization");
}

void LIRGenerator::visitTypeOf(MTypeOf* ins) {
  MDefinition* opd = ins->input();

  if (opd->type() == MIRType::Object) {
    auto* lir = new (alloc()) LTypeOfO(useRegister(opd));
    define(lir, ins);
    return;
  }

  MOZ_ASSERT(opd->type() == MIRType::Value);

  LTypeOfV* lir = new (alloc()) LTypeOfV(useBox(opd), tempToUnbox());
  define(lir, ins);
}

void LIRGenerator::visitTypeOfName(MTypeOfName* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::Int32);

  auto* lir = new (alloc()) LTypeOfName(useRegister(input));
  define(lir, ins);
}

void LIRGenerator::visitTypeOfIs(MTypeOfIs* ins) {
  MDefinition* input = ins->input();

  MOZ_ASSERT(input->type() == MIRType::Object ||
             input->type() == MIRType::Value);

  switch (ins->jstype()) {
    case JSTYPE_UNDEFINED:
    case JSTYPE_OBJECT:
    case JSTYPE_FUNCTION: {
      if (input->type() == MIRType::Object) {
        auto* lir = new (alloc()) LTypeOfIsNonPrimitiveO(useRegister(input));
        define(lir, ins);
      } else {
        auto* lir =
            new (alloc()) LTypeOfIsNonPrimitiveV(useBox(input), tempToUnbox());
        define(lir, ins);
      }
      return;
    }

    case JSTYPE_STRING:
    case JSTYPE_NUMBER:
    case JSTYPE_BOOLEAN:
    case JSTYPE_SYMBOL:
    case JSTYPE_BIGINT: {
      MOZ_ASSERT(input->type() == MIRType::Value);

      auto* lir = new (alloc()) LTypeOfIsPrimitive(useBoxAtStart(input));
      define(lir, ins);
      return;
    }

#ifdef ENABLE_RECORD_TUPLE
    case JSTYPE_RECORD:
    case JSTYPE_TUPLE:
#endif
    case JSTYPE_LIMIT:
      break;
  }
  MOZ_CRASH("Unhandled JSType");
}

void LIRGenerator::visitToAsyncIter(MToAsyncIter* ins) {
  LToAsyncIter* lir = new (alloc()) LToAsyncIter(
      useRegisterAtStart(ins->iterator()), useBoxAtStart(ins->nextMethod()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitToPropertyKeyCache(MToPropertyKeyCache* ins) {
  MDefinition* input = ins->getOperand(0);
  MOZ_ASSERT(ins->type() == MIRType::Value);

  auto* lir = new (alloc()) LToPropertyKeyCache(useBox(input));
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBitNot(MBitNot* ins) {
  MDefinition* input = ins->getOperand(0);

  if (ins->type() == MIRType::Int32) {
    MOZ_ASSERT(input->type() == MIRType::Int32);
    lowerForALU(new (alloc()) LBitNotI(), ins, input);
    return;
  }

  if (ins->type() == MIRType::Int64) {
    MOZ_ASSERT(input->type() == MIRType::Int64);
    lowerForALUInt64(new (alloc()) LBitNotI64(), ins, input);
    return;
  }

  MOZ_CRASH("Unhandled integer specialization");
}

static bool CanEmitBitAndAtUses(MInstruction* ins) {
  if (!ins->canEmitAtUses()) {
    return false;
  }

  MIRType tyL = ins->getOperand(0)->type();
  MIRType tyR = ins->getOperand(1)->type();
  if (tyL != tyR || (tyL != MIRType::Int32 && tyL != MIRType::Int64)) {
    return false;
  }

  MUseIterator iter(ins->usesBegin());
  if (iter == ins->usesEnd()) {
    return false;
  }

  MNode* node = iter->consumer();
  if (!node->isDefinition() || !node->toDefinition()->isInstruction()) {
    return false;
  }

  MInstruction* use = node->toDefinition()->toInstruction();
  if (!use->isTest() && !(use->isCompare() && CanEmitCompareAtUses(use))) {
    return false;
  }

  iter++;
  return iter == ins->usesEnd();
}

void LIRGenerator::visitBitAnd(MBitAnd* ins) {
  // Sniff out if the output of this bitand is used only for a branching.
  // If it is, then we will emit an LBitAndAndBranch instruction in place
  // of this bitand and any test that uses this bitand. Thus, we can
  // ignore this BitAnd.
  if (CanEmitBitAndAtUses(ins)) {
    emitAtUses(ins);
  } else {
    lowerBitOp(JSOp::BitAnd, ins);
  }
}

void LIRGenerator::visitBitOr(MBitOr* ins) { lowerBitOp(JSOp::BitOr, ins); }

void LIRGenerator::visitBitXor(MBitXor* ins) { lowerBitOp(JSOp::BitXor, ins); }

void LIRGenerator::visitWasmBinaryBitwise(MWasmBinaryBitwise* ins) {
  switch (ins->subOpcode()) {
    case MWasmBinaryBitwise::SubOpcode::And:
      if (CanEmitBitAndAtUses(ins)) {
        emitAtUses(ins);
      } else {
        lowerBitOp(JSOp::BitAnd, ins);
      }
      break;
    case MWasmBinaryBitwise::SubOpcode::Or:
      lowerBitOp(JSOp::BitOr, ins);
      break;
    case MWasmBinaryBitwise::SubOpcode::Xor:
      lowerBitOp(JSOp::BitXor, ins);
      break;
    default:
      MOZ_CRASH();
  }
}

void LIRGenerator::lowerShiftOp(JSOp op, MShiftInstruction* ins) {
  MDefinition* lhs = ins->getOperand(0);
  MDefinition* rhs = ins->getOperand(1);

  if (op == JSOp::Ursh && ins->type() == MIRType::Double) {
    MOZ_ASSERT(lhs->type() == MIRType::Int32);
    MOZ_ASSERT(rhs->type() == MIRType::Int32);
    lowerUrshD(ins->toUrsh());
    return;
  }

  MOZ_ASSERT(IsIntType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    MOZ_ASSERT(lhs->type() == MIRType::Int32);
    MOZ_ASSERT(rhs->type() == MIRType::Int32);

    LShiftI* lir = new (alloc()) LShiftI(op);
    if (op == JSOp::Ursh) {
      if (ins->toUrsh()->fallible()) {
        assignSnapshot(lir, ins->bailoutKind());
      }
    }
    lowerForShift(lir, ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Int64) {
    MOZ_ASSERT(lhs->type() == MIRType::Int64);
    MOZ_ASSERT(rhs->type() == MIRType::Int64);
    lowerForShiftInt64(new (alloc()) LShiftI64(op), ins, lhs, rhs);
    return;
  }

  MOZ_CRASH("Unhandled integer specialization");
}

void LIRGenerator::visitLsh(MLsh* ins) { lowerShiftOp(JSOp::Lsh, ins); }

void LIRGenerator::visitRsh(MRsh* ins) { lowerShiftOp(JSOp::Rsh, ins); }

void LIRGenerator::visitUrsh(MUrsh* ins) { lowerShiftOp(JSOp::Ursh, ins); }

void LIRGenerator::visitSignExtendInt32(MSignExtendInt32* ins) {
  LInstructionHelper<1, 1, 0>* lir;

  if (ins->mode() == MSignExtendInt32::Byte) {
    lir =
        new (alloc()) LSignExtendInt32(useByteOpRegisterAtStart(ins->input()));
  } else {
    lir = new (alloc()) LSignExtendInt32(useRegisterAtStart(ins->input()));
  }

  define(lir, ins);
}

void LIRGenerator::visitSignExtendIntPtr(MSignExtendIntPtr* ins) {
  LInstructionHelper<1, 1, 0>* lir;

  if (ins->mode() == MSignExtendIntPtr::Byte) {
    lir =
        new (alloc()) LSignExtendIntPtr(useByteOpRegisterAtStart(ins->input()));
  } else {
    lir = new (alloc()) LSignExtendIntPtr(useRegisterAtStart(ins->input()));
  }

  define(lir, ins);
}

void LIRGenerator::visitRotate(MRotate* ins) {
  MDefinition* input = ins->input();
  MDefinition* count = ins->count();

  if (ins->type() == MIRType::Int32) {
    auto* lir = new (alloc()) LRotate();
    lowerForShift(lir, ins, input, count);
  } else if (ins->type() == MIRType::Int64) {
    auto* lir = new (alloc()) LRotateI64();
    lowerForShiftInt64(lir, ins, input, count);
  } else {
    MOZ_CRASH("unexpected type in visitRotate");
  }
}

void LIRGenerator::visitFloor(MFloor* ins) {
  MIRType type = ins->input()->type();
  MOZ_ASSERT(IsFloatingPointType(type));

  LInstructionHelper<1, 1, 0>* lir;
  if (type == MIRType::Double) {
    lir = new (alloc()) LFloor(useRegister(ins->input()));
  } else {
    lir = new (alloc()) LFloorF(useRegister(ins->input()));
  }

  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitCeil(MCeil* ins) {
  MIRType type = ins->input()->type();
  MOZ_ASSERT(IsFloatingPointType(type));

  LInstructionHelper<1, 1, 0>* lir;
  if (type == MIRType::Double) {
    lir = new (alloc()) LCeil(useRegister(ins->input()));
  } else {
    lir = new (alloc()) LCeilF(useRegister(ins->input()));
  }

  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitRound(MRound* ins) {
  MIRType type = ins->input()->type();
  MOZ_ASSERT(IsFloatingPointType(type));

  LInstructionHelper<1, 1, 1>* lir;
  if (type == MIRType::Double) {
    lir = new (alloc()) LRound(useRegister(ins->input()), tempDouble());
  } else {
    lir = new (alloc()) LRoundF(useRegister(ins->input()), tempFloat32());
  }

  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitTrunc(MTrunc* ins) {
  MIRType type = ins->input()->type();
  MOZ_ASSERT(IsFloatingPointType(type));

  LInstructionHelper<1, 1, 0>* lir;
  if (type == MIRType::Double) {
    lir = new (alloc()) LTrunc(useRegister(ins->input()));
  } else {
    lir = new (alloc()) LTruncF(useRegister(ins->input()));
  }

  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitNearbyInt(MNearbyInt* ins) {
  MIRType inputType = ins->input()->type();
  MOZ_ASSERT(IsFloatingPointType(inputType));
  MOZ_ASSERT(ins->type() == inputType);

  LInstructionHelper<1, 1, 0>* lir;
  if (inputType == MIRType::Double) {
    lir = new (alloc()) LNearbyInt(useRegisterAtStart(ins->input()));
  } else {
    lir = new (alloc()) LNearbyIntF(useRegisterAtStart(ins->input()));
  }

  define(lir, ins);
}

void LIRGenerator::visitMinMax(MMinMax* ins) {
  MDefinition* first = ins->getOperand(0);
  MDefinition* second = ins->getOperand(1);

  ReorderCommutative(&first, &second, ins);

  LInstructionHelper<1, 2, 0>* lir;
  switch (ins->type()) {
    case MIRType::Int32:
      lir = new (alloc())
          LMinMaxI(useRegisterAtStart(first), useRegisterOrConstant(second));
      break;
    case MIRType::Float32:
      lir = new (alloc())
          LMinMaxF(useRegisterAtStart(first), useRegister(second));
      break;
    case MIRType::Double:
      lir = new (alloc())
          LMinMaxD(useRegisterAtStart(first), useRegister(second));
      break;
    default:
      MOZ_CRASH();
  }

  // Input reuse is OK (for now) even on ARM64: floating min/max are fairly
  // expensive due to SNaN -> QNaN conversion, and int min/max is for asm.js.
  defineReuseInput(lir, ins, 0);
}

void LIRGenerator::visitMinMaxArray(MMinMaxArray* ins) {
  LInstructionHelper<1, 1, 3>* lir;
  if (ins->type() == MIRType::Int32) {
    lir = new (alloc())
        LMinMaxArrayI(useRegisterAtStart(ins->array()), temp(), temp(), temp());
  } else {
    MOZ_ASSERT(ins->type() == MIRType::Double);
    lir = new (alloc()) LMinMaxArrayD(useRegisterAtStart(ins->array()),
                                      tempDouble(), temp(), temp());
  }
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

LInstructionHelper<1, 1, 0>* LIRGenerator::allocateAbs(MAbs* ins,
                                                       LAllocation input) {
  MDefinition* num = ins->input();
  MOZ_ASSERT(IsNumberType(num->type()));

  LInstructionHelper<1, 1, 0>* lir;
  switch (num->type()) {
    case MIRType::Int32:
      lir = new (alloc()) LAbsI(input);
      // needed to handle abs(INT32_MIN)
      if (ins->fallible()) {
        assignSnapshot(lir, ins->bailoutKind());
      }
      break;
    case MIRType::Float32:
      lir = new (alloc()) LAbsF(input);
      break;
    case MIRType::Double:
      lir = new (alloc()) LAbsD(input);
      break;
    default:
      MOZ_CRASH();
  }
  return lir;
}

void LIRGenerator::visitClz(MClz* ins) {
  MDefinition* num = ins->num();

  MOZ_ASSERT(IsIntType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    LClzI* lir = new (alloc()) LClzI(useRegisterAtStart(num));
    define(lir, ins);
    return;
  }

  auto* lir = new (alloc()) LClzI64(useInt64RegisterAtStart(num));
  defineInt64(lir, ins);
}

void LIRGenerator::visitCtz(MCtz* ins) {
  MDefinition* num = ins->num();

  MOZ_ASSERT(IsIntType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    LCtzI* lir = new (alloc()) LCtzI(useRegisterAtStart(num));
    define(lir, ins);
    return;
  }

  auto* lir = new (alloc()) LCtzI64(useInt64RegisterAtStart(num));
  defineInt64(lir, ins);
}

void LIRGenerator::visitPopcnt(MPopcnt* ins) {
  MDefinition* num = ins->num();

  MOZ_ASSERT(IsIntType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    LPopcntI* lir = new (alloc()) LPopcntI(useRegisterAtStart(num), temp());
    define(lir, ins);
    return;
  }

  auto* lir = new (alloc()) LPopcntI64(useInt64RegisterAtStart(num), temp());
  defineInt64(lir, ins);
}

void LIRGenerator::visitSqrt(MSqrt* ins) {
  MDefinition* num = ins->input();
  MOZ_ASSERT(IsFloatingPointType(num->type()));

  LInstructionHelper<1, 1, 0>* lir;
  if (num->type() == MIRType::Double) {
    lir = new (alloc()) LSqrtD(useRegisterAtStart(num));
  } else {
    lir = new (alloc()) LSqrtF(useRegisterAtStart(num));
  }
  define(lir, ins);
}

void LIRGenerator::visitAtan2(MAtan2* ins) {
  MDefinition* y = ins->y();
  MOZ_ASSERT(y->type() == MIRType::Double);

  MDefinition* x = ins->x();
  MOZ_ASSERT(x->type() == MIRType::Double);

  LAtan2D* lir =
      new (alloc()) LAtan2D(useRegisterAtStart(y), useRegisterAtStart(x));
  defineReturn(lir, ins);
}

void LIRGenerator::visitHypot(MHypot* ins) {
  LHypot* lir = nullptr;
  uint32_t length = ins->numOperands();
  for (uint32_t i = 0; i < length; ++i) {
    MOZ_ASSERT(ins->getOperand(i)->type() == MIRType::Double);
  }

  switch (length) {
    case 2:
      lir = new (alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)),
                                 useRegisterAtStart(ins->getOperand(1)));
      break;
    case 3:
      lir = new (alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)),
                                 useRegisterAtStart(ins->getOperand(1)),
                                 useRegisterAtStart(ins->getOperand(2)));
      break;
    case 4:
      lir = new (alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)),
                                 useRegisterAtStart(ins->getOperand(1)),
                                 useRegisterAtStart(ins->getOperand(2)),
                                 useRegisterAtStart(ins->getOperand(3)));
      break;
    default:
      MOZ_CRASH("Unexpected number of arguments to LHypot.");
  }

  defineReturn(lir, ins);
}

void LIRGenerator::visitPow(MPow* ins) {
  MDefinition* input = ins->input();
  MDefinition* power = ins->power();

  if (ins->type() == MIRType::Int32) {
    MOZ_ASSERT(input->type() == MIRType::Int32);
    MOZ_ASSERT(power->type() == MIRType::Int32);

    if (input->isConstant()) {
      // Restrict this optimization to |base <= 256| to avoid generating too
      // many consecutive shift instructions.
      int32_t base = input->toConstant()->toInt32();
      if (2 <= base && base <= 256 && mozilla::IsPowerOfTwo(uint32_t(base))) {
        lowerPowOfTwoI(ins);
        return;
      }
    }

    auto* lir = new (alloc())
        LPowII(useRegister(input), useRegister(power), temp(), temp());
    assignSnapshot(lir, ins->bailoutKind());
    define(lir, ins);
    return;
  }

  MOZ_ASSERT(ins->type() == MIRType::Double);
  MOZ_ASSERT(input->type() == MIRType::Double);
  MOZ_ASSERT(power->type() == MIRType::Int32 ||
             power->type() == MIRType::Double);

  LInstruction* lir;
  if (power->type() == MIRType::Int32) {
    lir = new (alloc())
        LPowI(useRegisterAtStart(input), useRegisterAtStart(power));
  } else {
    lir = new (alloc())
        LPowD(useRegisterAtStart(input), useRegisterAtStart(power));
  }
  defineReturn(lir, ins);
}

void LIRGenerator::visitSign(MSign* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Int32 || ins->type() == MIRType::Double);
  MOZ_ASSERT(ins->input()->type() == MIRType::Int32 ||
             ins->input()->type() == MIRType::Double);

  if (ins->type() == ins->input()->type()) {
    if (ins->type() == MIRType::Int32) {
      define(new (alloc()) LSignI(useRegister(ins->input())), ins);
    } else {
      define(new (alloc()) LSignD(useRegister(ins->input())), ins);
    }
  } else {
    if (ins->type() == MIRType::Int32) {
      auto* lir =
          new (alloc()) LSignDI(useRegister(ins->input()), tempDouble());
      assignSnapshot(lir, ins->bailoutKind());
      define(lir, ins);
    } else {
      define(new (alloc()) LSignID(useRegister(ins->input()), temp()), ins);
    }
  }
}

void LIRGenerator::visitMathFunction(MMathFunction* ins) {
  MOZ_ASSERT(IsFloatingPointType(ins->type()));
  MOZ_ASSERT(ins->type() == ins->input()->type());

  LInstruction* lir;
  if (ins->type() == MIRType::Double) {
    lir = new (alloc()) LMathFunctionD(useRegisterAtStart(ins->input()));
  } else {
    lir = new (alloc()) LMathFunctionF(useRegisterAtStart(ins->input()));
  }
  defineReturn(lir, ins);
}

void LIRGenerator::visitRandom(MRandom* ins) {
  auto* lir = new (alloc()) LRandom(temp(), tempInt64(), tempInt64());
  define(lir, ins);
}

// Try to mark an add or sub instruction as able to recover its input when
// bailing out.
template <typename S, typename T>
static void MaybeSetRecoversInput(S* mir, T* lir) {
  MOZ_ASSERT(lir->mirRaw() == mir);
  if (!mir->fallible() || !lir->snapshot()) {
    return;
  }

  if (lir->output()->policy() != LDefinition::MUST_REUSE_INPUT) {
    return;
  }

  // The original operands to an add or sub can't be recovered if they both
  // use the same register.
  if (lir->lhs()->isUse() && lir->rhs()->isUse() &&
      lir->lhs()->toUse()->virtualRegister() ==
          lir->rhs()->toUse()->virtualRegister()) {
    return;
  }

  // Add instructions that are on two different values can recover
  // the input they clobbered via MUST_REUSE_INPUT. Thus, a copy
  // of that input does not need to be kept alive in the snapshot
  // for the instruction.

  lir->setRecoversInput();

  const LUse* input = lir->getOperand(lir->output()->getReusedInput())->toUse();
  lir->snapshot()->rewriteRecoveredInput(*input);
}

void LIRGenerator::visitAdd(MAdd* ins) {
  MDefinition* lhs = ins->getOperand(0);
  MDefinition* rhs = ins->getOperand(1);

  MOZ_ASSERT(lhs->type() == rhs->type());
  MOZ_ASSERT(IsNumberType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    MOZ_ASSERT(lhs->type() == MIRType::Int32);
    ReorderCommutative(&lhs, &rhs, ins);
    LAddI* lir = new (alloc()) LAddI;

    if (ins->fallible()) {
      assignSnapshot(lir, ins->bailoutKind());
    }

    lowerForALU(lir, ins, lhs, rhs);
    MaybeSetRecoversInput(ins, lir);
    return;
  }

  if (ins->type() == MIRType::Int64) {
    MOZ_ASSERT(lhs->type() == MIRType::Int64);
    ReorderCommutative(&lhs, &rhs, ins);
    LAddI64* lir = new (alloc()) LAddI64;
    lowerForALUInt64(lir, ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Double) {
    MOZ_ASSERT(lhs->type() == MIRType::Double);
    ReorderCommutative(&lhs, &rhs, ins);
    lowerForFPU(new (alloc()) LMathD(JSOp::Add), ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Float32) {
    MOZ_ASSERT(lhs->type() == MIRType::Float32);
    ReorderCommutative(&lhs, &rhs, ins);
    lowerForFPU(new (alloc()) LMathF(JSOp::Add), ins, lhs, rhs);
    return;
  }

  MOZ_CRASH("Unhandled number specialization");
}

void LIRGenerator::visitSub(MSub* ins) {
  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();

  MOZ_ASSERT(lhs->type() == rhs->type());
  MOZ_ASSERT(IsNumberType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    MOZ_ASSERT(lhs->type() == MIRType::Int32);

    LSubI* lir = new (alloc()) LSubI;
    if (ins->fallible()) {
      assignSnapshot(lir, ins->bailoutKind());
    }

    // If our LHS is a constant 0 and we don't have to worry about results that
    // can't be represented as an int32, we can optimize to an LNegI.
    if (!ins->fallible() && lhs->isConstant() &&
        lhs->toConstant()->toInt32() == 0) {
      lowerNegI(ins, rhs);
      return;
    }

    lowerForALU(lir, ins, lhs, rhs);
    MaybeSetRecoversInput(ins, lir);
    return;
  }

  if (ins->type() == MIRType::Int64) {
    MOZ_ASSERT(lhs->type() == MIRType::Int64);

    // If our LHS is a constant 0, we can optimize to an LNegI64.
    if (lhs->isConstant() && lhs->toConstant()->toInt64() == 0) {
      lowerNegI64(ins, rhs);
      return;
    }

    LSubI64* lir = new (alloc()) LSubI64;
    lowerForALUInt64(lir, ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Double) {
    MOZ_ASSERT(lhs->type() == MIRType::Double);
    lowerForFPU(new (alloc()) LMathD(JSOp::Sub), ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Float32) {
    MOZ_ASSERT(lhs->type() == MIRType::Float32);
    lowerForFPU(new (alloc()) LMathF(JSOp::Sub), ins, lhs, rhs);
    return;
  }

  MOZ_CRASH("Unhandled number specialization");
}

void LIRGenerator::visitMul(MMul* ins) {
  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();
  MOZ_ASSERT(lhs->type() == rhs->type());
  MOZ_ASSERT(IsNumberType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    MOZ_ASSERT(lhs->type() == MIRType::Int32);
    ReorderCommutative(&lhs, &rhs, ins);

    // If our RHS is a constant -1 and we don't have to worry about results that
    // can't be represented as an int32, we can optimize to an LNegI.
    if (!ins->fallible() && rhs->isConstant() &&
        rhs->toConstant()->toInt32() == -1) {
      lowerNegI(ins, lhs);
      return;
    }

    lowerMulI(ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Int64) {
    MOZ_ASSERT(lhs->type() == MIRType::Int64);
    ReorderCommutative(&lhs, &rhs, ins);

    // If our RHS is a constant -1, we can optimize to an LNegI64.
    if (rhs->isConstant() && rhs->toConstant()->toInt64() == -1) {
      lowerNegI64(ins, lhs);
      return;
    }

    LMulI64* lir = new (alloc()) LMulI64;
    lowerForMulInt64(lir, ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Double) {
    MOZ_ASSERT(lhs->type() == MIRType::Double);
    ReorderCommutative(&lhs, &rhs, ins);

    // If our RHS is a constant -1.0, we can optimize to an LNegD.
    if (!ins->mustPreserveNaN() && rhs->isConstant() &&
        rhs->toConstant()->toDouble() == -1.0) {
      defineReuseInput(new (alloc()) LNegD(useRegisterAtStart(lhs)), ins, 0);
      return;
    }

    lowerForFPU(new (alloc()) LMathD(JSOp::Mul), ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Float32) {
    MOZ_ASSERT(lhs->type() == MIRType::Float32);
    ReorderCommutative(&lhs, &rhs, ins);

    // We apply the same optimizations as for doubles
    if (!ins->mustPreserveNaN() && rhs->isConstant() &&
        rhs->toConstant()->toFloat32() == -1.0f) {
      defineReuseInput(new (alloc()) LNegF(useRegisterAtStart(lhs)), ins, 0);
      return;
    }

    lowerForFPU(new (alloc()) LMathF(JSOp::Mul), ins, lhs, rhs);
    return;
  }

  MOZ_CRASH("Unhandled number specialization");
}

void LIRGenerator::visitDiv(MDiv* ins) {
  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();
  MOZ_ASSERT(lhs->type() == rhs->type());
  MOZ_ASSERT(IsNumberType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    MOZ_ASSERT(lhs->type() == MIRType::Int32);
    lowerDivI(ins);
    return;
  }

  if (ins->type() == MIRType::Int64) {
    MOZ_ASSERT(lhs->type() == MIRType::Int64);
    lowerDivI64(ins);
    return;
  }

  if (ins->type() == MIRType::Double) {
    MOZ_ASSERT(lhs->type() == MIRType::Double);
    lowerForFPU(new (alloc()) LMathD(JSOp::Div), ins, lhs, rhs);
    return;
  }

  if (ins->type() == MIRType::Float32) {
    MOZ_ASSERT(lhs->type() == MIRType::Float32);
    lowerForFPU(new (alloc()) LMathF(JSOp::Div), ins, lhs, rhs);
    return;
  }

  MOZ_CRASH("Unhandled number specialization");
}

void LIRGenerator::visitWasmBuiltinDivI64(MWasmBuiltinDivI64* div) {
  lowerWasmBuiltinDivI64(div);
}

void LIRGenerator::visitWasmBuiltinModI64(MWasmBuiltinModI64* mod) {
  lowerWasmBuiltinModI64(mod);
}

void LIRGenerator::visitBuiltinInt64ToFloatingPoint(
    MBuiltinInt64ToFloatingPoint* ins) {
  lowerBuiltinInt64ToFloatingPoint(ins);
}

void LIRGenerator::visitWasmBuiltinTruncateToInt64(
    MWasmBuiltinTruncateToInt64* ins) {
  lowerWasmBuiltinTruncateToInt64(ins);
}

void LIRGenerator::visitWasmBuiltinModD(MWasmBuiltinModD* ins) {
  MOZ_ASSERT(gen->compilingWasm());
  LWasmBuiltinModD* lir = new (alloc()) LWasmBuiltinModD(
      useRegisterAtStart(ins->lhs()), useRegisterAtStart(ins->rhs()),
      useFixedAtStart(ins->instance(), InstanceReg));
  defineReturn(lir, ins);
}

void LIRGenerator::visitMod(MMod* ins) {
  MOZ_ASSERT(ins->lhs()->type() == ins->rhs()->type());
  MOZ_ASSERT(IsNumberType(ins->type()));

  if (ins->type() == MIRType::Int32) {
    MOZ_ASSERT(ins->type() == MIRType::Int32);
    MOZ_ASSERT(ins->lhs()->type() == MIRType::Int32);
    lowerModI(ins);
    return;
  }

  if (ins->type() == MIRType::Int64) {
    MOZ_ASSERT(ins->type() == MIRType::Int64);
    MOZ_ASSERT(ins->lhs()->type() == MIRType::Int64);
    lowerModI64(ins);
    return;
  }

  if (ins->type() == MIRType::Double) {
    MOZ_ASSERT(ins->lhs()->type() == MIRType::Double);
    MOZ_ASSERT(ins->rhs()->type() == MIRType::Double);

    MOZ_ASSERT(!gen->compilingWasm());

    if (Assembler::HasRoundInstruction(RoundingMode::TowardsZero)) {
      if (ins->rhs()->isConstant()) {
        double d = ins->rhs()->toConstant()->toDouble();
        int32_t div;
        if (mozilla::NumberIsInt32(d, &div) && div > 0 &&
            mozilla::IsPowerOfTwo(uint32_t(div))) {
          auto* lir = new (alloc()) LModPowTwoD(useRegister(ins->lhs()), div);
          define(lir, ins);
          return;
        }
      }
    }

    LModD* lir = new (alloc())
        LModD(useRegisterAtStart(ins->lhs()), useRegisterAtStart(ins->rhs()));
    defineReturn(lir, ins);
    return;
  }

  MOZ_CRASH("Unhandled number specialization");
}

void LIRGenerator::visitBigIntAdd(MBigIntAdd* ins) {
  auto* lir = new (alloc()) LBigIntAdd(useRegisterAtStart(ins->lhs()),
                                       useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntSub(MBigIntSub* ins) {
  auto* lir = new (alloc()) LBigIntSub(useRegisterAtStart(ins->lhs()),
                                       useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntMul(MBigIntMul* ins) {
  auto* lir = new (alloc()) LBigIntMul(useRegisterAtStart(ins->lhs()),
                                       useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntDiv(MBigIntDiv* ins) {
  auto* lir = new (alloc()) LBigIntDiv(useRegisterAtStart(ins->lhs()),
                                       useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntMod(MBigIntMod* ins) {
  auto* lir = new (alloc()) LBigIntMod(useRegisterAtStart(ins->lhs()),
                                       useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntPow(MBigIntPow* ins) {
  auto* lir = new (alloc()) LBigIntPow(useRegisterAtStart(ins->lhs()),
                                       useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntBitAnd(MBigIntBitAnd* ins) {
  auto* lir = new (alloc()) LBigIntBitAnd(useRegisterAtStart(ins->lhs()),
                                          useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntBitOr(MBigIntBitOr* ins) {
  auto* lir = new (alloc()) LBigIntBitOr(useRegisterAtStart(ins->lhs()),
                                         useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntBitXor(MBigIntBitXor* ins) {
  auto* lir = new (alloc()) LBigIntBitXor(useRegisterAtStart(ins->lhs()),
                                          useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntLsh(MBigIntLsh* ins) {
  auto* lir = new (alloc()) LBigIntLsh(useRegisterAtStart(ins->lhs()),
                                       useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntRsh(MBigIntRsh* ins) {
  auto* lir = new (alloc()) LBigIntRsh(useRegisterAtStart(ins->lhs()),
                                       useRegisterAtStart(ins->rhs()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntIncrement(MBigIntIncrement* ins) {
  auto* lir = new (alloc()) LBigIntIncrement(useRegisterAtStart(ins->input()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntDecrement(MBigIntDecrement* ins) {
  auto* lir = new (alloc()) LBigIntDecrement(useRegisterAtStart(ins->input()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntNegate(MBigIntNegate* ins) {
  auto* lir = new (alloc()) LBigIntNegate(useRegister(ins->input()), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntBitNot(MBigIntBitNot* ins) {
  auto* lir = new (alloc()) LBigIntBitNot(useRegisterAtStart(ins->input()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntToIntPtr(MBigIntToIntPtr* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::BigInt);

  auto* lir = new (alloc()) LBigIntToIntPtr(useRegister(ins->input()));
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitIntPtrToBigInt(MIntPtrToBigInt* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::IntPtr);

  auto* lir = new (alloc()) LIntPtrToBigInt(useRegister(ins->input()), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntPtrAdd(MBigIntPtrAdd* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();
  ReorderCommutative(&lhs, &rhs, ins);

  auto* lir =
      new (alloc()) LBigIntPtrAdd(useRegister(lhs), useRegisterOrConstant(rhs));
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitBigIntPtrSub(MBigIntPtrSub* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  auto* lir = new (alloc())
      LBigIntPtrSub(useRegister(ins->lhs()), useRegister(ins->rhs()));
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitBigIntPtrMul(MBigIntPtrMul* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();
  ReorderCommutative(&lhs, &rhs, ins);

  auto* lir =
      new (alloc()) LBigIntPtrMul(useRegister(lhs), useRegisterOrConstant(rhs));
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitBigIntPtrDiv(MBigIntPtrDiv* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  if (ins->rhs()->isConstant()) {
    intptr_t rhs = ins->rhs()->toConstant()->toIntPtr();
    if (mozilla::IsPowerOfTwo(mozilla::Abs(rhs))) {
      int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs));
      auto* lir = new (alloc())
          LBigIntPtrDivPowTwo(useRegister(ins->lhs()), shift, rhs < 0);
      if (shift == 0 && rhs < 0) {
        assignSnapshot(lir, ins->bailoutKind());
      }
      define(lir, ins);
      return;
    }
  }

  lowerBigIntPtrDiv(ins);
}

void LIRGenerator::visitBigIntPtrMod(MBigIntPtrMod* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  if (ins->rhs()->isConstant()) {
    intptr_t rhs = ins->rhs()->toConstant()->toIntPtr();
    if (mozilla::IsPowerOfTwo(mozilla::Abs(rhs))) {
      int32_t shift = mozilla::FloorLog2(mozilla::Abs(rhs));
      auto* lir = new (alloc())
          LBigIntPtrModPowTwo(useRegister(ins->lhs()), temp(), shift);
      define(lir, ins);
      return;
    }
  }

  lowerBigIntPtrMod(ins);
}

void LIRGenerator::visitBigIntPtrPow(MBigIntPtrPow* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  auto* lir = new (alloc()) LBigIntPtrPow(
      useRegister(ins->lhs()), useRegister(ins->rhs()), temp(), temp());
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitBigIntPtrBitAnd(MBigIntPtrBitAnd* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();
  ReorderCommutative(&lhs, &rhs, ins);

  auto* lir = new (alloc())
      LBigIntPtrBitAnd(useRegister(lhs), useRegisterOrConstant(rhs));
  define(lir, ins);
}

void LIRGenerator::visitBigIntPtrBitOr(MBigIntPtrBitOr* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();
  ReorderCommutative(&lhs, &rhs, ins);

  auto* lir = new (alloc())
      LBigIntPtrBitOr(useRegister(lhs), useRegisterOrConstant(rhs));
  define(lir, ins);
}

void LIRGenerator::visitBigIntPtrBitXor(MBigIntPtrBitXor* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();
  ReorderCommutative(&lhs, &rhs, ins);

  auto* lir = new (alloc())
      LBigIntPtrBitXor(useRegister(lhs), useRegisterOrConstant(rhs));
  define(lir, ins);
}

void LIRGenerator::visitBigIntPtrLsh(MBigIntPtrLsh* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  if (ins->rhs()->isConstant()) {
    // Need an additional temp when the operation is fallible.
    auto tmp = ins->fallible() ? temp() : LDefinition::BogusTemp();

    auto* lir = new (alloc()) LBigIntPtrLsh(useRegister(ins->lhs()),
                                            useRegisterOrConstant(ins->rhs()),
                                            tmp, LDefinition::BogusTemp());
    if (ins->fallible()) {
      assignSnapshot(lir, ins->bailoutKind());
    }
    define(lir, ins);
    return;
  }

  lowerBigIntPtrLsh(ins);
}

void LIRGenerator::visitBigIntPtrRsh(MBigIntPtrRsh* ins) {
  MOZ_ASSERT(ins->lhs()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::IntPtr);

  if (ins->rhs()->isConstant()) {
    // Need an additional temp when the operation is fallible.
    auto tmp = ins->fallible() ? temp() : LDefinition::BogusTemp();

    auto* lir = new (alloc()) LBigIntPtrRsh(useRegister(ins->lhs()),
                                            useRegisterOrConstant(ins->rhs()),
                                            tmp, LDefinition::BogusTemp());
    if (ins->fallible()) {
      assignSnapshot(lir, ins->bailoutKind());
    }
    define(lir, ins);
    return;
  }

  lowerBigIntPtrRsh(ins);
}

void LIRGenerator::visitBigIntPtrBitNot(MBigIntPtrBitNot* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::IntPtr);

  auto* lir = new (alloc()) LBigIntPtrBitNot(useRegister(ins->input()));
  define(lir, ins);
}

void LIRGenerator::visitInt32ToStringWithBase(MInt32ToStringWithBase* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->base()->type() == MIRType::Int32);

  int32_t baseInt =
      ins->base()->isConstant() ? ins->base()->toConstant()->toInt32() : 0;

  LAllocation base;
  if (2 <= baseInt && baseInt <= 36) {
    base = useRegisterOrConstant(ins->base());
  } else {
    base = useRegister(ins->base());
  }

  auto* lir = new (alloc())
      LInt32ToStringWithBase(useRegister(ins->input()), base, temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNumberParseInt(MNumberParseInt* ins) {
  MOZ_ASSERT(ins->string()->type() == MIRType::String);
  MOZ_ASSERT(ins->radix()->type() == MIRType::Int32);

  auto* lir = new (alloc()) LNumberParseInt(useRegisterAtStart(ins->string()),
                                            useRegisterAtStart(ins->radix()),
                                            tempFixed(CallTempReg0));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitDoubleParseInt(MDoubleParseInt* ins) {
  MOZ_ASSERT(ins->number()->type() == MIRType::Double);

  auto* lir =
      new (alloc()) LDoubleParseInt(useRegister(ins->number()), tempDouble());
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitConcat(MConcat* ins) {
  MDefinition* lhs = ins->getOperand(0);
  MDefinition* rhs = ins->getOperand(1);

  MOZ_ASSERT(lhs->type() == MIRType::String);
  MOZ_ASSERT(rhs->type() == MIRType::String);
  MOZ_ASSERT(ins->type() == MIRType::String);

  LConcat* lir = new (alloc()) LConcat(
      useFixedAtStart(lhs, CallTempReg0), useFixedAtStart(rhs, CallTempReg1),
      tempFixed(CallTempReg0), tempFixed(CallTempReg1), tempFixed(CallTempReg2),
      tempFixed(CallTempReg3), tempFixed(CallTempReg4));
  defineFixed(lir, ins, LAllocation(AnyRegister(CallTempReg5)));
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitLinearizeString(MLinearizeString* ins) {
  MDefinition* str = ins->string();
  MOZ_ASSERT(str->type() == MIRType::String);

  auto* lir = new (alloc()) LLinearizeString(useRegister(str));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitLinearizeForCharAccess(MLinearizeForCharAccess* ins) {
  MDefinition* str = ins->string();
  MDefinition* idx = ins->index();

  MOZ_ASSERT(str->type() == MIRType::String);
  MOZ_ASSERT(idx->type() == MIRType::Int32);

  auto* lir =
      new (alloc()) LLinearizeForCharAccess(useRegister(str), useRegister(idx));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitLinearizeForCodePointAccess(
    MLinearizeForCodePointAccess* ins) {
  MDefinition* str = ins->string();
  MDefinition* idx = ins->index();

  MOZ_ASSERT(str->type() == MIRType::String);
  MOZ_ASSERT(idx->type() == MIRType::Int32);

  auto* lir = new (alloc())
      LLinearizeForCodePointAccess(useRegister(str), useRegister(idx), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitToRelativeStringIndex(MToRelativeStringIndex* ins) {
  MDefinition* index = ins->index();
  MDefinition* length = ins->length();

  MOZ_ASSERT(index->type() == MIRType::Int32);
  MOZ_ASSERT(length->type() == MIRType::Int32);

  auto* lir = new (alloc())
      LToRelativeStringIndex(useRegister(index), useRegister(length));
  define(lir, ins);
}

void LIRGenerator::visitCharCodeAt(MCharCodeAt* ins) {
  MDefinition* str = ins->string();
  MDefinition* idx = ins->index();

  MOZ_ASSERT(str->type() == MIRType::String);
  MOZ_ASSERT(idx->type() == MIRType::Int32);

  auto* lir = new (alloc())
      LCharCodeAt(useRegister(str), useRegisterOrZero(idx), temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCharCodeAtOrNegative(MCharCodeAtOrNegative* ins) {
  MDefinition* str = ins->string();
  MDefinition* idx = ins->index();

  MOZ_ASSERT(str->type() == MIRType::String);
  MOZ_ASSERT(idx->type() == MIRType::Int32);

  auto* lir = new (alloc()) LCharCodeAtOrNegative(
      useRegister(str), useRegisterOrZero(idx), temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCodePointAt(MCodePointAt* ins) {
  MDefinition* str = ins->string();
  MDefinition* idx = ins->index();

  MOZ_ASSERT(str->type() == MIRType::String);
  MOZ_ASSERT(idx->type() == MIRType::Int32);

  auto* lir = new (alloc())
      LCodePointAt(useRegister(str), useRegister(idx), temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCodePointAtOrNegative(MCodePointAtOrNegative* ins) {
  MDefinition* str = ins->string();
  MDefinition* idx = ins->index();

  MOZ_ASSERT(str->type() == MIRType::String);
  MOZ_ASSERT(idx->type() == MIRType::Int32);

  auto* lir = new (alloc()) LCodePointAtOrNegative(
      useRegister(str), useRegister(idx), temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNegativeToNaN(MNegativeToNaN* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::Int32);

  auto* lir = new (alloc()) LNegativeToNaN(useRegister(ins->input()));
  defineBox(lir, ins);
}

void LIRGenerator::visitNegativeToUndefined(MNegativeToUndefined* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::Int32);

  auto* lir = new (alloc()) LNegativeToUndefined(useRegister(ins->input()));
  defineBox(lir, ins);
}

void LIRGenerator::visitFromCharCode(MFromCharCode* ins) {
  MDefinition* code = ins->code();

  MOZ_ASSERT(code->type() == MIRType::Int32);

  LFromCharCode* lir = new (alloc()) LFromCharCode(useRegister(code));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitFromCharCodeEmptyIfNegative(
    MFromCharCodeEmptyIfNegative* ins) {
  MDefinition* code = ins->code();

  MOZ_ASSERT(code->type() == MIRType::Int32);

  auto* lir = new (alloc()) LFromCharCodeEmptyIfNegative(useRegister(code));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitFromCharCodeUndefinedIfNegative(
    MFromCharCodeUndefinedIfNegative* ins) {
  MDefinition* code = ins->code();

  MOZ_ASSERT(code->type() == MIRType::Int32);

  auto* lir = new (alloc()) LFromCharCodeUndefinedIfNegative(useRegister(code));
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitFromCodePoint(MFromCodePoint* ins) {
  MDefinition* codePoint = ins->codePoint();

  MOZ_ASSERT(codePoint->type() == MIRType::Int32);

  LFromCodePoint* lir =
      new (alloc()) LFromCodePoint(useRegister(codePoint), temp(), temp());
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringIncludes(MStringIncludes* ins) {
  auto* string = ins->string();
  MOZ_ASSERT(string->type() == MIRType::String);

  auto* searchStr = ins->searchString();
  MOZ_ASSERT(searchStr->type() == MIRType::String);

  if (searchStr->isConstant()) {
    JSLinearString* linear = &searchStr->toConstant()->toString()->asLinear();
    size_t length = linear->length();
    if (length == 1 || length == 2) {
      LDefinition tempDef = LDefinition::BogusTemp();
      if (length > 1) {
        tempDef = temp();
      }

      auto* lir = new (alloc()) LStringIncludesSIMD(useRegister(string), temp(),
                                                    temp(), tempDef, linear);
      define(lir, ins);
      assignSafepoint(lir, ins);
      return;
    }
  }

  auto* lir = new (alloc()) LStringIncludes(useRegisterAtStart(string),
                                            useRegisterAtStart(searchStr));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringIndexOf(MStringIndexOf* ins) {
  auto* string = ins->string();
  MOZ_ASSERT(string->type() == MIRType::String);

  auto* searchStr = ins->searchString();
  MOZ_ASSERT(searchStr->type() == MIRType::String);

  if (searchStr->isConstant()) {
    JSLinearString* linear = &searchStr->toConstant()->toString()->asLinear();
    size_t length = linear->length();
    if (length == 1 || length == 2) {
      LDefinition tempDef = LDefinition::BogusTemp();
      if (length > 1) {
        tempDef = temp();
      }

      auto* lir = new (alloc()) LStringIndexOfSIMD(useRegister(string), temp(),
                                                   temp(), tempDef, linear);
      define(lir, ins);
      assignSafepoint(lir, ins);
      return;
    }
  }

  auto* lir = new (alloc())
      LStringIndexOf(useRegisterAtStart(string), useRegisterAtStart(searchStr));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringLastIndexOf(MStringLastIndexOf* ins) {
  auto* string = ins->string();
  MOZ_ASSERT(string->type() == MIRType::String);

  auto* searchStr = ins->searchString();
  MOZ_ASSERT(searchStr->type() == MIRType::String);

  auto* lir = new (alloc()) LStringLastIndexOf(useRegisterAtStart(string),
                                               useRegisterAtStart(searchStr));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringStartsWith(MStringStartsWith* ins) {
  auto* string = ins->string();
  MOZ_ASSERT(string->type() == MIRType::String);

  auto* searchStr = ins->searchString();
  MOZ_ASSERT(searchStr->type() == MIRType::String);

  if (searchStr->isConstant()) {
    JSLinearString* linear = &searchStr->toConstant()->toString()->asLinear();

    if (MacroAssembler::canCompareStringCharsInline(linear)) {
      auto* lir = new (alloc())
          LStringStartsWithInline(useRegister(string), temp(), linear);
      define(lir, ins);
      assignSafepoint(lir, ins);
      return;
    }
  }

  auto* lir = new (alloc()) LStringStartsWith(useRegisterAtStart(string),
                                              useRegisterAtStart(searchStr));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringEndsWith(MStringEndsWith* ins) {
  auto* string = ins->string();
  MOZ_ASSERT(string->type() == MIRType::String);

  auto* searchStr = ins->searchString();
  MOZ_ASSERT(searchStr->type() == MIRType::String);

  if (searchStr->isConstant()) {
    JSLinearString* linear = &searchStr->toConstant()->toString()->asLinear();

    if (MacroAssembler::canCompareStringCharsInline(linear)) {
      auto* lir = new (alloc())
          LStringEndsWithInline(useRegister(string), temp(), linear);
      define(lir, ins);
      assignSafepoint(lir, ins);
      return;
    }
  }

  auto* lir = new (alloc()) LStringEndsWith(useRegisterAtStart(string),
                                            useRegisterAtStart(searchStr));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringConvertCase(MStringConvertCase* ins) {
  MOZ_ASSERT(ins->string()->type() == MIRType::String);

  if (ins->mode() == MStringConvertCase::LowerCase) {
#ifdef JS_CODEGEN_X86
    // Due to lack of registers on x86, we reuse the string register as
    // temporary. As a result we only need four temporary registers and take a
    // bogus temporary as the fifth argument.
    LDefinition temp4 = LDefinition::BogusTemp();
#else
    LDefinition temp4 = temp();
#endif
    auto* lir = new (alloc())
        LStringToLowerCase(useRegister(ins->string()), temp(), temp(), temp(),
                           temp4, tempByteOpRegister());
    define(lir, ins);
    assignSafepoint(lir, ins);
  } else {
    auto* lir =
        new (alloc()) LStringToUpperCase(useRegisterAtStart(ins->string()));
    defineReturn(lir, ins);
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitCharCodeConvertCase(MCharCodeConvertCase* ins) {
  MOZ_ASSERT(ins->code()->type() == MIRType::Int32);

  if (ins->mode() == MCharCodeConvertCase::LowerCase) {
    auto* lir = new (alloc())
        LCharCodeToLowerCase(useRegister(ins->code()), tempByteOpRegister());
    define(lir, ins);
    assignSafepoint(lir, ins);
  } else {
    auto* lir = new (alloc())
        LCharCodeToUpperCase(useRegister(ins->code()), tempByteOpRegister());
    define(lir, ins);
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitStringTrimStartIndex(MStringTrimStartIndex* ins) {
  auto* string = ins->string();
  MOZ_ASSERT(string->type() == MIRType::String);

  auto* lir = new (alloc()) LStringTrimStartIndex(useRegister(string));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringTrimEndIndex(MStringTrimEndIndex* ins) {
  auto* string = ins->string();
  MOZ_ASSERT(string->type() == MIRType::String);

  auto* start = ins->start();
  MOZ_ASSERT(start->type() == MIRType::Int32);

  auto* lir = new (alloc())
      LStringTrimEndIndex(useRegister(string), useRegister(start));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStart(MStart* start) {}

void LIRGenerator::visitNop(MNop* nop) {}

void LIRGenerator::visitLimitedTruncate(MLimitedTruncate* nop) {
  redefine(nop, nop->input());
}

void LIRGenerator::visitIntPtrLimitedTruncate(MIntPtrLimitedTruncate* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::IntPtr);

  // This is a no-op.
  redefine(ins, ins->input());
}

void LIRGenerator::visitInt64LimitedTruncate(MInt64LimitedTruncate* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::Int64);

  // This is a no-op.
  redefine(ins, ins->input());
}

void LIRGenerator::visitOsrEntry(MOsrEntry* entry) {
  LOsrEntry* lir = new (alloc()) LOsrEntry(temp());
  defineFixed(lir, entry, LAllocation(AnyRegister(OsrFrameReg)));
}

void LIRGenerator::visitOsrValue(MOsrValue* value) {
  LOsrValue* lir = new (alloc()) LOsrValue(useRegister(value->entry()));
  defineBox(lir, value);
}

void LIRGenerator::visitOsrReturnValue(MOsrReturnValue* value) {
  LOsrReturnValue* lir =
      new (alloc()) LOsrReturnValue(useRegister(value->entry()));
  defineBox(lir, value);
}

void LIRGenerator::visitOsrEnvironmentChain(MOsrEnvironmentChain* object) {
  LOsrEnvironmentChain* lir =
      new (alloc()) LOsrEnvironmentChain(useRegister(object->entry()));
  define(lir, object);
}

void LIRGenerator::visitOsrArgumentsObject(MOsrArgumentsObject* object) {
  LOsrArgumentsObject* lir =
      new (alloc()) LOsrArgumentsObject(useRegister(object->entry()));
  define(lir, object);
}

void LIRGenerator::visitToDouble(MToDouble* convert) {
  MDefinition* opd = convert->input();

  switch (opd->type()) {
    case MIRType::Value: {
      LValueToDouble* lir = new (alloc()) LValueToDouble(useBox(opd));
      assignSnapshot(lir, convert->bailoutKind());
      define(lir, convert);
      break;
    }

    case MIRType::Null:
      lowerConstantDouble(0, convert);
      break;

    case MIRType::Undefined:
      lowerConstantDouble(GenericNaN(), convert);
      break;

    case MIRType::Boolean:
    case MIRType::Int32: {
      LInt32ToDouble* lir =
          new (alloc()) LInt32ToDouble(useRegisterAtStart(opd));
      define(lir, convert);
      break;
    }

    case MIRType::Float32: {
      LFloat32ToDouble* lir =
          new (alloc()) LFloat32ToDouble(useRegisterAtStart(opd));
      define(lir, convert);
      break;
    }

    case MIRType::Double:
      redefine(convert, opd);
      break;

    default:
      // Objects might be effectful. Symbols will throw.
      // Strings are complicated - we don't handle them yet.
      MOZ_CRASH("unexpected type");
  }
}

void LIRGenerator::visitToFloat32(MToFloat32* convert) {
  MDefinition* opd = convert->input();

  switch (opd->type()) {
    case MIRType::Value: {
      LValueToFloat32* lir = new (alloc()) LValueToFloat32(useBox(opd));
      assignSnapshot(lir, convert->bailoutKind());
      define(lir, convert);
      break;
    }

    case MIRType::Null:
      lowerConstantFloat32(0, convert);
      break;

    case MIRType::Undefined:
      lowerConstantFloat32(GenericNaN(), convert);
      break;

    case MIRType::Boolean:
    case MIRType::Int32: {
      LInt32ToFloat32* lir =
          new (alloc()) LInt32ToFloat32(useRegisterAtStart(opd));
      define(lir, convert);
      break;
    }

    case MIRType::Double: {
      LDoubleToFloat32* lir =
          new (alloc()) LDoubleToFloat32(useRegisterAtStart(opd));
      define(lir, convert);
      break;
    }

    case MIRType::Float32:
      redefine(convert, opd);
      break;

    default:
      // Objects might be effectful. Symbols will throw.
      // Strings are complicated - we don't handle them yet.
      MOZ_CRASH("unexpected type");
  }
}

void LIRGenerator::visitToFloat16(MToFloat16* convert) {
  MDefinition* opd = convert->input();

  switch (opd->type()) {
    case MIRType::Value: {
      LDefinition tempDef = LDefinition::BogusTemp();
      if (!MacroAssembler::SupportsFloat64To16()) {
        tempDef = temp();
      }

      auto* lir = new (alloc()) LValueToFloat16(useBox(opd), tempDef);
      assignSnapshot(lir, convert->bailoutKind());
      define(lir, convert);

      if (!MacroAssembler::SupportsFloat64To16()) {
        assignSafepoint(lir, convert);
      }
      break;
    }

    case MIRType::Null:
      lowerConstantFloat32(0, convert);
      break;

    case MIRType::Undefined:
      lowerConstantFloat32(GenericNaN(), convert);
      break;

    case MIRType::Boolean:
    case MIRType::Int32: {
      LDefinition tempDef = LDefinition::BogusTemp();
      if (!MacroAssembler::SupportsFloat32To16()) {
        tempDef = temp();
      }

      auto* lir =
          new (alloc()) LInt32ToFloat16(useRegisterAtStart(opd), tempDef);
      define(lir, convert);

      if (!MacroAssembler::SupportsFloat32To16()) {
        assignSafepoint(lir, convert);
      }
      break;
    }

    case MIRType::Double: {
      if (MacroAssembler::SupportsFloat64To16()) {
        auto* lir = new (alloc())
            LDoubleToFloat16(useRegisterAtStart(opd), LDefinition::BogusTemp());
        define(lir, convert);
      } else if (MacroAssembler::SupportsFloat32To16()) {
        auto* lir = new (alloc())
            LDoubleToFloat32ToFloat16(useRegister(opd), temp(), temp());
        define(lir, convert);
      } else {
        auto* lir =
            new (alloc()) LDoubleToFloat16(useRegisterAtStart(opd), temp());
        define(lir, convert);
        assignSafepoint(lir, convert);
      }
      break;
    }

    case MIRType::Float32: {
      LDefinition tempDef = LDefinition::BogusTemp();
      if (!MacroAssembler::SupportsFloat32To16()) {
        tempDef = temp();
      }

      auto* lir =
          new (alloc()) LFloat32ToFloat16(useRegisterAtStart(opd), tempDef);
      define(lir, convert);

      if (!MacroAssembler::SupportsFloat32To16()) {
        assignSafepoint(lir, convert);
      }
      break;
    }

    default:
      // Objects might be effectful. Symbols will throw.
      // Strings are complicated - we don't handle them yet.
      MOZ_CRASH("unexpected type");
  }
}

void LIRGenerator::visitToNumberInt32(MToNumberInt32* convert) {
  MDefinition* opd = convert->input();

  switch (opd->type()) {
    case MIRType::Value: {
      auto* lir = new (alloc()) LValueToNumberInt32(useBox(opd), tempDouble());
      assignSnapshot(lir, convert->bailoutKind());
      define(lir, convert);
      break;
    }

    case MIRType::Null:
      MOZ_ASSERT(convert->conversion() == IntConversionInputKind::Any);
      define(new (alloc()) LInteger(0), convert);
      break;

    case MIRType::Boolean:
      MOZ_ASSERT(convert->conversion() == IntConversionInputKind::Any);
      redefine(convert, opd);
      break;

    case MIRType::Int32:
      redefine(convert, opd);
      break;

    case MIRType::Float32: {
      LFloat32ToInt32* lir = new (alloc()) LFloat32ToInt32(useRegister(opd));
      assignSnapshot(lir, convert->bailoutKind());
      define(lir, convert);
      break;
    }

    case MIRType::Double: {
      LDoubleToInt32* lir = new (alloc()) LDoubleToInt32(useRegister(opd));
      assignSnapshot(lir, convert->bailoutKind());
      define(lir, convert);
      break;
    }

    case MIRType::String:
    case MIRType::Symbol:
    case MIRType::BigInt:
    case MIRType::Object:
    case MIRType::Undefined:
      // Objects might be effectful. Symbols and BigInts throw. Undefined
      // coerces to NaN, not int32.
      MOZ_CRASH("ToInt32 invalid input type");

    default:
      MOZ_CRASH("unexpected type");
  }
}

void LIRGenerator::visitBooleanToInt32(MBooleanToInt32* convert) {
  MDefinition* opd = convert->input();
  MOZ_ASSERT(opd->type() == MIRType::Boolean);
  redefine(convert, opd);
}

void LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate) {
  MDefinition* opd = truncate->input();

  switch (opd->type()) {
    case MIRType::Value: {
      auto* lir = new (alloc())
          LValueTruncateToInt32(useBox(opd), tempDouble(), temp());
      assignSnapshot(lir, truncate->bailoutKind());
      define(lir, truncate);
      assignSafepoint(lir, truncate);
      break;
    }

    case MIRType::Null:
    case MIRType::Undefined:
      define(new (alloc()) LInteger(0), truncate);
      break;

    case MIRType::Int32:
    case MIRType::Boolean:
      redefine(truncate, opd);
      break;

    case MIRType::Double:
      // May call into JS::ToInt32() on the slow OOL path.
      gen->setNeedsStaticStackAlignment();
      lowerTruncateDToInt32(truncate);
      break;

    case MIRType::Float32:
      // May call into JS::ToInt32() on the slow OOL path.
      gen->setNeedsStaticStackAlignment();
      lowerTruncateFToInt32(truncate);
      break;

    default:
      // Objects might be effectful. Symbols throw.
      // Strings are complicated - we don't handle them yet.
      MOZ_CRASH("unexpected type");
  }
}

void LIRGenerator::visitInt32ToIntPtr(MInt32ToIntPtr* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::Int32);
  MOZ_ASSERT(ins->type() == MIRType::IntPtr);

#ifdef JS_64BIT
  // If the result is only used by instructions that expect a bounds-checked
  // index, we must have eliminated or hoisted a bounds check and we can assume
  // the index is non-negative. This lets us generate more efficient code.
  if (ins->canBeNegative()) {
    bool canBeNegative = false;
    for (MUseDefIterator iter(ins); iter; iter++) {
      if (iter.def()->isSpectreMaskIndex()) {
        continue;
      }
      if (iter.def()->isLoadUnboxedScalar() ||
          iter.def()->isStoreUnboxedScalar() ||
          iter.def()->isLoadDataViewElement() ||
          iter.def()->isStoreDataViewElement()) {
        MOZ_ASSERT(iter.def()->indexOf(iter.use()) == 1,
                   "unexpected non-index operand use");
        continue;
      }

      canBeNegative = true;
      break;
    }
    if (!canBeNegative) {
      ins->setCanNotBeNegative();
    }
  }

  if (ins->canBeNegative()) {
    auto* lir = new (alloc()) LInt32ToIntPtr(useAnyAtStart(input));
    define(lir, ins);
  } else {
    redefine(ins, input);
  }
#else
  // On 32-bit platforms this is a no-op.
  redefine(ins, input);
#endif
}

void LIRGenerator::visitNonNegativeIntPtrToInt32(
    MNonNegativeIntPtrToInt32* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->type() == MIRType::Int32);

#ifdef JS_64BIT
  auto* lir =
      new (alloc()) LNonNegativeIntPtrToInt32(useRegisterAtStart(input));
  assignSnapshot(lir, ins->bailoutKind());
  defineReuseInput(lir, ins, 0);
#else
  // On 32-bit platforms this is a no-op.
  redefine(ins, input);
#endif
}

void LIRGenerator::visitWasmExtendU32Index(MWasmExtendU32Index* ins) {
#ifdef JS_64BIT
  // Technically this produces an Int64 register and I guess we could clean that
  // up, but it's a 64-bit only operation, so it doesn't actually matter.

  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::Int32);
  MOZ_ASSERT(ins->type() == MIRType::Int64);

  // Input reuse is OK even on ARM64 because this node *must* reuse its input in
  // order not to generate any code at all, as is the intent.
  auto* lir = new (alloc()) LWasmExtendU32Index(useRegisterAtStart(input));
  defineReuseInput(lir, ins, 0);
#else
  MOZ_CRASH("64-bit only");
#endif
}

void LIRGenerator::visitWasmWrapU32Index(MWasmWrapU32Index* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::Int64);
  MOZ_ASSERT(ins->type() == MIRType::Int32);

  // Tricky: On 64-bit, this just returns its input (except on MIPS64 there may
  // be a sign/zero extension).  On 32-bit, it returns the low register of the
  // input, and should generate no code.

  // If this assertion does not hold then using "input" unadorned as an alias
  // for the low register will not work.
#if defined(JS_NUNBOX32)
  static_assert(INT64LOW_INDEX == 0);
#endif

  auto* lir = new (alloc()) LWasmWrapU32Index(useRegisterAtStart(input));
  defineReuseInput(lir, ins, 0);
}

void LIRGenerator::visitWasmClampTable64Address(MWasmClampTable64Address* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::Int64);
  MOZ_ASSERT(ins->type() == MIRType::Int32);

  auto* lir =
      new (alloc()) LWasmClampTable64Address(useInt64RegisterAtStart(input));
  define(lir, ins);
}

void LIRGenerator::visitIntPtrToDouble(MIntPtrToDouble* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->type() == MIRType::Double);

  auto* lir = new (alloc()) LIntPtrToDouble(useRegister(input));
  define(lir, ins);
}

void LIRGenerator::visitAdjustDataViewLength(MAdjustDataViewLength* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::IntPtr);

  auto* lir = new (alloc()) LAdjustDataViewLength(useRegisterAtStart(input));
  assignSnapshot(lir, ins->bailoutKind());
  defineReuseInput(lir, ins, 0);
}

void LIRGenerator::visitToBigInt(MToBigInt* ins) {
  MDefinition* opd = ins->input();

  switch (opd->type()) {
    case MIRType::Value: {
      auto* lir = new (alloc()) LValueToBigInt(useBox(opd));
      assignSnapshot(lir, ins->bailoutKind());
      define(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }

    case MIRType::BigInt:
      redefine(ins, opd);
      break;

    default:
      MOZ_CRASH("unexpected type");
  }
}

void LIRGenerator::visitToInt64(MToInt64* ins) {
  MDefinition* opd = ins->input();

  switch (opd->type()) {
    case MIRType::Value: {
      auto* lir = new (alloc()) LValueToInt64(useBox(opd), temp());
      assignSnapshot(lir, ins->bailoutKind());
      defineInt64(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }

    case MIRType::Boolean: {
      auto* lir = new (alloc()) LBooleanToInt64(useRegisterAtStart(opd));
      defineInt64(lir, ins);
      break;
    }

    case MIRType::String: {
      auto* lir = new (alloc()) LStringToInt64(useRegister(opd));
      defineInt64(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }

    // An Int64 may be passed here from a BigInt to Int64 conversion.
    case MIRType::Int64: {
      redefine(ins, opd);
      break;
    }

    default:
      // Undefined, Null, Number, and Symbol throw.
      // Objects may be effectful.
      // BigInt operands are eliminated by the type policy.
      MOZ_CRASH("unexpected type");
  }
}

void LIRGenerator::visitTruncateBigIntToInt64(MTruncateBigIntToInt64* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::BigInt);
  auto* lir = new (alloc()) LTruncateBigIntToInt64(useRegister(ins->input()));
  defineInt64(lir, ins);
}

void LIRGenerator::visitInt64ToBigInt(MInt64ToBigInt* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::Int64);

  if (ins->isSigned()) {
    auto* lir = new (alloc())
        LInt64ToBigInt(useInt64Register(ins->input()), tempInt64());
    define(lir, ins);
    assignSafepoint(lir, ins);
  } else {
    auto* lir =
        new (alloc()) LUint64ToBigInt(useInt64Register(ins->input()), temp());
    define(lir, ins);
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitInt64ToIntPtr(MInt64ToIntPtr* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::Int64);

#ifdef JS_64BIT
  if (ins->isSigned()) {
    redefine(ins, ins->input());
    return;
  }
#endif

  auto* lir =
      new (alloc()) LInt64ToIntPtr(useInt64RegisterAtStart(ins->input()));
  assignSnapshot(lir, ins->bailoutKind());
#ifdef JS_64BIT
  defineReuseInput(lir, ins, 0);
#else
  define(lir, ins);
#endif
}

void LIRGenerator::visitIntPtrToInt64(MIntPtrToInt64* ins) {
  MOZ_ASSERT(ins->input()->type() == MIRType::IntPtr);

#ifdef JS_64BIT
  redefine(ins, ins->input());
#else
  auto* lir = new (alloc()) LIntPtrToInt64(useRegister(ins->input()));
  defineInt64(lir, ins);
#endif
}

void LIRGenerator::visitWasmTruncateToInt32(MWasmTruncateToInt32* ins) {
  MDefinition* input = ins->input();
  switch (input->type()) {
    case MIRType::Double:
    case MIRType::Float32: {
      auto* lir = new (alloc()) LWasmTruncateToInt32(useRegisterAtStart(input));
      define(lir, ins);
      break;
    }
    default:
      MOZ_CRASH("unexpected type in WasmTruncateToInt32");
  }
}

void LIRGenerator::visitWasmBuiltinTruncateToInt32(
    MWasmBuiltinTruncateToInt32* truncate) {
  mozilla::DebugOnly<MDefinition*> opd = truncate->input();
  MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);

  // May call into JS::ToInt32() on the slow OOL path.
  gen->setNeedsStaticStackAlignment();
  lowerWasmBuiltinTruncateToInt32(truncate);
}

void LIRGenerator::visitWasmAnyRefFromJSValue(MWasmAnyRefFromJSValue* ins) {
  LWasmAnyRefFromJSValue* lir =
      new (alloc()) LWasmAnyRefFromJSValue(useBox(ins->input()), tempDouble());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitWasmAnyRefFromJSObject(MWasmAnyRefFromJSObject* ins) {
  LWasmAnyRefFromJSObject* lir =
      new (alloc()) LWasmAnyRefFromJSObject(useRegisterAtStart(ins->input()));
  define(lir, ins);
}

void LIRGenerator::visitWasmAnyRefFromJSString(MWasmAnyRefFromJSString* ins) {
  LWasmAnyRefFromJSString* lir =
      new (alloc()) LWasmAnyRefFromJSString(useRegisterAtStart(ins->input()));
  define(lir, ins);
}

void LIRGenerator::visitWasmAnyRefIsJSString(MWasmAnyRefIsJSString* ins) {
  LWasmAnyRefIsJSString* lir = new (alloc())
      LWasmAnyRefIsJSString(useRegisterAtStart(ins->input()), temp());
  define(lir, ins);
}

void LIRGenerator::visitWasmTrapIfAnyRefIsNotJSString(
    MWasmTrapIfAnyRefIsNotJSString* ins) {
  LWasmTrapIfAnyRefIsNotJSString* lir = new (alloc())
      LWasmTrapIfAnyRefIsNotJSString(useRegisterAtStart(ins->input()), temp());
  add(lir, ins);
}

void LIRGenerator::visitWasmAnyRefJSStringLength(
    MWasmAnyRefJSStringLength* ins) {
  LWasmAnyRefJSStringLength* lir = new (alloc())
      LWasmAnyRefJSStringLength(useRegisterAtStart(ins->input()), temp());
  define(lir, ins);
}

void LIRGenerator::visitWasmNewI31Ref(MWasmNewI31Ref* ins) {
  // If it's a constant, it will be put directly into the register.
  LWasmNewI31Ref* lir =
      new (alloc()) LWasmNewI31Ref(useRegisterOrConstant(ins->input()));
  define(lir, ins);
}

void LIRGenerator::visitWasmI31RefGet(MWasmI31RefGet* ins) {
  LWasmI31RefGet* lir = new (alloc()) LWasmI31RefGet(useRegister(ins->input()));
  define(lir, ins);
}

void LIRGenerator::visitWrapInt64ToInt32(MWrapInt64ToInt32* ins) {
  define(new (alloc()) LWrapInt64ToInt32(useInt64AtStart(ins->input())), ins);
}

void LIRGenerator::visitToString(MToString* ins) {
  MDefinition* opd = ins->input();

  switch (opd->type()) {
    case MIRType::Null: {
      const JSAtomState& names = gen->runtime->names();
      LPointer* lir = new (alloc()) LPointer(names.null);
      define(lir, ins);
      break;
    }

    case MIRType::Undefined: {
      const JSAtomState& names = gen->runtime->names();
      LPointer* lir = new (alloc()) LPointer(names.undefined);
      define(lir, ins);
      break;
    }

    case MIRType::Boolean: {
      LBooleanToString* lir = new (alloc()) LBooleanToString(useRegister(opd));
      define(lir, ins);
      break;
    }

    case MIRType::Double: {
      LDoubleToString* lir =
          new (alloc()) LDoubleToString(useRegister(opd), temp());

      define(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }

    case MIRType::Int32: {
      LIntToString* lir = new (alloc()) LIntToString(useRegister(opd));

      define(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }

    case MIRType::String:
      redefine(ins, ins->input());
      break;

    case MIRType::Value: {
      LValueToString* lir =
          new (alloc()) LValueToString(useBox(opd), tempToUnbox());
      if (ins->needsSnapshot()) {
        assignSnapshot(lir, ins->bailoutKind());
      }
      define(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }

    default:
      // Float32, symbols, bigint, and objects are not supported.
      MOZ_CRASH("unexpected type");
  }
}

void LIRGenerator::visitRegExp(MRegExp* ins) {
  LRegExp* lir = new (alloc()) LRegExp(temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitRegExpMatcher(MRegExpMatcher* ins) {
  MOZ_ASSERT(ins->regexp()->type() == MIRType::Object);
  MOZ_ASSERT(ins->string()->type() == MIRType::String);
  MOZ_ASSERT(ins->lastIndex()->type() == MIRType::Int32);

  LRegExpMatcher* lir = new (alloc()) LRegExpMatcher(
      useFixedAtStart(ins->regexp(), RegExpMatcherRegExpReg),
      useFixedAtStart(ins->string(), RegExpMatcherStringReg),
      useFixedAtStart(ins->lastIndex(), RegExpMatcherLastIndexReg));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitRegExpSearcher(MRegExpSearcher* ins) {
  MOZ_ASSERT(ins->regexp()->type() == MIRType::Object);
  MOZ_ASSERT(ins->string()->type() == MIRType::String);
  MOZ_ASSERT(ins->lastIndex()->type() == MIRType::Int32);

  LRegExpSearcher* lir = new (alloc()) LRegExpSearcher(
      useFixedAtStart(ins->regexp(), RegExpSearcherRegExpReg),
      useFixedAtStart(ins->string(), RegExpSearcherStringReg),
      useFixedAtStart(ins->lastIndex(), RegExpSearcherLastIndexReg));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitRegExpSearcherLastLimit(MRegExpSearcherLastLimit* ins) {
  auto* lir = new (alloc()) LRegExpSearcherLastLimit(temp());
  define(lir, ins);
}

void LIRGenerator::visitRegExpExecMatch(MRegExpExecMatch* ins) {
  MOZ_ASSERT(ins->regexp()->type() == MIRType::Object);
  MOZ_ASSERT(ins->string()->type() == MIRType::String);

  auto* lir = new (alloc())
      LRegExpExecMatch(useFixedAtStart(ins->regexp(), RegExpMatcherRegExpReg),
                       useFixedAtStart(ins->string(), RegExpMatcherStringReg));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitRegExpExecTest(MRegExpExecTest* ins) {
  MOZ_ASSERT(ins->regexp()->type() == MIRType::Object);
  MOZ_ASSERT(ins->string()->type() == MIRType::String);

  auto* lir = new (alloc())
      LRegExpExecTest(useFixedAtStart(ins->regexp(), RegExpExecTestRegExpReg),
                      useFixedAtStart(ins->string(), RegExpExecTestStringReg));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitRegExpHasCaptureGroups(MRegExpHasCaptureGroups* ins) {
  MOZ_ASSERT(ins->regexp()->type() == MIRType::Object);
  MOZ_ASSERT(ins->input()->type() == MIRType::String);
  MOZ_ASSERT(ins->type() == MIRType::Boolean);

  auto* lir = new (alloc()) LRegExpHasCaptureGroups(useRegister(ins->regexp()),
                                                    useRegister(ins->input()));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitRegExpPrototypeOptimizable(
    MRegExpPrototypeOptimizable* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Boolean);
  LRegExpPrototypeOptimizable* lir = new (alloc())
      LRegExpPrototypeOptimizable(useRegister(ins->object()), temp());
  define(lir, ins);
}

void LIRGenerator::visitRegExpInstanceOptimizable(
    MRegExpInstanceOptimizable* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->proto()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Boolean);
  LRegExpInstanceOptimizable* lir = new (alloc()) LRegExpInstanceOptimizable(
      useRegister(ins->object()), useRegister(ins->proto()), temp());
  define(lir, ins);
}

void LIRGenerator::visitGetFirstDollarIndex(MGetFirstDollarIndex* ins) {
  MOZ_ASSERT(ins->str()->type() == MIRType::String);
  MOZ_ASSERT(ins->type() == MIRType::Int32);
  LGetFirstDollarIndex* lir = new (alloc())
      LGetFirstDollarIndex(useRegister(ins->str()), temp(), temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringReplace(MStringReplace* ins) {
  MOZ_ASSERT(ins->pattern()->type() == MIRType::String);
  MOZ_ASSERT(ins->string()->type() == MIRType::String);
  MOZ_ASSERT(ins->replacement()->type() == MIRType::String);

  LStringReplace* lir = new (alloc())
      LStringReplace(useRegisterOrConstantAtStart(ins->string()),
                     useRegisterAtStart(ins->pattern()),
                     useRegisterOrConstantAtStart(ins->replacement()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBinaryCache(MBinaryCache* ins) {
  MDefinition* lhs = ins->getOperand(0);
  MDefinition* rhs = ins->getOperand(1);

  MOZ_ASSERT(ins->type() == MIRType::Value || ins->type() == MIRType::Boolean);
  LInstruction* lir;
  if (ins->type() == MIRType::Value) {
    LBinaryValueCache* valueLir = new (alloc()) LBinaryValueCache(
        useBox(lhs), useBox(rhs), tempFixed(FloatReg0), tempFixed(FloatReg1));
    defineBox(valueLir, ins);
    lir = valueLir;
  } else {
    MOZ_ASSERT(ins->type() == MIRType::Boolean);
    LBinaryBoolCache* boolLir = new (alloc()) LBinaryBoolCache(
        useBox(lhs), useBox(rhs), tempFixed(FloatReg0), tempFixed(FloatReg1));
    define(boolLir, ins);
    lir = boolLir;
  }
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitUnaryCache(MUnaryCache* ins) {
  MDefinition* input = ins->getOperand(0);
  MOZ_ASSERT(ins->type() == MIRType::Value);

  LUnaryCache* lir = new (alloc()) LUnaryCache(useBox(input));
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitModuleMetadata(MModuleMetadata* ins) {
  LModuleMetadata* lir = new (alloc()) LModuleMetadata();
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitDynamicImport(MDynamicImport* ins) {
  LDynamicImport* lir = new (alloc()) LDynamicImport(
      useBoxAtStart(ins->specifier()), useBoxAtStart(ins->options()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitLambda(MLambda* ins) {
  MOZ_ASSERT(ins->environmentChain()->type() == MIRType::Object);

  auto* lir =
      new (alloc()) LLambda(useRegister(ins->environmentChain()), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitFunctionWithProto(MFunctionWithProto* ins) {
  MOZ_ASSERT(ins->environmentChain()->type() == MIRType::Object);
  MOZ_ASSERT(ins->prototype()->type() == MIRType::Object);

  auto* lir = new (alloc())
      LFunctionWithProto(useRegisterAtStart(ins->environmentChain()),
                         useRegisterAtStart(ins->prototype()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitSetFunName(MSetFunName* ins) {
  MOZ_ASSERT(ins->fun()->type() == MIRType::Object);
  MOZ_ASSERT(ins->name()->type() == MIRType::Value);

  LSetFunName* lir = new (alloc())
      LSetFunName(useRegisterAtStart(ins->fun()), useBoxAtStart(ins->name()));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewLexicalEnvironmentObject(
    MNewLexicalEnvironmentObject* ins) {
  auto* lir = new (alloc()) LNewLexicalEnvironmentObject(temp());

  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewClassBodyEnvironmentObject(
    MNewClassBodyEnvironmentObject* ins) {
  auto* lir = new (alloc()) LNewClassBodyEnvironmentObject(temp());

  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewVarEnvironmentObject(MNewVarEnvironmentObject* ins) {
  auto* lir = new (alloc()) LNewVarEnvironmentObject(temp());

  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitKeepAliveObject(MKeepAliveObject* ins) {
  MDefinition* obj = ins->object();
  MOZ_ASSERT(obj->type() == MIRType::Object);

  add(new (alloc()) LKeepAliveObject(useKeepalive(obj)), ins);
}

void LIRGenerator::visitDebugEnterGCUnsafeRegion(
    MDebugEnterGCUnsafeRegion* ins) {
  add(new (alloc()) LDebugEnterGCUnsafeRegion(temp()), ins);
}

void LIRGenerator::visitDebugLeaveGCUnsafeRegion(
    MDebugLeaveGCUnsafeRegion* ins) {
  add(new (alloc()) LDebugLeaveGCUnsafeRegion(temp()), ins);
}

void LIRGenerator::visitSlots(MSlots* ins) {
  define(new (alloc()) LSlots(useRegisterAtStart(ins->object())), ins);
}

void LIRGenerator::visitElements(MElements* ins) {
  define(new (alloc()) LElements(useRegisterAtStart(ins->object())), ins);
}

void LIRGenerator::visitLoadDynamicSlot(MLoadDynamicSlot* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Value);
  if (ins->usedAsPropertyKey()) {
    auto* lir = new (alloc())
        LLoadDynamicSlotAndAtomize(useRegister(ins->slots()), temp());
    defineBox(lir, ins);
    assignSafepoint(lir, ins);
  } else {
    defineBox(new (alloc()) LLoadDynamicSlotV(useRegisterAtStart(ins->slots())),
              ins);
  }
}

void LIRGenerator::visitFunctionEnvironment(MFunctionEnvironment* ins) {
  define(new (alloc())
             LFunctionEnvironment(useRegisterAtStart(ins->function())),
         ins);
}

void LIRGenerator::visitHomeObject(MHomeObject* ins) {
  define(new (alloc()) LHomeObject(useRegisterAtStart(ins->function())), ins);
}

void LIRGenerator::visitHomeObjectSuperBase(MHomeObjectSuperBase* ins) {
  MOZ_ASSERT(ins->homeObject()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Value);

  auto lir =
      new (alloc()) LHomeObjectSuperBase(useRegisterAtStart(ins->homeObject()));
  defineBox(lir, ins);
}

void LIRGenerator::visitInterruptCheck(MInterruptCheck* ins) {
  LInstruction* lir = new (alloc()) LInterruptCheck();
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitWasmInterruptCheck(MWasmInterruptCheck* ins) {
  auto* lir =
      new (alloc()) LWasmInterruptCheck(useRegisterAtStart(ins->instance()));
  add(lir, ins);
  assignWasmSafepoint(lir);
}

void LIRGenerator::visitWasmTrap(MWasmTrap* ins) {
  add(new (alloc()) LWasmTrap, ins);
}

void LIRGenerator::visitWasmTrapIfNull(MWasmTrapIfNull* ins) {
  auto* lir = new (alloc()) LWasmTrapIfNull(useRegister(ins->ref()));
  add(lir, ins);
}

void LIRGenerator::visitReinterpretCast(MReinterpretCast* ins) {
  if (ins->type() == MIRType::Int64) {
    defineInt64(new (alloc())
                    LReinterpretCastToI64(useRegisterAtStart(ins->input())),
                ins);
  } else if (ins->input()->type() == MIRType::Int64) {
    define(new (alloc())
               LReinterpretCastFromI64(useInt64RegisterAtStart(ins->input())),
           ins);
  } else {
    define(new (alloc()) LReinterpretCast(useRegisterAtStart(ins->input())),
           ins);
  }
}

void LIRGenerator::visitStoreDynamicSlot(MStoreDynamicSlot* ins) {
  LInstruction* lir;

  switch (ins->value()->type()) {
    case MIRType::Value:
      lir = new (alloc())
          LStoreDynamicSlotV(useRegister(ins->slots()), useBox(ins->value()));
      add(lir, ins);
      break;

    case MIRType::Double:
      add(new (alloc()) LStoreDynamicSlotT(useRegister(ins->slots()),
                                           useRegister(ins->value())),
          ins);
      break;

    case MIRType::Float32:
      MOZ_CRASH("Float32 shouldn't be stored in a slot.");

    default:
      add(new (alloc()) LStoreDynamicSlotT(useRegister(ins->slots()),
                                           useRegisterOrConstant(ins->value())),
          ins);
      break;
  }
}

// Returns true iff |def| is a constant that's either not a GC thing or is not
// allocated in the nursery.
static bool IsNonNurseryConstant(MDefinition* def) {
  if (!def->isConstant()) {
    return false;
  }
  Value v = def->toConstant()->toJSValue();
  return !v.isGCThing() || !IsInsideNursery(v.toGCThing());
}

void LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  // LPostWriteBarrier assumes that if it has a constant object then that
  // object is tenured, and does not need to be tested for being in the
  // nursery. Ensure that assumption holds by lowering constant nursery
  // objects to a register.
  bool useConstantObject = IsNonNurseryConstant(ins->object());

  switch (ins->value()->type()) {
    case MIRType::Object: {
      LDefinition tmp =
          needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
      LPostWriteBarrierO* lir = new (alloc())
          LPostWriteBarrierO(useConstantObject ? useOrConstant(ins->object())
                                               : useRegister(ins->object()),
                             useRegister(ins->value()), tmp);
      add(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }
    case MIRType::String: {
      LDefinition tmp =
          needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
      LPostWriteBarrierS* lir = new (alloc())
          LPostWriteBarrierS(useConstantObject ? useOrConstant(ins->object())
                                               : useRegister(ins->object()),
                             useRegister(ins->value()), tmp);
      add(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }
    case MIRType::BigInt: {
      LDefinition tmp =
          needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
      auto* lir = new (alloc())
          LPostWriteBarrierBI(useConstantObject ? useOrConstant(ins->object())
                                                : useRegister(ins->object()),
                              useRegister(ins->value()), tmp);
      add(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }
    case MIRType::Value: {
      LDefinition tmp =
          needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
      LPostWriteBarrierV* lir = new (alloc())
          LPostWriteBarrierV(useConstantObject ? useOrConstant(ins->object())
                                               : useRegister(ins->object()),
                             useBox(ins->value()), tmp);
      add(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }
    default:
      // Currently, only objects and strings can be in the nursery. Other
      // instruction types cannot hold nursery pointers.
      break;
  }
}

void LIRGenerator::visitPostWriteElementBarrier(MPostWriteElementBarrier* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);

  // LPostWriteElementBarrier assumes that if it has a constant object then that
  // object is tenured, and does not need to be tested for being in the
  // nursery. Ensure that assumption holds by lowering constant nursery
  // objects to a register.
  bool useConstantObject =
      ins->object()->isConstant() &&
      !IsInsideNursery(&ins->object()->toConstant()->toObject());

  switch (ins->value()->type()) {
    case MIRType::Object: {
      LDefinition tmp =
          needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
      LPostWriteElementBarrierO* lir = new (alloc()) LPostWriteElementBarrierO(
          useConstantObject ? useOrConstant(ins->object())
                            : useRegister(ins->object()),
          useRegister(ins->value()), useRegister(ins->index()), tmp);
      add(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }
    case MIRType::String: {
      LDefinition tmp =
          needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
      LPostWriteElementBarrierS* lir = new (alloc()) LPostWriteElementBarrierS(
          useConstantObject ? useOrConstant(ins->object())
                            : useRegister(ins->object()),
          useRegister(ins->value()), useRegister(ins->index()), tmp);
      add(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }
    case MIRType::BigInt: {
      LDefinition tmp =
          needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
      auto* lir = new (alloc()) LPostWriteElementBarrierBI(
          useConstantObject ? useOrConstant(ins->object())
                            : useRegister(ins->object()),
          useRegister(ins->value()), useRegister(ins->index()), tmp);
      add(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }
    case MIRType::Value: {
      LDefinition tmp =
          needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
      LPostWriteElementBarrierV* lir = new (alloc()) LPostWriteElementBarrierV(
          useConstantObject ? useOrConstant(ins->object())
                            : useRegister(ins->object()),
          useRegister(ins->index()), useBox(ins->value()), tmp);
      add(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }
    default:
      // Currently, only objects, strings, and bigints can be in the nursery.
      // Other instruction types cannot hold nursery pointers.
      break;
  }
}

void LIRGenerator::visitAssertCanElidePostWriteBarrier(
    MAssertCanElidePostWriteBarrier* ins) {
  auto* lir = new (alloc()) LAssertCanElidePostWriteBarrier(
      useRegister(ins->object()), useBox(ins->value()), temp());
  add(lir, ins);
}

void LIRGenerator::visitArrayLength(MArrayLength* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  auto* lir = new (alloc()) LArrayLength(useRegisterAtStart(ins->elements()));
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitSetArrayLength(MSetArrayLength* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);

  MOZ_ASSERT(ins->index()->isConstant());
  add(new (alloc()) LSetArrayLength(useRegister(ins->elements()),
                                    useRegisterOrConstant(ins->index())),
      ins);
}

void LIRGenerator::visitFunctionLength(MFunctionLength* ins) {
  MOZ_ASSERT(ins->function()->type() == MIRType::Object);

  auto* lir = new (alloc()) LFunctionLength(useRegister(ins->function()));
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitFunctionName(MFunctionName* ins) {
  MOZ_ASSERT(ins->function()->type() == MIRType::Object);

  auto* lir = new (alloc()) LFunctionName(useRegister(ins->function()));
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitGetNextEntryForIterator(MGetNextEntryForIterator* ins) {
  MOZ_ASSERT(ins->iter()->type() == MIRType::Object);
  MOZ_ASSERT(ins->result()->type() == MIRType::Object);
  auto lir = new (alloc()) LGetNextEntryForIterator(useRegister(ins->iter()),
                                                    useRegister(ins->result()),
                                                    temp(), temp(), temp());
  define(lir, ins);
}

static auto SynchronizeLoad(MemoryBarrierRequirement requiresBarrier) {
  if (requiresBarrier == MemoryBarrierRequirement::Required) {
    return Synchronization::Load();
  }
  return Synchronization::None();
}

static auto SynchronizeStore(MemoryBarrierRequirement requiresBarrier) {
  if (requiresBarrier == MemoryBarrierRequirement::Required) {
    return Synchronization::Store();
  }
  return Synchronization::None();
}

void LIRGenerator::visitArrayBufferByteLength(MArrayBufferByteLength* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::IntPtr);

  auto* lir =
      new (alloc()) LArrayBufferByteLength(useRegisterAtStart(ins->object()));
  define(lir, ins);
}

void LIRGenerator::visitArrayBufferViewLength(MArrayBufferViewLength* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::IntPtr);

  auto* lir =
      new (alloc()) LArrayBufferViewLength(useRegisterAtStart(ins->object()));
  define(lir, ins);
}

void LIRGenerator::visitArrayBufferViewByteOffset(
    MArrayBufferViewByteOffset* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::IntPtr);

  auto* lir = new (alloc())
      LArrayBufferViewByteOffset(useRegisterAtStart(ins->object()));
  define(lir, ins);
}

void LIRGenerator::visitArrayBufferViewElements(MArrayBufferViewElements* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Elements);
  define(new (alloc())
             LArrayBufferViewElements(useRegisterAtStart(ins->object())),
         ins);
}

void LIRGenerator::visitTypedArrayElementSize(MTypedArrayElementSize* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  define(new (alloc())
             LTypedArrayElementSize(useRegisterAtStart(ins->object())),
         ins);
}

void LIRGenerator::visitResizableTypedArrayLength(
    MResizableTypedArrayLength* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::IntPtr);

  auto sync = SynchronizeLoad(ins->requiresMemoryBarrier());
  auto* lir = new (alloc())
      LResizableTypedArrayLength(useRegister(ins->object()), temp(), sync);
  define(lir, ins);
}

void LIRGenerator::visitResizableTypedArrayByteOffsetMaybeOutOfBounds(
    MResizableTypedArrayByteOffsetMaybeOutOfBounds* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::IntPtr);

  auto* lir = new (alloc()) LResizableTypedArrayByteOffsetMaybeOutOfBounds(
      useRegister(ins->object()), temp());
  define(lir, ins);
}

void LIRGenerator::visitResizableDataViewByteLength(
    MResizableDataViewByteLength* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::IntPtr);

  auto sync = SynchronizeLoad(ins->requiresMemoryBarrier());
  auto* lir = new (alloc())
      LResizableDataViewByteLength(useRegister(ins->object()), temp(), sync);
  define(lir, ins);
}

void LIRGenerator::visitGrowableSharedArrayBufferByteLength(
    MGrowableSharedArrayBufferByteLength* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::IntPtr);

  auto* lir = new (alloc())
      LGrowableSharedArrayBufferByteLength(useRegisterAtStart(ins->object()));
  define(lir, ins);
}

void LIRGenerator::visitGuardResizableArrayBufferViewInBounds(
    MGuardResizableArrayBufferViewInBounds* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc()) LGuardResizableArrayBufferViewInBounds(
      useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached(
    MGuardResizableArrayBufferViewInBoundsOrDetached* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc()) LGuardResizableArrayBufferViewInBoundsOrDetached(
      useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardHasAttachedArrayBuffer(
    MGuardHasAttachedArrayBuffer* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc())
      LGuardHasAttachedArrayBuffer(useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardNumberToIntPtrIndex(
    MGuardNumberToIntPtrIndex* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::Double);

  auto* lir = new (alloc()) LGuardNumberToIntPtrIndex(useRegister(input));
  if (!ins->supportOOB()) {
    assignSnapshot(lir, ins->bailoutKind());
  }
  define(lir, ins);
}

void LIRGenerator::visitInitializedLength(MInitializedLength* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  define(new (alloc()) LInitializedLength(useRegisterAtStart(ins->elements())),
         ins);
}

void LIRGenerator::visitSetInitializedLength(MSetInitializedLength* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);

  MOZ_ASSERT(ins->index()->isConstant());
  add(new (alloc()) LSetInitializedLength(useRegister(ins->elements()),
                                          useRegisterOrConstant(ins->index())),
      ins);
}

void LIRGenerator::visitNot(MNot* ins) {
  MDefinition* op = ins->input();

  // String is converted to length of string in the type analysis phase (see
  // TestPolicy).
  MOZ_ASSERT(op->type() != MIRType::String);

  // - boolean: x xor 1
  // - int32: LCompare(x, 0)
  // - double: LCompare(x, 0)
  // - null or undefined: true
  // - symbol: false
  // - bigint: LNotBI(x)
  // - object: false if it never emulates undefined, else LNotO(x)
  switch (op->type()) {
    case MIRType::Boolean: {
      MConstant* cons = MConstant::New(alloc(), Int32Value(1));
      ins->block()->insertBefore(ins, cons);
      lowerForALU(new (alloc()) LBitOpI(JSOp::BitXor), ins, op, cons);
      break;
    }
    case MIRType::Int32:
      define(new (alloc()) LNotI(useRegisterAtStart(op)), ins);
      break;
    case MIRType::IntPtr:
      define(new (alloc()) LNotIPtr(useRegisterAtStart(op)), ins);
      break;
    case MIRType::Int64:
      define(new (alloc()) LNotI64(useInt64RegisterAtStart(op)), ins);
      break;
    case MIRType::Double:
      define(new (alloc()) LNotD(useRegister(op)), ins);
      break;
    case MIRType::Float32:
      define(new (alloc()) LNotF(useRegister(op)), ins);
      break;
    case MIRType::Undefined:
    case MIRType::Null:
      define(new (alloc()) LInteger(1), ins);
      break;
    case MIRType::Symbol:
      define(new (alloc()) LInteger(0), ins);
      break;
    case MIRType::BigInt:
      define(new (alloc()) LNotBI(useRegisterAtStart(op)), ins);
      break;
    case MIRType::Object:
      define(new (alloc()) LNotO(useRegister(op)), ins);
      break;
    case MIRType::Value: {
      auto* lir = new (alloc()) LNotV(useBox(op), tempDouble(), tempToUnbox());
      define(lir, ins);
      break;
    }

    default:
      MOZ_CRASH("Unexpected MIRType.");
  }
}

void LIRGenerator::visitBoundsCheck(MBoundsCheck* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Int32 || ins->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->index()->type() == ins->type());
  MOZ_ASSERT(ins->length()->type() == ins->type());

  if (!ins->fallible()) {
    return;
  }

  LInstruction* check;
  if (ins->minimum() || ins->maximum()) {
    check = new (alloc())
        LBoundsCheckRange(useRegisterOrInt32Constant(ins->index()),
                          useAny(ins->length()), temp());
  } else {
    check = new (alloc()) LBoundsCheck(useRegisterOrInt32Constant(ins->index()),
                                       useAnyOrInt32Constant(ins->length()));
  }
  assignSnapshot(check, ins->bailoutKind());
  add(check, ins);
}

void LIRGenerator::visitSpectreMaskIndex(MSpectreMaskIndex* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Int32 || ins->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->index()->type() == ins->type());
  MOZ_ASSERT(ins->length()->type() == ins->type());

  auto* lir = new (alloc())
      LSpectreMaskIndex(useRegister(ins->index()), useAny(ins->length()));
  define(lir, ins);
}

void LIRGenerator::visitBoundsCheckLower(MBoundsCheckLower* ins) {
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);

  if (!ins->fallible()) {
    return;
  }

  LInstruction* check =
      new (alloc()) LBoundsCheckLower(useRegister(ins->index()));
  assignSnapshot(check, ins->bailoutKind());
  add(check, ins);
}

void LIRGenerator::visitInArray(MInArray* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->initLength()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->type() == MIRType::Boolean);

  auto* lir = new (alloc()) LInArray(useRegister(ins->elements()),
                                     useRegisterOrConstant(ins->index()),
                                     useRegister(ins->initLength()));
  if (ins->needsNegativeIntCheck()) {
    assignSnapshot(lir, ins->bailoutKind());
  }
  define(lir, ins);
}

void LIRGenerator::visitGuardElementNotHole(MGuardElementNotHole* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);

  auto* guard = new (alloc())
      LGuardElementNotHole(useRegisterAtStart(ins->elements()),
                           useRegisterOrConstantAtStart(ins->index()));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
}

void LIRGenerator::visitLoadElement(MLoadElement* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->type() == MIRType::Value);

  auto* lir = new (alloc()) LLoadElementV(useRegister(ins->elements()),
                                          useRegisterOrConstant(ins->index()));
  assignSnapshot(lir, ins->bailoutKind());
  defineBox(lir, ins);
}

void LIRGenerator::visitLoadElementHole(MLoadElementHole* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->initLength()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->type() == MIRType::Value);

  LLoadElementHole* lir = new (alloc())
      LLoadElementHole(useRegister(ins->elements()), useRegister(ins->index()),
                       useRegister(ins->initLength()));
  if (ins->needsNegativeIntCheck()) {
    assignSnapshot(lir, ins->bailoutKind());
  }
  defineBox(lir, ins);
}

void LIRGenerator::visitStoreElement(MStoreElement* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);

  const LUse elements = useRegister(ins->elements());
  const LAllocation index = useRegisterOrConstant(ins->index());

  switch (ins->value()->type()) {
    case MIRType::Value: {
      LInstruction* lir =
          new (alloc()) LStoreElementV(elements, index, useBox(ins->value()));
      if (ins->fallible()) {
        assignSnapshot(lir, ins->bailoutKind());
      }
      add(lir, ins);
      break;
    }

    default: {
      const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
      LInstruction* lir = new (alloc()) LStoreElementT(elements, index, value);
      if (ins->fallible()) {
        assignSnapshot(lir, ins->bailoutKind());
      }
      add(lir, ins);
      break;
    }
  }
}

void LIRGenerator::visitStoreHoleValueElement(MStoreHoleValueElement* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);

  auto* lir = new (alloc()) LStoreHoleValueElement(useRegister(ins->elements()),
                                                   useRegister(ins->index()));
  add(lir, ins);
}

static bool BoundsCheckNeedsSpectreTemp() {
  // On x86, spectreBoundsCheck32 can emit better code if it has a scratch
  // register and index masking is enabled.
#ifdef JS_CODEGEN_X86
  return JitOptions.spectreIndexMasking;
#else
  return false;
#endif
}

void LIRGenerator::visitStoreElementHole(MStoreElementHole* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::Int32);

  const LUse object = useRegister(ins->object());
  const LUse elements = useRegister(ins->elements());
  const LAllocation index = useRegister(ins->index());

  LInstruction* lir;
  switch (ins->value()->type()) {
    case MIRType::Value:
      lir = new (alloc()) LStoreElementHoleV(object, elements, index,
                                             useBox(ins->value()), temp());
      break;

    default: {
      const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
      lir = new (alloc())
          LStoreElementHoleT(object, elements, index, value, temp());
      break;
    }
  }

  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitEffectiveAddress(MEffectiveAddress* ins) {
  define(new (alloc()) LEffectiveAddress(useRegister(ins->base()),
                                         useRegister(ins->index())),
         ins);
}

void LIRGenerator::visitArrayPopShift(MArrayPopShift* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Value);

  auto* lir =
      new (alloc()) LArrayPopShift(useRegister(ins->object()), temp(), temp());
  assignSnapshot(lir, ins->bailoutKind());
  defineBox(lir, ins);

  if (ins->mode() == MArrayPopShift::Shift) {
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitArrayPush(MArrayPush* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Int32);
  MOZ_ASSERT(ins->value()->type() == MIRType::Value);

  LUse object = useRegister(ins->object());

  LDefinition spectreTemp =
      BoundsCheckNeedsSpectreTemp() ? temp() : LDefinition::BogusTemp();

  auto* lir = new (alloc())
      LArrayPush(object, useBox(ins->value()), temp(), spectreTemp);
  // We will bailout before pushing if the length would overflow INT32_MAX.
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitArraySlice(MArraySlice* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Object);
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->begin()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->end()->type() == MIRType::Int32);

  LArraySlice* lir = new (alloc()) LArraySlice(
      useRegisterAtStart(ins->object()), useRegisterAtStart(ins->begin()),
      useRegisterAtStart(ins->end()), tempFixed(CallTempReg0),
      tempFixed(CallTempReg1));
  assignSnapshot(lir, ins->bailoutKind());
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitArgumentsSlice(MArgumentsSlice* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Object);
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->begin()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->end()->type() == MIRType::Int32);

  auto* lir = new (alloc()) LArgumentsSlice(
      useRegisterAtStart(ins->object()), useRegisterAtStart(ins->begin()),
      useRegisterAtStart(ins->end()), tempFixed(CallTempReg0),
      tempFixed(CallTempReg1));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitFrameArgumentsSlice(MFrameArgumentsSlice* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Object);
  MOZ_ASSERT(ins->begin()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->count()->type() == MIRType::Int32);

  auto* lir = new (alloc()) LFrameArgumentsSlice(
      useRegister(ins->begin()), useRegister(ins->count()), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitInlineArgumentsSlice(MInlineArgumentsSlice* ins) {
  LAllocation begin = useRegisterOrConstant(ins->begin());
  LAllocation count = useRegisterOrConstant(ins->count());
  uint32_t numActuals = ins->numActuals();
  uint32_t numOperands =
      numActuals * BOX_PIECES + LInlineArgumentsSlice::NumNonArgumentOperands;

  auto* lir = allocateVariadic<LInlineArgumentsSlice>(numOperands, temp());
  if (!lir) {
    abort(AbortReason::Alloc, "OOM: LIRGenerator::visitInlineArgumentsSlice");
    return;
  }

  lir->setOperand(LInlineArgumentsSlice::Begin, begin);
  lir->setOperand(LInlineArgumentsSlice::Count, count);
  for (uint32_t i = 0; i < numActuals; i++) {
    MDefinition* arg = ins->getArg(i);
    uint32_t index = LInlineArgumentsSlice::ArgIndex(i);
    lir->setBoxOperand(index,
                       useBoxOrTypedOrConstant(arg, /*useConstant = */ true));
  }
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNormalizeSliceTerm(MNormalizeSliceTerm* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Int32);
  MOZ_ASSERT(ins->value()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->length()->type() == MIRType::Int32);

  auto* lir = new (alloc()) LNormalizeSliceTerm(useRegister(ins->value()),
                                                useRegister(ins->length()));
  define(lir, ins);
}

void LIRGenerator::visitArrayJoin(MArrayJoin* ins) {
  MOZ_ASSERT(ins->type() == MIRType::String);
  MOZ_ASSERT(ins->array()->type() == MIRType::Object);
  MOZ_ASSERT(ins->sep()->type() == MIRType::String);

  auto* lir = new (alloc())
      LArrayJoin(useRegisterAtStart(ins->array()),
                 useRegisterAtStart(ins->sep()), tempFixed(CallTempReg0));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitObjectKeys(MObjectKeys* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Object);

  auto* lir = new (alloc()) LObjectKeys(useRegisterAtStart(ins->object()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitObjectKeysLength(MObjectKeysLength* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Int32);

  auto* lir =
      new (alloc()) LObjectKeysLength(useRegisterAtStart(ins->object()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringSplit(MStringSplit* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Object);
  MOZ_ASSERT(ins->string()->type() == MIRType::String);
  MOZ_ASSERT(ins->separator()->type() == MIRType::String);

  LStringSplit* lir = new (alloc()) LStringSplit(
      useRegisterAtStart(ins->string()), useRegisterAtStart(ins->separator()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr);
  MOZ_ASSERT(IsNumberType(ins->type()));

  auto sync = SynchronizeLoad(ins->requiresMemoryBarrier());

  if (Scalar::isBigIntType(ins->storageType()) && !sync.isNone()) {
    lowerAtomicLoad64(ins);
    return;
  }

  const LUse elements = useRegister(ins->elements());
  const LAllocation index = useRegisterOrIndexConstant(
      ins->index(), ins->storageType(), ins->offsetAdjustment());

  if (Scalar::isBigIntType(ins->storageType())) {
    MOZ_ASSERT(ins->type() == MIRType::Int64);

    auto* lir = new (alloc()) LLoadUnboxedInt64(elements, index);
    defineInt64(lir, ins);
    return;
  }

  // NOTE: the generated code must match the assembly code in gen_load in
  // GenerateAtomicOperations.py
  if (!sync.isNone()) {
    LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierBefore);
    add(fence, ins);
  }

  // We need a temp register for Uint32Array with known double result and for
  // Float16Array.
  LDefinition temp1 = LDefinition::BogusTemp();
  if ((ins->storageType() == Scalar::Uint32 &&
       IsFloatingPointType(ins->type())) ||
      ins->storageType() == Scalar::Float16) {
    temp1 = temp();
  }

  // Additional temp when Float16 to Float32 conversion requires a call.
  LDefinition temp2 = LDefinition::BogusTemp();
  if (MacroAssembler::LoadRequiresCall(ins->storageType())) {
    temp2 = temp();
  }

  auto* lir = new (alloc()) LLoadUnboxedScalar(elements, index, temp1, temp2);
  if (ins->fallible()) {
    assignSnapshot(lir, ins->bailoutKind());
  }
  define(lir, ins);

  if (MacroAssembler::LoadRequiresCall(ins->storageType())) {
    assignSafepoint(lir, ins);
  }

  if (!sync.isNone()) {
    LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierAfter);
    add(fence, ins);
  }
}

void LIRGenerator::visitLoadDataViewElement(MLoadDataViewElement* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr);

  MOZ_ASSERT(IsNumberType(ins->type()));

  const LUse elements = useRegister(ins->elements());
  const LUse index = useRegister(ins->index());
  const LAllocation littleEndian = useRegisterOrConstant(ins->littleEndian());

  if (Scalar::isBigIntType(ins->storageType())) {
    auto* lir =
        new (alloc()) LLoadDataViewElement64(elements, index, littleEndian);
    defineInt64(lir, ins);
    return;
  }

  // We need a temp register for:
  // - Uint32Array with known double result,
  // - Float16Array,
  // - Float32Array.
  LDefinition temp1 = LDefinition::BogusTemp();
  if ((ins->storageType() == Scalar::Uint32 &&
       IsFloatingPointType(ins->type())) ||
      ins->storageType() == Scalar::Float16 ||
      ins->storageType() == Scalar::Float32) {
    temp1 = temp();
  }

  // Additional temp when Float16 to Float64 conversion requires a call.
  LDefinition temp2 = LDefinition::BogusTemp();
  if (MacroAssembler::LoadRequiresCall(ins->storageType())) {
    temp2 = temp();
  }

  // We also need a separate 64-bit temp register for Float64Array.
  LInt64Definition temp64 = LInt64Definition::BogusTemp();
  if (ins->storageType() == Scalar::Float64) {
    temp64 = tempInt64();
  }

  auto* lir = new (alloc())
      LLoadDataViewElement(elements, index, littleEndian, temp1, temp2, temp64);
  if (ins->fallible()) {
    assignSnapshot(lir, ins->bailoutKind());
  }
  define(lir, ins);
  if (MacroAssembler::LoadRequiresCall(ins->storageType())) {
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitClampToUint8(MClampToUint8* ins) {
  MDefinition* in = ins->input();

  switch (in->type()) {
    case MIRType::Int32:
      defineReuseInput(new (alloc()) LClampIToUint8(useRegisterAtStart(in)),
                       ins, 0);
      break;

    case MIRType::Double:
      // LClampDToUint8 clobbers its input register. Making it available as
      // a temp copy describes this behavior to the register allocator.
      define(new (alloc())
                 LClampDToUint8(useRegisterAtStart(in), tempCopy(in, 0)),
             ins);
      break;

    case MIRType::Value: {
      LClampVToUint8* lir =
          new (alloc()) LClampVToUint8(useBox(in), tempDouble());
      assignSnapshot(lir, ins->bailoutKind());
      define(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }

    default:
      MOZ_CRASH("unexpected type");
  }
}

void LIRGenerator::visitLoadTypedArrayElementHole(
    MLoadTypedArrayElementHole* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->length()->type() == MIRType::IntPtr);

  MOZ_ASSERT(ins->type() == MIRType::Value);

  const LUse elements = useRegister(ins->elements());
  const LAllocation index = useRegister(ins->index());
  const LAllocation length = useRegister(ins->length());

  if (!Scalar::isBigIntType(ins->arrayType())) {
    LDefinition tempDef = LDefinition::BogusTemp();
    if (MacroAssembler::LoadRequiresCall(ins->arrayType())) {
      tempDef = temp();
    }

    auto* lir = new (alloc())
        LLoadTypedArrayElementHole(elements, index, length, tempDef);
    if (ins->fallible()) {
      assignSnapshot(lir, ins->bailoutKind());
    }
    defineBox(lir, ins);

    if (MacroAssembler::LoadRequiresCall(ins->arrayType())) {
      assignSafepoint(lir, ins);
    }
  } else {
#ifdef JS_CODEGEN_X86
    LInt64Definition temp64 = LInt64Definition::BogusTemp();
#else
    LInt64Definition temp64 = tempInt64();
#endif

    auto* lir = new (alloc()) LLoadTypedArrayElementHoleBigInt(
        elements, index, length, temp(), temp64);
    defineBox(lir, ins);
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr);

  if (ins->isFloatWrite()) {
    MOZ_ASSERT_IF(ins->writeType() == Scalar::Float16,
                  ins->value()->type() == MIRType::Float32);
    MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32,
                  ins->value()->type() == MIRType::Float32);
    MOZ_ASSERT_IF(ins->writeType() == Scalar::Float64,
                  ins->value()->type() == MIRType::Double);
  } else if (ins->isBigIntWrite()) {
    MOZ_ASSERT(ins->value()->type() == MIRType::Int64);
  } else {
    MOZ_ASSERT(ins->value()->type() == MIRType::Int32);
  }

  auto sync = SynchronizeStore(ins->requiresMemoryBarrier());

  if (ins->isBigIntWrite() && !sync.isNone()) {
    lowerAtomicStore64(ins);
    return;
  }

  LUse elements = useRegister(ins->elements());
  LAllocation index =
      useRegisterOrIndexConstant(ins->index(), ins->writeType());

  if (ins->isBigIntWrite()) {
    LInt64Allocation value = useInt64RegisterOrConstant(ins->value());
    add(new (alloc()) LStoreUnboxedInt64(elements, index, value), ins);
    return;
  }

  // For byte arrays, the value has to be in a byte register on x86.
  LAllocation value;
  if (ins->isByteWrite()) {
    value = useByteOpRegisterOrNonDoubleConstant(ins->value());
  } else {
    value = useRegisterOrNonDoubleConstant(ins->value());
  }

  // Optimization opportunity for atomics: on some platforms there
  // is a store instruction that incorporates the necessary
  // barriers, and we could use that instead of separate barrier and
  // store instructions.  See bug #1077027.
  //
  // NOTE: the generated code must match the assembly code in gen_store in
  // GenerateAtomicOperations.py
  if (!sync.isNone()) {
    LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierBefore);
    add(fence, ins);
  }

  // We need a temp register for Float16Array.
  LDefinition tempDef = LDefinition::BogusTemp();
  if (ins->writeType() == Scalar::Float16) {
    tempDef = temp();
  }

  auto* lir =
      new (alloc()) LStoreUnboxedScalar(elements, index, value, tempDef);
  add(lir, ins);

  if (MacroAssembler::StoreRequiresCall(ins->writeType())) {
    assignSafepoint(lir, ins);
  }

  if (!sync.isNone()) {
    LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierAfter);
    add(fence, ins);
  }
}

void LIRGenerator::visitStoreDataViewElement(MStoreDataViewElement* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->littleEndian()->type() == MIRType::Boolean);

  if (ins->isFloatWrite()) {
    MOZ_ASSERT_IF(ins->writeType() == Scalar::Float16,
                  ins->value()->type() == MIRType::Float32);
    MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32,
                  ins->value()->type() == MIRType::Float32);
    MOZ_ASSERT_IF(ins->writeType() == Scalar::Float64,
                  ins->value()->type() == MIRType::Double);
  } else if (ins->isBigIntWrite()) {
    MOZ_ASSERT(ins->value()->type() == MIRType::Int64);
  } else {
    MOZ_ASSERT(ins->value()->type() == MIRType::Int32);
  }

  LUse elements = useRegister(ins->elements());
  LUse index = useRegister(ins->index());
  LAllocation littleEndian = useRegisterOrConstant(ins->littleEndian());

  if (ins->isBigIntWrite()) {
#ifdef JS_CODEGEN_X86
    LInt64Allocation value = useInt64Register(ins->value());
    LInt64Definition temp = LInt64Definition::BogusTemp();
    if (littleEndian.isConstant()) {
      temp = tempInt64();
    }
#else
    LInt64Allocation value = useInt64RegisterOrConstant(ins->value());
    LInt64Definition temp = tempInt64();
#endif

    auto* lir = new (alloc())
        LStoreDataViewElement64(elements, index, value, littleEndian, temp);
    add(lir, ins);
    return;
  }

  LAllocation value = useRegisterOrNonDoubleConstant(ins->value());

  LDefinition tempDef = LDefinition::BogusTemp();
  LInt64Definition temp64Def = LInt64Definition::BogusTemp();
  if (ins->writeType() != Scalar::Float64) {
    tempDef = temp();
  } else {
    temp64Def = tempInt64();
  }

  auto* lir = new (alloc()) LStoreDataViewElement(
      elements, index, value, littleEndian, tempDef, temp64Def);
  add(lir, ins);

  if (MacroAssembler::StoreRequiresCall(ins->writeType())) {
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitStoreTypedArrayElementHole(
    MStoreTypedArrayElementHole* ins) {
  MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
  MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr);
  MOZ_ASSERT(ins->length()->type() == MIRType::IntPtr);

  if (ins->isFloatWrite()) {
    MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float16,
                  ins->value()->type() == MIRType::Float32);
    MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float32,
                  ins->value()->type() == MIRType::Float32);
    MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float64,
                  ins->value()->type() == MIRType::Double);
  } else if (ins->isBigIntWrite()) {
    MOZ_ASSERT(ins->value()->type() == MIRType::Int64);
  } else {
    MOZ_ASSERT(ins->value()->type() == MIRType::Int32);
  }

  LUse elements = useRegister(ins->elements());
  LAllocation length = useAny(ins->length());
  LAllocation index = useRegister(ins->index());

  if (ins->isBigIntWrite()) {
    LInt64Allocation value = useInt64RegisterOrConstant(ins->value());
    LDefinition spectreTemp =
        BoundsCheckNeedsSpectreTemp() ? temp() : LDefinition::BogusTemp();

    auto* lir = new (alloc()) LStoreTypedArrayElementHoleInt64(
        elements, length, index, value, spectreTemp);
    add(lir, ins);
    return;
  }

  // For byte arrays, the value has to be in a byte register on x86.
  LAllocation value;
  if (ins->isByteWrite()) {
    value = useByteOpRegisterOrNonDoubleConstant(ins->value());
  } else {
    value = useRegisterOrNonDoubleConstant(ins->value());
  }

  LDefinition tempDef = LDefinition::BogusTemp();
  if (BoundsCheckNeedsSpectreTemp() || ins->arrayType() == Scalar::Float16) {
    tempDef = temp();
  }

  auto* lir = new (alloc())
      LStoreTypedArrayElementHole(elements, length, index, value, tempDef);
  add(lir, ins);

  if (MacroAssembler::StoreRequiresCall(ins->arrayType())) {
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitLoadScriptedProxyHandler(
    MLoadScriptedProxyHandler* ins) {
  LLoadScriptedProxyHandler* lir = new (alloc())
      LLoadScriptedProxyHandler(useRegisterAtStart(ins->object()));
  assignSnapshot(lir, ins->bailoutKind());
  define(lir, ins);
}

void LIRGenerator::visitIdToStringOrSymbol(MIdToStringOrSymbol* ins) {
  LIdToStringOrSymbol* lir =
      new (alloc()) LIdToStringOrSymbol(useBoxAtStart(ins->idVal()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot* ins) {
  MDefinition* obj = ins->object();
  MOZ_ASSERT(obj->type() == MIRType::Object);

  MIRType type = ins->type();

  if (type == MIRType::Value) {
    if (ins->usedAsPropertyKey()) {
      LLoadFixedSlotAndAtomize* lir =
          new (alloc()) LLoadFixedSlotAndAtomize(useRegister(obj), temp());
      defineBox(lir, ins);
      assignSafepoint(lir, ins);
    } else {
      LLoadFixedSlotV* lir =
          new (alloc()) LLoadFixedSlotV(useRegisterAtStart(obj));
      defineBox(lir, ins);
    }
  } else {
    LLoadFixedSlotT* lir =
        new (alloc()) LLoadFixedSlotT(useRegisterForTypedLoad(obj, type));
    define(lir, ins);
  }
}

void LIRGenerator::visitLoadFixedSlotAndUnbox(MLoadFixedSlotAndUnbox* ins) {
  MDefinition* obj = ins->object();
  MOZ_ASSERT(obj->type() == MIRType::Object);

  if (ins->usedAsPropertyKey() && ins->type() == MIRType::String) {
    LLoadFixedSlotUnboxAndAtomize* lir =
        new (alloc()) LLoadFixedSlotUnboxAndAtomize(useRegister(obj));
    if (ins->fallible()) {
      assignSnapshot(lir, ins->bailoutKind());
    }
    define(lir, ins);
    assignSafepoint(lir, ins);
  } else {
    LLoadFixedSlotAndUnbox* lir =
        new (alloc()) LLoadFixedSlotAndUnbox(useRegisterAtStart(obj));
    if (ins->fallible()) {
      assignSnapshot(lir, ins->bailoutKind());
    }
    define(lir, ins);
  }
}

void LIRGenerator::visitLoadDynamicSlotAndUnbox(MLoadDynamicSlotAndUnbox* ins) {
  MDefinition* slots = ins->slots();
  MOZ_ASSERT(slots->type() == MIRType::Slots);

  if (ins->usedAsPropertyKey() && ins->type() == MIRType::String) {
    auto* lir =
        new (alloc()) LLoadDynamicSlotUnboxAndAtomize(useRegister(slots));
    if (ins->fallible()) {
      assignSnapshot(lir, ins->bailoutKind());
    }
    define(lir, ins);
    assignSafepoint(lir, ins);
  } else {
    auto* lir =
        new (alloc()) LLoadDynamicSlotAndUnbox(useRegisterAtStart(slots));
    if (ins->fallible()) {
      assignSnapshot(lir, ins->bailoutKind());
    }
    define(lir, ins);
  }
}

void LIRGenerator::visitLoadElementAndUnbox(MLoadElementAndUnbox* ins) {
  MDefinition* elements = ins->elements();
  MDefinition* index = ins->index();
  MOZ_ASSERT(elements->type() == MIRType::Elements);
  MOZ_ASSERT(index->type() == MIRType::Int32);

  auto* lir = new (alloc())
      LLoadElementAndUnbox(useRegister(elements), useRegisterOrConstant(index));
  if (ins->fallible()) {
    assignSnapshot(lir, ins->bailoutKind());
  }
  define(lir, ins);
}

void LIRGenerator::visitAddAndStoreSlot(MAddAndStoreSlot* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  LDefinition maybeTemp = LDefinition::BogusTemp();
  if (ins->kind() != MAddAndStoreSlot::Kind::FixedSlot) {
    maybeTemp = temp();
  }

  auto* lir = new (alloc()) LAddAndStoreSlot(useRegister(ins->object()),
                                             useBox(ins->value()), maybeTemp);
  add(lir, ins);
}

void LIRGenerator::visitAllocateAndStoreSlot(MAllocateAndStoreSlot* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc()) LAllocateAndStoreSlot(
      useRegisterAtStart(ins->object()), useBoxAtStart(ins->value()),
      tempFixed(CallTempReg0), tempFixed(CallTempReg1));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
}

void LIRGenerator::visitAddSlotAndCallAddPropHook(
    MAddSlotAndCallAddPropHook* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->value()->type() == MIRType::Value);

  auto* lir = new (alloc()) LAddSlotAndCallAddPropHook(
      useRegisterAtStart(ins->object()), useBoxAtStart(ins->value()));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStoreFixedSlot(MStoreFixedSlot* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  if (ins->value()->type() == MIRType::Value) {
    LStoreFixedSlotV* lir = new (alloc())
        LStoreFixedSlotV(useRegister(ins->object()), useBox(ins->value()));
    add(lir, ins);
  } else {
    LStoreFixedSlotT* lir = new (alloc()) LStoreFixedSlotT(
        useRegister(ins->object()), useRegisterOrConstant(ins->value()));
    add(lir, ins);
  }
}

void LIRGenerator::visitGetNameCache(MGetNameCache* ins) {
  MOZ_ASSERT(ins->envObj()->type() == MIRType::Object);

  // Emit an overrecursed check: this is necessary because the cache can
  // attach a scripted getter stub that calls this script recursively.
  gen->setNeedsOverrecursedCheck();

  LGetNameCache* lir =
      new (alloc()) LGetNameCache(useRegister(ins->envObj()), temp());
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCallGetIntrinsicValue(MCallGetIntrinsicValue* ins) {
  LCallGetIntrinsicValue* lir = new (alloc()) LCallGetIntrinsicValue();
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGetPropSuperCache(MGetPropSuperCache* ins) {
  MDefinition* obj = ins->object();
  MDefinition* receiver = ins->receiver();
  MDefinition* id = ins->idval();

  gen->setNeedsOverrecursedCheck();

  bool useConstId =
      id->type() == MIRType::String || id->type() == MIRType::Symbol;

  auto* lir = new (alloc())
      LGetPropSuperCache(useRegister(obj), useBoxOrTyped(receiver),
                         useBoxOrTypedOrConstant(id, useConstId));
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGetPropertyCache(MGetPropertyCache* ins) {
  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Object ||
             value->type() == MIRType::Value);

  MDefinition* id = ins->idval();
  MOZ_ASSERT(id->type() == MIRType::String || id->type() == MIRType::Symbol ||
             id->type() == MIRType::Int32 || id->type() == MIRType::Value);

  // Emit an overrecursed check: this is necessary because the cache can
  // attach a scripted getter stub that calls this script recursively.
  gen->setNeedsOverrecursedCheck();

  // If this is a GetProp, the id is a constant string. Allow passing it as a
  // constant to reduce register allocation pressure.
  bool useConstId =
      id->type() == MIRType::String || id->type() == MIRType::Symbol;

  auto* lir = new (alloc()) LGetPropertyCache(
      useBoxOrTyped(value), useBoxOrTypedOrConstant(id, useConstId));
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBindNameCache(MBindNameCache* ins) {
  MOZ_ASSERT(ins->envChain()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Object);

  LBindNameCache* lir =
      new (alloc()) LBindNameCache(useRegister(ins->envChain()), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCallBindVar(MCallBindVar* ins) {
  MOZ_ASSERT(ins->environmentChain()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Object);

  LCallBindVar* lir =
      new (alloc()) LCallBindVar(useRegister(ins->environmentChain()));
  define(lir, ins);
}

void LIRGenerator::visitGuardObjectIdentity(MGuardObjectIdentity* ins) {
  LGuardObjectIdentity* guard = new (alloc()) LGuardObjectIdentity(
      useRegister(ins->object()), useRegister(ins->expected()));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardSpecificFunction(MGuardSpecificFunction* ins) {
  auto* guard = new (alloc()) LGuardSpecificFunction(
      useRegister(ins->function()), useRegister(ins->expected()));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, ins->function());
}

void LIRGenerator::visitGuardSpecificAtom(MGuardSpecificAtom* ins) {
  auto* guard =
      new (alloc()) LGuardSpecificAtom(useRegister(ins->str()), temp());
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, ins->str());
  assignSafepoint(guard, ins);
}

void LIRGenerator::visitGuardSpecificSymbol(MGuardSpecificSymbol* ins) {
  auto* guard = new (alloc()) LGuardSpecificSymbol(useRegister(ins->symbol()));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, ins->symbol());
}

void LIRGenerator::visitGuardSpecificInt32(MGuardSpecificInt32* ins) {
  auto* guard = new (alloc()) LGuardSpecificInt32(useRegister(ins->num()));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, ins->num());
}

void LIRGenerator::visitGuardStringToIndex(MGuardStringToIndex* ins) {
  MOZ_ASSERT(ins->string()->type() == MIRType::String);
  auto* guard = new (alloc()) LGuardStringToIndex(useRegister(ins->string()));
  assignSnapshot(guard, ins->bailoutKind());
  define(guard, ins);
  assignSafepoint(guard, ins);
}

void LIRGenerator::visitGuardStringToInt32(MGuardStringToInt32* ins) {
  MOZ_ASSERT(ins->string()->type() == MIRType::String);
  auto* guard =
      new (alloc()) LGuardStringToInt32(useRegister(ins->string()), temp());
  assignSnapshot(guard, ins->bailoutKind());
  define(guard, ins);
  assignSafepoint(guard, ins);
}

void LIRGenerator::visitGuardStringToDouble(MGuardStringToDouble* ins) {
  MOZ_ASSERT(ins->string()->type() == MIRType::String);
  auto* guard = new (alloc())
      LGuardStringToDouble(useRegister(ins->string()), temp(), temp());
  assignSnapshot(guard, ins->bailoutKind());
  define(guard, ins);
  assignSafepoint(guard, ins);
}

void LIRGenerator::visitGuardNoDenseElements(MGuardNoDenseElements* ins) {
  auto* guard =
      new (alloc()) LGuardNoDenseElements(useRegister(ins->object()), temp());
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardShape(MGuardShape* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  if (JitOptions.spectreObjectMitigations) {
    auto* lir =
        new (alloc()) LGuardShape(useRegisterAtStart(ins->object()), temp());
    assignSnapshot(lir, ins->bailoutKind());
    defineReuseInput(lir, ins, 0);
  } else {
    auto* lir = new (alloc())
        LGuardShape(useRegister(ins->object()), LDefinition::BogusTemp());
    assignSnapshot(lir, ins->bailoutKind());
    add(lir, ins);
    redefine(ins, ins->object());
  }
}

void LIRGenerator::visitGuardMultipleShapes(MGuardMultipleShapes* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  if (JitOptions.spectreObjectMitigations) {
    auto* lir = new (alloc()) LGuardMultipleShapes(
        useRegisterAtStart(ins->object()), useRegister(ins->shapeList()),
        temp(), temp(), temp(), temp());
    assignSnapshot(lir, ins->bailoutKind());
    defineReuseInput(lir, ins, 0);
  } else {
    auto* lir = new (alloc()) LGuardMultipleShapes(
        useRegister(ins->object()), useRegister(ins->shapeList()), temp(),
        temp(), temp(), LDefinition::BogusTemp());
    assignSnapshot(lir, ins->bailoutKind());
    add(lir, ins);
    redefine(ins, ins->object());
  }
}

void LIRGenerator::visitGuardProto(MGuardProto* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->expected()->type() == MIRType::Object);

  auto* lir = new (alloc()) LGuardProto(useRegister(ins->object()),
                                        useRegister(ins->expected()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardNullProto(MGuardNullProto* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc()) LGuardNullProto(useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardIsNativeObject(MGuardIsNativeObject* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir =
      new (alloc()) LGuardIsNativeObject(useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardGlobalGeneration(MGuardGlobalGeneration* ins) {
  auto* lir = new (alloc()) LGuardGlobalGeneration(temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
}

void LIRGenerator::visitGuardFuse(MGuardFuse* ins) {
  auto* lir = new (alloc()) LGuardFuse(temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
}

void LIRGenerator::visitGuardIsProxy(MGuardIsProxy* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc()) LGuardIsProxy(useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardIsNotProxy(MGuardIsNotProxy* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir =
      new (alloc()) LGuardIsNotProxy(useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardIsNotDOMProxy(MGuardIsNotDOMProxy* ins) {
  MOZ_ASSERT(ins->proxy()->type() == MIRType::Object);

  auto* lir =
      new (alloc()) LGuardIsNotDOMProxy(useRegister(ins->proxy()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->proxy());
}

void LIRGenerator::visitProxyGet(MProxyGet* ins) {
  MOZ_ASSERT(ins->proxy()->type() == MIRType::Object);
  auto* lir = new (alloc())
      LProxyGet(useRegisterAtStart(ins->proxy()), tempFixed(CallTempReg0));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitProxyGetByValue(MProxyGetByValue* ins) {
  MOZ_ASSERT(ins->proxy()->type() == MIRType::Object);
  MOZ_ASSERT(ins->idVal()->type() == MIRType::Value);
  auto* lir = new (alloc()) LProxyGetByValue(useRegisterAtStart(ins->proxy()),
                                             useBoxAtStart(ins->idVal()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitProxyHasProp(MProxyHasProp* ins) {
  MOZ_ASSERT(ins->proxy()->type() == MIRType::Object);
  MOZ_ASSERT(ins->idVal()->type() == MIRType::Value);
  auto* lir = new (alloc()) LProxyHasProp(useRegisterAtStart(ins->proxy()),
                                          useBoxAtStart(ins->idVal()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitProxySet(MProxySet* ins) {
  MOZ_ASSERT(ins->proxy()->type() == MIRType::Object);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::Value);
  auto* lir = new (alloc())
      LProxySet(useRegisterAtStart(ins->proxy()), useBoxAtStart(ins->rhs()),
                tempFixed(CallTempReg0));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitProxySetByValue(MProxySetByValue* ins) {
  MOZ_ASSERT(ins->proxy()->type() == MIRType::Object);
  MOZ_ASSERT(ins->idVal()->type() == MIRType::Value);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::Value);
  auto* lir = new (alloc())
      LProxySetByValue(useRegisterAtStart(ins->proxy()),
                       useBoxAtStart(ins->idVal()), useBoxAtStart(ins->rhs()));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCallSetArrayLength(MCallSetArrayLength* ins) {
  MOZ_ASSERT(ins->obj()->type() == MIRType::Object);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::Value);
  auto* lir = new (alloc()) LCallSetArrayLength(useRegisterAtStart(ins->obj()),
                                                useBoxAtStart(ins->rhs()));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMegamorphicLoadSlot(MMegamorphicLoadSlot* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  auto* lir = new (alloc())
      LMegamorphicLoadSlot(useRegisterAtStart(ins->object()),
                           tempFixed(CallTempReg0), tempFixed(CallTempReg1),
                           tempFixed(CallTempReg2), tempFixed(CallTempReg3));
  assignSnapshot(lir, ins->bailoutKind());
  defineReturn(lir, ins);
}

void LIRGenerator::visitMegamorphicLoadSlotPermissive(
    MMegamorphicLoadSlotPermissive* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  auto* lir = new (alloc()) LMegamorphicLoadSlotPermissive(
      useRegisterAtStart(ins->object()), tempFixed(CallTempReg0),
      tempFixed(CallTempReg1), tempFixed(CallTempReg2));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMegamorphicLoadSlotByValue(
    MMegamorphicLoadSlotByValue* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->idVal()->type() == MIRType::Value);
  auto* lir = new (alloc()) LMegamorphicLoadSlotByValue(
      useRegisterAtStart(ins->object()), useBoxAtStart(ins->idVal()),
      tempFixed(CallTempReg0), tempFixed(CallTempReg1),
      tempFixed(CallTempReg2));
  assignSnapshot(lir, ins->bailoutKind());
  defineReturn(lir, ins);
}

void LIRGenerator::visitMegamorphicLoadSlotByValuePermissive(
    MMegamorphicLoadSlotByValuePermissive* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->idVal()->type() == MIRType::Value);
  auto* lir = new (alloc()) LMegamorphicLoadSlotByValuePermissive(
      useRegisterAtStart(ins->object()), useBoxAtStart(ins->idVal()),
      tempFixed(CallTempReg0), tempFixed(CallTempReg1),
      tempFixed(CallTempReg2));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMegamorphicStoreSlot(MMegamorphicStoreSlot* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->rhs()->type() == MIRType::Value);

#ifdef JS_CODEGEN_X86
  auto* lir = new (alloc()) LMegamorphicStoreSlot(
      useFixedAtStart(ins->object(), CallTempReg0),
      useBoxFixedAtStart(ins->rhs(), CallTempReg1, CallTempReg2),
      tempFixed(CallTempReg5));
#else
  auto* lir = new (alloc())
      LMegamorphicStoreSlot(useRegisterAtStart(ins->object()),
                            useBoxAtStart(ins->rhs()), tempFixed(CallTempReg0),
                            tempFixed(CallTempReg1), tempFixed(CallTempReg2));
#endif

  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMegamorphicHasProp(MMegamorphicHasProp* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->idVal()->type() == MIRType::Value);
  auto* lir = new (alloc())
      LMegamorphicHasProp(useRegisterAtStart(ins->object()),
                          useBoxAtStart(ins->idVal()), tempFixed(CallTempReg0),
                          tempFixed(CallTempReg1), tempFixed(CallTempReg2));
  assignSnapshot(lir, ins->bailoutKind());
  defineReturn(lir, ins);
}

void LIRGenerator::visitSmallObjectVariableKeyHasProp(
    MSmallObjectVariableKeyHasProp* ins) {
  MOZ_ASSERT(ins->idStr()->type() == MIRType::String);
  auto* lir = new (alloc())
      LSmallObjectVariableKeyHasProp(useRegisterAtStart(ins->idStr()));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGuardIsNotArrayBufferMaybeShared(
    MGuardIsNotArrayBufferMaybeShared* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc())
      LGuardIsNotArrayBufferMaybeShared(useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardIsTypedArray(MGuardIsTypedArray* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir =
      new (alloc()) LGuardIsTypedArray(useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardIsFixedLengthTypedArray(
    MGuardIsFixedLengthTypedArray* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc())
      LGuardIsFixedLengthTypedArray(useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardIsResizableTypedArray(
    MGuardIsResizableTypedArray* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc())
      LGuardIsResizableTypedArray(useRegister(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitGuardHasProxyHandler(MGuardHasProxyHandler* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  auto* lir = new (alloc()) LGuardHasProxyHandler(useRegister(ins->object()));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->object());
}

void LIRGenerator::visitNurseryObject(MNurseryObject* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Object);

  auto* lir = new (alloc()) LNurseryObject();
  define(lir, ins);
}

void LIRGenerator::visitGuardValue(MGuardValue* ins) {
  MOZ_ASSERT(ins->value()->type() == MIRType::Value);
  auto* lir = new (alloc()) LGuardValue(useBox(ins->value()));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->value());
}

void LIRGenerator::visitGuardNullOrUndefined(MGuardNullOrUndefined* ins) {
  MOZ_ASSERT(ins->value()->type() == MIRType::Value);
  auto* lir = new (alloc()) LGuardNullOrUndefined(useBox(ins->value()));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->value());
}

void LIRGenerator::visitGuardIsNotObject(MGuardIsNotObject* ins) {
  MOZ_ASSERT(ins->value()->type() == MIRType::Value);
  auto* lir = new (alloc()) LGuardIsNotObject(useBox(ins->value()));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->value());
}

void LIRGenerator::visitGuardFunctionFlags(MGuardFunctionFlags* ins) {
  MOZ_ASSERT(ins->function()->type() == MIRType::Object);

  auto* lir = new (alloc()) LGuardFunctionFlags(useRegister(ins->function()));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->function());
}

void LIRGenerator::visitGuardFunctionIsNonBuiltinCtor(
    MGuardFunctionIsNonBuiltinCtor* ins) {
  MOZ_ASSERT(ins->function()->type() == MIRType::Object);

  auto* lir = new (alloc())
      LGuardFunctionIsNonBuiltinCtor(useRegister(ins->function()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->function());
}

void LIRGenerator::visitGuardFunctionKind(MGuardFunctionKind* ins) {
  MOZ_ASSERT(ins->function()->type() == MIRType::Object);

  auto* lir =
      new (alloc()) LGuardFunctionKind(useRegister(ins->function()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->function());
}

void LIRGenerator::visitGuardFunctionScript(MGuardFunctionScript* ins) {
  MOZ_ASSERT(ins->function()->type() == MIRType::Object);

  auto* lir = new (alloc()) LGuardFunctionScript(useRegister(ins->function()));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->function());
}

void LIRGenerator::visitAssertRange(MAssertRange* ins) {
  MDefinition* input = ins->input();
  LInstruction* lir = nullptr;

  switch (input->type()) {
    case MIRType::Boolean:
    case MIRType::Int32:
    case MIRType::IntPtr:
      lir = new (alloc()) LAssertRangeI(useRegisterAtStart(input));
      break;

    case MIRType::Double:
      lir = new (alloc()) LAssertRangeD(useRegister(input), tempDouble());
      break;

    case MIRType::Float32:
      lir = new (alloc())
          LAssertRangeF(useRegister(input), tempDouble(), tempDouble());
      break;

    case MIRType::Value:
      lir = new (alloc()) LAssertRangeV(useBox(input), tempToUnbox(),
                                        tempDouble(), tempDouble());
      break;

    default:
      MOZ_CRASH("Unexpected Range for MIRType");
      break;
  }

  lir->setMir(ins);
  add(lir);
}

void LIRGenerator::visitAssertClass(MAssertClass* ins) {
  auto* lir =
      new (alloc()) LAssertClass(useRegisterAtStart(ins->input()), temp());
  add(lir, ins);
}

void LIRGenerator::visitAssertShape(MAssertShape* ins) {
  auto* lir = new (alloc()) LAssertShape(useRegisterAtStart(ins->input()));
  add(lir, ins);
}

void LIRGenerator::visitDeleteProperty(MDeleteProperty* ins) {
  LCallDeleteProperty* lir =
      new (alloc()) LCallDeleteProperty(useBoxAtStart(ins->value()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitDeleteElement(MDeleteElement* ins) {
  LCallDeleteElement* lir = new (alloc()) LCallDeleteElement(
      useBoxAtStart(ins->value()), useBoxAtStart(ins->index()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitObjectToIterator(MObjectToIterator* ins) {
  auto* lir = new (alloc())
      LObjectToIterator(useRegister(ins->object()), temp(), temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitValueToIterator(MValueToIterator* ins) {
  auto* lir = new (alloc()) LValueToIterator(useBoxAtStart(ins->value()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitLoadSlotByIteratorIndex(MLoadSlotByIteratorIndex* ins) {
  auto* lir = new (alloc()) LLoadSlotByIteratorIndex(
      useRegisterAtStart(ins->object()), useRegisterAtStart(ins->iterator()),
      temp(), temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitStoreSlotByIteratorIndex(
    MStoreSlotByIteratorIndex* ins) {
  auto* lir = new (alloc()) LStoreSlotByIteratorIndex(
      useRegister(ins->object()), useRegister(ins->iterator()),
      useBox(ins->value()), temp(), temp());
  add(lir, ins);
}

void LIRGenerator::visitIteratorHasIndices(MIteratorHasIndices* ins) {
  MOZ_ASSERT(ins->hasOneUse());
  emitAtUses(ins);
}

void LIRGenerator::visitSetPropertyCache(MSetPropertyCache* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);

  MDefinition* id = ins->idval();
  MOZ_ASSERT(id->type() == MIRType::String || id->type() == MIRType::Symbol ||
             id->type() == MIRType::Int32 || id->type() == MIRType::Value);

  // If this is a SetProp, the id is a constant string. Allow passing it as a
  // constant to reduce register allocation pressure.
  bool useConstId =
      id->type() == MIRType::String || id->type() == MIRType::Symbol;
  bool useConstValue = IsNonNurseryConstant(ins->value());

  // Emit an overrecursed check: this is necessary because the cache can
  // attach a scripted setter stub that calls this script recursively.
  gen->setNeedsOverrecursedCheck();

  // We need a double temp register for TypedArray stubs.
  LDefinition tempD = tempFixed(FloatReg0);

  LInstruction* lir = new (alloc()) LSetPropertyCache(
      useRegister(ins->object()), useBoxOrTypedOrConstant(id, useConstId),
      useBoxOrTypedOrConstant(ins->value(), useConstValue), temp(), tempD);
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMegamorphicSetElement(MMegamorphicSetElement* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->index()->type() == MIRType::Value);
  MOZ_ASSERT(ins->value()->type() == MIRType::Value);

  // See comment in LIROps.yaml (x86 is short on registers)
#ifdef JS_CODEGEN_X86
  auto* lir = new (alloc()) LMegamorphicSetElement(
      useFixedAtStart(ins->object(), CallTempReg0),
      useBoxFixedAtStart(ins->index(), CallTempReg1, CallTempReg2),
      useBoxFixedAtStart(ins->value(), CallTempReg3, CallTempReg4),
      tempFixed(CallTempReg5));
#else
  auto* lir = new (alloc()) LMegamorphicSetElement(
      useRegisterAtStart(ins->object()), useBoxAtStart(ins->index()),
      useBoxAtStart(ins->value()), tempFixed(CallTempReg0),
      tempFixed(CallTempReg1), tempFixed(CallTempReg2));
#endif
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGetIteratorCache(MGetIteratorCache* ins) {
  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Object ||
             value->type() == MIRType::Value);

  LGetIteratorCache* lir =
      new (alloc()) LGetIteratorCache(useBoxOrTyped(value), temp(), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitOptimizeSpreadCallCache(MOptimizeSpreadCallCache* ins) {
  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Value);

  auto* lir = new (alloc()) LOptimizeSpreadCallCache(useBox(value), temp());
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitIteratorMore(MIteratorMore* ins) {
  LIteratorMore* lir =
      new (alloc()) LIteratorMore(useRegister(ins->iterator()), temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitIsNoIter(MIsNoIter* ins) {
  MOZ_ASSERT(ins->hasOneUse());
  emitAtUses(ins);
}

void LIRGenerator::visitIteratorEnd(MIteratorEnd* ins) {
  LIteratorEnd* lir = new (alloc())
      LIteratorEnd(useRegister(ins->iterator()), temp(), temp(), temp());
  add(lir, ins);
}

void LIRGenerator::visitCloseIterCache(MCloseIterCache* ins) {
  LCloseIterCache* lir =
      new (alloc()) LCloseIterCache(useRegister(ins->iter()), temp());
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitOptimizeGetIteratorCache(
    MOptimizeGetIteratorCache* ins) {
  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Value);

  auto* lir = new (alloc()) LOptimizeGetIteratorCache(useBox(value), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitStringLength(MStringLength* ins) {
  MOZ_ASSERT(ins->string()->type() == MIRType::String);
  define(new (alloc()) LStringLength(useRegisterAtStart(ins->string())), ins);
}

void LIRGenerator::visitArgumentsLength(MArgumentsLength* ins) {
  define(new (alloc()) LArgumentsLength(), ins);
}

void LIRGenerator::visitGetFrameArgument(MGetFrameArgument* ins) {
  LGetFrameArgument* lir =
      new (alloc()) LGetFrameArgument(useRegisterOrConstant(ins->index()));
  defineBox(lir, ins);
}

void LIRGenerator::visitGetFrameArgumentHole(MGetFrameArgumentHole* ins) {
  LDefinition spectreTemp =
      BoundsCheckNeedsSpectreTemp() ? temp() : LDefinition::BogusTemp();

  auto* lir = new (alloc()) LGetFrameArgumentHole(
      useRegister(ins->index()), useRegister(ins->length()), spectreTemp);
  assignSnapshot(lir, ins->bailoutKind());
  defineBox(lir, ins);
}

void LIRGenerator::visitNewTarget(MNewTarget* ins) {
  LNewTarget* lir = new (alloc()) LNewTarget();
  defineBox(lir, ins);
}

void LIRGenerator::visitRest(MRest* ins) {
  MOZ_ASSERT(ins->numActuals()->type() == MIRType::Int32);

  LRest* lir =
      new (alloc()) LRest(useRegisterAtStart(ins->numActuals()),
                          tempFixed(CallTempReg0), tempFixed(CallTempReg1),
                          tempFixed(CallTempReg2), tempFixed(CallTempReg3));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitThrow(MThrow* ins) {
  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Value);

  LThrow* lir = new (alloc()) LThrow(useBoxAtStart(value));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitThrowWithStack(MThrowWithStack* ins) {
  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Value);

  MDefinition* stack = ins->stack();
  MOZ_ASSERT(stack->type() == MIRType::Value);

  auto* lir =
      new (alloc()) LThrowWithStack(useBoxAtStart(value), useBoxAtStart(stack));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitInCache(MInCache* ins) {
  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();

  MOZ_ASSERT(lhs->type() == MIRType::String || lhs->type() == MIRType::Symbol ||
             lhs->type() == MIRType::Int32 || lhs->type() == MIRType::Value);
  MOZ_ASSERT(rhs->type() == MIRType::Object);

  LInCache* lir =
      new (alloc()) LInCache(useBoxOrTyped(lhs), useRegister(rhs), temp());
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitHasOwnCache(MHasOwnCache* ins) {
  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Object ||
             value->type() == MIRType::Value);

  MDefinition* id = ins->idval();
  MOZ_ASSERT(id->type() == MIRType::String || id->type() == MIRType::Symbol ||
             id->type() == MIRType::Int32 || id->type() == MIRType::Value);

  // Emit an overrecursed check: this is necessary because the cache can
  // attach a scripted getter stub that calls this script recursively.
  gen->setNeedsOverrecursedCheck();

  LHasOwnCache* lir =
      new (alloc()) LHasOwnCache(useBoxOrTyped(value), useBoxOrTyped(id));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCheckPrivateFieldCache(MCheckPrivateFieldCache* ins) {
  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Object ||
             value->type() == MIRType::Value);

  MDefinition* id = ins->idval();
  MOZ_ASSERT(id->type() == MIRType::String || id->type() == MIRType::Symbol ||
             id->type() == MIRType::Int32 || id->type() == MIRType::Value);

  LCheckPrivateFieldCache* lir = new (alloc())
      LCheckPrivateFieldCache(useBoxOrTyped(value), useBoxOrTyped(id));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitNewPrivateName(MNewPrivateName* ins) {
  auto* lir = new (alloc()) LNewPrivateName();
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitInstanceOf(MInstanceOf* ins) {
  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();

  MOZ_ASSERT(lhs->type() == MIRType::Value || lhs->type() == MIRType::Object);
  MOZ_ASSERT(rhs->type() == MIRType::Object);

  if (lhs->type() == MIRType::Object) {
    auto* lir = new (alloc()) LInstanceOfO(useRegister(lhs), useRegister(rhs));
    define(lir, ins);
    assignSafepoint(lir, ins);
  } else {
    auto* lir = new (alloc()) LInstanceOfV(useBox(lhs), useRegister(rhs));
    define(lir, ins);
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitInstanceOfCache(MInstanceOfCache* ins) {
  MDefinition* lhs = ins->lhs();
  MDefinition* rhs = ins->rhs();

  MOZ_ASSERT(lhs->type() == MIRType::Value);
  MOZ_ASSERT(rhs->type() == MIRType::Object);

  LInstanceOfCache* lir =
      new (alloc()) LInstanceOfCache(useBox(lhs), useRegister(rhs));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitIsArray(MIsArray* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Boolean);

  if (ins->value()->type() == MIRType::Object) {
    LIsArrayO* lir = new (alloc()) LIsArrayO(useRegister(ins->value()));
    define(lir, ins);
    assignSafepoint(lir, ins);
  } else {
    MOZ_ASSERT(ins->value()->type() == MIRType::Value);
    LIsArrayV* lir = new (alloc()) LIsArrayV(useBox(ins->value()), temp());
    define(lir, ins);
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitIsTypedArray(MIsTypedArray* ins) {
  MOZ_ASSERT(ins->value()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Boolean);

  auto* lir = new (alloc()) LIsTypedArray(useRegister(ins->value()));
  define(lir, ins);

  if (ins->isPossiblyWrapped()) {
    assignSafepoint(lir, ins);
  }
}

void LIRGenerator::visitIsCallable(MIsCallable* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Boolean);

  if (ins->object()->type() == MIRType::Object) {
    define(new (alloc()) LIsCallableO(useRegister(ins->object())), ins);
  } else {
    MOZ_ASSERT(ins->object()->type() == MIRType::Value);
    define(new (alloc()) LIsCallableV(useBox(ins->object()), temp()), ins);
  }
}

void LIRGenerator::visitIsConstructor(MIsConstructor* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Boolean);
  define(new (alloc()) LIsConstructor(useRegister(ins->object())), ins);
}

void LIRGenerator::visitIsCrossRealmArrayConstructor(
    MIsCrossRealmArrayConstructor* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Boolean);
  define(new (alloc())
             LIsCrossRealmArrayConstructor(useRegister(ins->object())),
         ins);
}

static bool CanEmitAtUseForSingleTest(MInstruction* ins) {
  if (!ins->canEmitAtUses()) {
    return false;
  }

  MUseIterator iter(ins->usesBegin());
  if (iter == ins->usesEnd()) {
    return false;
  }

  MNode* node = iter->consumer();
  if (!node->isDefinition()) {
    return false;
  }

  if (!node->toDefinition()->isTest()) {
    return false;
  }

  iter++;
  return iter == ins->usesEnd();
}

void LIRGenerator::visitIsObject(MIsObject* ins) {
  if (CanEmitAtUseForSingleTest(ins)) {
    emitAtUses(ins);
    return;
  }

  MDefinition* opd = ins->input();
  MOZ_ASSERT(opd->type() == MIRType::Value);
  LIsObject* lir = new (alloc()) LIsObject(useBoxAtStart(opd));
  define(lir, ins);
}

void LIRGenerator::visitIsNullOrUndefined(MIsNullOrUndefined* ins) {
  if (CanEmitAtUseForSingleTest(ins)) {
    emitAtUses(ins);
    return;
  }

  MDefinition* opd = ins->input();
  if (opd->type() == MIRType::Value) {
    auto* lir = new (alloc()) LIsNullOrUndefined(useBoxAtStart(opd));
    define(lir, ins);
  } else {
    define(new (alloc()) LInteger(IsNullOrUndefined(opd->type())), ins);
  }
}

void LIRGenerator::visitHasClass(MHasClass* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Boolean);
  define(new (alloc()) LHasClass(useRegister(ins->object())), ins);
}

void LIRGenerator::visitGuardToClass(MGuardToClass* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Object);
  LGuardToClass* lir =
      new (alloc()) LGuardToClass(useRegisterAtStart(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  defineReuseInput(lir, ins, 0);
}

void LIRGenerator::visitGuardToEitherClass(MGuardToEitherClass* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Object);
  auto* lir = new (alloc())
      LGuardToEitherClass(useRegisterAtStart(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  defineReuseInput(lir, ins, 0);
}

void LIRGenerator::visitGuardToFunction(MGuardToFunction* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Object);
  LGuardToFunction* lir =
      new (alloc()) LGuardToFunction(useRegisterAtStart(ins->object()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  defineReuseInput(lir, ins, 0);
}

void LIRGenerator::visitObjectClassToString(MObjectClassToString* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::String);
  auto* lir = new (alloc()) LObjectClassToString(
      useRegisterAtStart(ins->object()), tempFixed(CallTempReg0));
  assignSnapshot(lir, ins->bailoutKind());
  defineReturn(lir, ins);
}

void LIRGenerator::visitWasmAddOffset(MWasmAddOffset* ins) {
  MOZ_ASSERT(ins->offset());
  if (ins->base()->type() == MIRType::Int32) {
    MOZ_ASSERT(ins->type() == MIRType::Int32);
    MOZ_ASSERT(ins->offset() <= UINT32_MAX);  // Because memory32
    define(new (alloc()) LWasmAddOffset(useRegisterAtStart(ins->base())), ins);
  } else {
    MOZ_ASSERT(ins->type() == MIRType::Int64);
#ifdef JS_64BIT
    defineInt64(new (alloc())
                    LWasmAddOffset64(useInt64RegisterAtStart(ins->base())),
                ins);
#else
    // Avoid situation where the input is (a,b) and the output is (b,a).
    defineInt64ReuseInput(
        new (alloc()) LWasmAddOffset64(useInt64RegisterAtStart(ins->base())),
        ins, 0);
#endif
  }
}

void LIRGenerator::visitWasmLoadInstance(MWasmLoadInstance* ins) {
  if (ins->type() == MIRType::Int64) {
#ifdef JS_PUNBOX64
    LAllocation instance = useRegisterAtStart(ins->instance());
#else
    // Avoid reusing instance for a 64-bit output pair as the load clobbers the
    // first half of that pair before loading the second half.
    LAllocation instance = useRegister(ins->instance());
#endif
    auto* lir = new (alloc()) LWasmLoadInstance64(instance);
    defineInt64(lir, ins);
  } else {
    auto* lir =
        new (alloc()) LWasmLoadInstance(useRegisterAtStart(ins->instance()));
    define(lir, ins);
  }
}

void LIRGenerator::visitWasmStoreInstance(MWasmStoreInstance* ins) {
  MDefinition* value = ins->value();
  if (value->type() == MIRType::Int64) {
#ifdef JS_PUNBOX64
    LAllocation instance = useRegisterAtStart(ins->instance());
    LInt64Allocation valueAlloc = useInt64RegisterAtStart(value);
#else
    LAllocation instance = useRegister(ins->instance());
    LInt64Allocation valueAlloc = useInt64Register(value);
#endif
    add(new (alloc()) LWasmStoreSlotI64(valueAlloc, instance, ins->offset(),
                                        mozilla::Nothing()),
        ins);
  } else {
    MOZ_ASSERT(value->type() != MIRType::WasmAnyRef);
    LAllocation instance = useRegisterAtStart(ins->instance());
    LAllocation valueAlloc = useRegisterAtStart(value);
    add(new (alloc())
            LWasmStoreSlot(valueAlloc, instance, ins->offset(), value->type(),
                           MNarrowingOp::None, mozilla::Nothing()),
        ins);
  }
}

void LIRGenerator::visitWasmHeapReg(MWasmHeapReg* ins) {
#ifdef WASM_HAS_HEAPREG
  auto* lir = new (alloc()) LWasmHeapReg();
  define(lir, ins);
#else
  MOZ_CRASH();
#endif
}

void LIRGenerator::visitWasmBoundsCheck(MWasmBoundsCheck* ins) {
  MOZ_ASSERT(!ins->isRedundant());

  MDefinition* index = ins->index();
  MDefinition* boundsCheckLimit = ins->boundsCheckLimit();

  MOZ_ASSERT(boundsCheckLimit->type() == index->type());

  if (index->type() == MIRType::Int64) {
    if (JitOptions.spectreIndexMasking) {
      auto* lir = new (alloc()) LWasmBoundsCheck64(
          useInt64RegisterAtStart(index), useInt64Register(boundsCheckLimit));
      defineInt64ReuseInput(lir, ins, 0);
    } else {
      auto* lir = new (alloc())
          LWasmBoundsCheck64(useInt64RegisterAtStart(index),
                             useInt64RegisterAtStart(boundsCheckLimit));
      add(lir, ins);
    }
  } else {
    MOZ_ASSERT(index->type() == MIRType::Int32);

    if (JitOptions.spectreIndexMasking) {
      auto* lir = new (alloc()) LWasmBoundsCheck(useRegisterAtStart(index),
                                                 useRegister(boundsCheckLimit));
      defineReuseInput(lir, ins, 0);
    } else {
      auto* lir = new (alloc()) LWasmBoundsCheck(
          useRegisterAtStart(index), useRegisterAtStart(boundsCheckLimit));
      add(lir, ins);
    }
  }
}

void LIRGenerator::visitWasmBoundsCheckRange32(MWasmBoundsCheckRange32* ins) {
  MDefinition* index = ins->index();
  MDefinition* length = ins->length();
  MDefinition* limit = ins->limit();

  MOZ_ASSERT(index->type() == MIRType::Int32);
  MOZ_ASSERT(length->type() == MIRType::Int32);
  MOZ_ASSERT(limit->type() == MIRType::Int32);

  add(new (alloc()) LWasmBoundsCheckRange32(
          useRegister(index), useRegister(length), useRegister(limit), temp()),
      ins);
}

void LIRGenerator::visitWasmAlignmentCheck(MWasmAlignmentCheck* ins) {
  MDefinition* index = ins->index();
  if (index->type() == MIRType::Int64) {
    auto* lir =
        new (alloc()) LWasmAlignmentCheck64(useInt64RegisterAtStart(index));
    add(lir, ins);
  } else {
    auto* lir = new (alloc()) LWasmAlignmentCheck(useRegisterAtStart(index));
    add(lir, ins);
  }
}

void LIRGenerator::visitWasmLoadInstanceDataField(
    MWasmLoadInstanceDataField* ins) {
  size_t offs = wasm::Instance::offsetInData(ins->instanceDataOffset());
  if (ins->type() == MIRType::Int64) {
#ifdef JS_PUNBOX64
    LAllocation instance = useRegisterAtStart(ins->instance());
#else
    // Avoid reusing instance for the output pair as the load clobbers the first
    // half of that pair before loading the second half.
    LAllocation instance = useRegister(ins->instance());
#endif
    defineInt64(new (alloc())
                    LWasmLoadSlotI64(instance, offs, mozilla::Nothing()),
                ins);
  } else {
    LAllocation instance = useRegisterAtStart(ins->instance());
    define(new (alloc()) LWasmLoadSlot(instance, offs, ins->type(),
                                       MWideningOp::None, mozilla::Nothing()),
           ins);
  }
}

void LIRGenerator::visitWasmLoadGlobalCell(MWasmLoadGlobalCell* ins) {
  if (ins->type() == MIRType::Int64) {
#ifdef JS_PUNBOX64
    LAllocation cellPtr = useRegisterAtStart(ins->cellPtr());
#else
    // Avoid reusing cellPtr for the output pair as the load clobbers the first
    // half of that pair before loading the second half.
    LAllocation cellPtr = useRegister(ins->cellPtr());
#endif
    defineInt64(new (alloc())
                    LWasmLoadSlotI64(cellPtr, /*offset=*/0, mozilla::Nothing()),
                ins);
  } else {
    LAllocation cellPtr = useRegisterAtStart(ins->cellPtr());
    define(new (alloc()) LWasmLoadSlot(cellPtr, /*offset=*/0, ins->type(),
                                       MWideningOp::None, mozilla::Nothing()),
           ins);
  }
}

void LIRGenerator::visitWasmLoadTableElement(MWasmLoadTableElement* ins) {
  LAllocation elements = useRegisterAtStart(ins->elements());
  LAllocation index = useRegisterAtStart(ins->index());
  define(new (alloc()) LWasmLoadTableElement(elements, index), ins);
}

void LIRGenerator::visitWasmStoreInstanceDataField(
    MWasmStoreInstanceDataField* ins) {
  MDefinition* value = ins->value();
  size_t offs = wasm::Instance::offsetInData(ins->instanceDataOffset());
  if (value->type() == MIRType::Int64) {
#ifdef JS_PUNBOX64
    LAllocation instance = useRegisterAtStart(ins->instance());
    LInt64Allocation valueAlloc = useInt64RegisterAtStart(value);
#else
    LAllocation instance = useRegister(ins->instance());
    LInt64Allocation valueAlloc = useInt64Register(value);
#endif
    add(new (alloc())
            LWasmStoreSlotI64(valueAlloc, instance, offs, mozilla::Nothing()),
        ins);
  } else {
    MOZ_ASSERT(value->type() != MIRType::WasmAnyRef);
    LAllocation instance = useRegisterAtStart(ins->instance());
    LAllocation valueAlloc = useRegisterAtStart(value);
    add(new (alloc()) LWasmStoreSlot(valueAlloc, instance, offs, value->type(),
                                     MNarrowingOp::None, mozilla::Nothing()),
        ins);
  }
}

void LIRGenerator::visitWasmStoreGlobalCell(MWasmStoreGlobalCell* ins) {
  MDefinition* value = ins->value();
  size_t offs = 0;
  if (value->type() == MIRType::Int64) {
#ifdef JS_PUNBOX64
    LAllocation cellPtr = useRegisterAtStart(ins->cellPtr());
    LInt64Allocation valueAlloc = useInt64RegisterAtStart(value);
#else
    LAllocation cellPtr = useRegister(ins->cellPtr());
    LInt64Allocation valueAlloc = useInt64Register(value);
#endif
    add(new (alloc())
            LWasmStoreSlotI64(valueAlloc, cellPtr, offs, mozilla::Nothing()));
  } else {
    MOZ_ASSERT(value->type() != MIRType::WasmAnyRef);
    LAllocation cellPtr = useRegisterAtStart(ins->cellPtr());
    LAllocation valueAlloc = useRegisterAtStart(value);
    add(new (alloc()) LWasmStoreSlot(valueAlloc, cellPtr, offs, value->type(),
                                     MNarrowingOp::None, mozilla::Nothing()));
  }
}

void LIRGenerator::visitWasmStoreStackResult(MWasmStoreStackResult* ins) {
  MDefinition* stackResultArea = ins->stackResultArea();
  MDefinition* value = ins->value();
  size_t offs = ins->offset();
  LInstruction* lir;
  if (value->type() == MIRType::Int64) {
    lir = new (alloc())
        LWasmStoreSlotI64(useInt64Register(value), useRegister(stackResultArea),
                          offs, mozilla::Nothing());
  } else {
    MOZ_ASSERT(value->type() != MIRType::WasmAnyRef);
    lir = new (alloc())
        LWasmStoreSlot(useRegister(value), useRegister(stackResultArea), offs,
                       value->type(), MNarrowingOp::None, mozilla::Nothing());
  }
  add(lir, ins);
}

void LIRGenerator::visitWasmDerivedPointer(MWasmDerivedPointer* ins) {
  LAllocation base = useRegisterAtStart(ins->base());
  define(new (alloc()) LWasmDerivedPointer(base), ins);
}

void LIRGenerator::visitWasmDerivedIndexPointer(MWasmDerivedIndexPointer* ins) {
  LAllocation base = useRegisterAtStart(ins->base());
  LAllocation index = useRegisterAtStart(ins->index());
  define(new (alloc()) LWasmDerivedIndexPointer(base, index), ins);
}

void LIRGenerator::visitWasmStoreRef(MWasmStoreRef* ins) {
  LAllocation instance = useRegister(ins->instance());
  LAllocation valueBase = useFixed(ins->valueBase(), PreBarrierReg);
  LAllocation value = useRegister(ins->value());
  uint32_t valueOffset = ins->offset();
  add(new (alloc())
          LWasmStoreRef(instance, valueBase, value, temp(), valueOffset,
                        mozilla::Nothing(), ins->preBarrierKind()),
      ins);
}

void LIRGenerator::visitWasmPostWriteBarrierImmediate(
    MWasmPostWriteBarrierImmediate* ins) {
  LWasmPostWriteBarrierImmediate* lir =
      new (alloc()) LWasmPostWriteBarrierImmediate(
          useFixed(ins->instance(), InstanceReg), useRegister(ins->object()),
          useRegister(ins->valueBase()), useRegister(ins->value()), temp(),
          ins->valueOffset());
  add(lir, ins);
  assignWasmSafepoint(lir);
}

void LIRGenerator::visitWasmPostWriteBarrierIndex(
    MWasmPostWriteBarrierIndex* ins) {
  LWasmPostWriteBarrierIndex* lir = new (alloc()) LWasmPostWriteBarrierIndex(
      useFixed(ins->instance(), InstanceReg), useRegister(ins->object()),
      useRegister(ins->valueBase()), useRegister(ins->index()),
      useRegister(ins->value()), temp(), ins->elemSize());
  add(lir, ins);
  assignWasmSafepoint(lir);
}

void LIRGenerator::visitWasmParameter(MWasmParameter* ins) {
  ABIArg abi = ins->abi();
  if (ins->type() == MIRType::StackResults) {
    // Functions that return stack results receive an extra incoming parameter
    // with type MIRType::StackResults.  This value is a pointer to fresh
    // memory.  Here we treat it as if it were in fact MIRType::Pointer.
    auto* lir = new (alloc()) LWasmParameter;
    LDefinition def(LDefinition::TypeFrom(MIRType::Pointer),
                    LDefinition::FIXED);
    def.setOutput(abi.argInRegister() ? LAllocation(abi.reg())
                                      : LArgument(abi.offsetFromArgBase()));
    define(lir, ins, def);
    return;
  }
  if (abi.argInRegister()) {
#if defined(JS_NUNBOX32)
    if (abi.isGeneralRegPair()) {
      defineInt64Fixed(
          new (alloc()) LWasmParameterI64, ins,
          LInt64Allocation(LAllocation(AnyRegister(abi.gpr64().high)),
                           LAllocation(AnyRegister(abi.gpr64().low))));
      return;
    }
#endif
    defineFixed(new (alloc()) LWasmParameter, ins, LAllocation(abi.reg()));
    return;
  }
  if (ins->type() == MIRType::Int64) {
    MOZ_ASSERT(!abi.argInRegister());
    defineInt64Fixed(
        new (alloc()) LWasmParameterI64, ins,
#if defined(JS_NUNBOX32)
        LInt64Allocation(LArgument(abi.offsetFromArgBase() + INT64HIGH_OFFSET),
                         LArgument(abi.offsetFromArgBase() + INT64LOW_OFFSET))
#else
        LInt64Allocation(LArgument(abi.offsetFromArgBase()))
#endif
    );
  } else {
    MOZ_ASSERT(IsNumberType(ins->type()) || ins->type() == MIRType::WasmAnyRef
#ifdef ENABLE_WASM_SIMD
               || ins->type() == MIRType::Simd128
#endif
    );
    defineFixed(new (alloc()) LWasmParameter, ins,
                LArgument(abi.offsetFromArgBase()));
  }
}

void LIRGenerator::visitWasmReturn(MWasmReturn* ins) {
  MDefinition* rval = ins->getOperand(0);
  MDefinition* instance = ins->getOperand(1);

  if (rval->type() == MIRType::Int64) {
    add(new (alloc()) LWasmReturnI64(useInt64Fixed(rval, ReturnReg64),
                                     useFixed(instance, InstanceReg)));
    return;
  }

  LAllocation returnReg;
  if (rval->type() == MIRType::Float32) {
    returnReg = useFixed(rval, ReturnFloat32Reg);
  } else if (rval->type() == MIRType::Double) {
    returnReg = useFixed(rval, ReturnDoubleReg);
#ifdef ENABLE_WASM_SIMD
  } else if (rval->type() == MIRType::Simd128) {
    returnReg = useFixed(rval, ReturnSimd128Reg);
#endif
  } else if (rval->type() == MIRType::Int32 ||
             rval->type() == MIRType::WasmAnyRef) {
    returnReg = useFixed(rval, ReturnReg);
  } else {
    MOZ_CRASH("Unexpected wasm return type");
  }

  LWasmReturn* lir =
      new (alloc()) LWasmReturn(useFixed(instance, InstanceReg), returnReg);
  add(lir);
}

void LIRGenerator::visitWasmReturnVoid(MWasmReturnVoid* ins) {
  MDefinition* instance = ins->getOperand(0);
  LWasmReturnVoid* lir =
      new (alloc()) LWasmReturnVoid(useFixed(instance, InstanceReg));
  add(lir);
}

void LIRGenerator::visitWasmStackArg(MWasmStackArg* ins) {
  if (ins->arg()->type() == MIRType::Int64) {
    add(new (alloc())
            LWasmStackArgI64(useInt64RegisterOrConstantAtStart(ins->arg())),
        ins);
  } else if (IsFloatingPointType(ins->arg()->type())) {
    MOZ_ASSERT(!ins->arg()->isEmittedAtUses());
    add(new (alloc()) LWasmStackArg(useRegisterAtStart(ins->arg())), ins);
  } else {
    add(new (alloc()) LWasmStackArg(useRegisterOrConstantAtStart(ins->arg())),
        ins);
  }
}

void LIRGenerator::visitWasmRegisterResult(MWasmRegisterResult* ins) {
  auto* lir = new (alloc()) LWasmRegisterResult();
  uint32_t vreg = getVirtualRegister();
  MOZ_ASSERT(ins->type() != MIRType::Int64);
  auto type = LDefinition::TypeFrom(ins->type());
  lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ins->loc())));
  ins->setVirtualRegister(vreg);
  add(lir, ins);
}

void LIRGenerator::visitWasmFloatRegisterResult(MWasmFloatRegisterResult* ins) {
  auto* lir = new (alloc()) LWasmRegisterResult();
  uint32_t vreg = getVirtualRegister();
  auto type = LDefinition::TypeFrom(ins->type());
  lir->setDef(0, LDefinition(vreg, type, LFloatReg(ins->loc())));
  ins->setVirtualRegister(vreg);
  add(lir, ins);
}

void LIRGenerator::visitWasmRegister64Result(MWasmRegister64Result* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Int64);
  uint32_t vreg = getVirtualRegister();

#if defined(JS_NUNBOX32)
  auto* lir = new (alloc()) LWasmRegisterPairResult();
  lir->setDef(INT64LOW_INDEX,
              LDefinition(vreg + INT64LOW_INDEX, LDefinition::GENERAL,
                          LGeneralReg(ins->loc().low)));
  lir->setDef(INT64HIGH_INDEX,
              LDefinition(vreg + INT64HIGH_INDEX, LDefinition::GENERAL,
                          LGeneralReg(ins->loc().high)));
  getVirtualRegister();
#elif defined(JS_PUNBOX64)
  auto* lir = new (alloc()) LWasmRegisterResult();
  lir->setDef(
      0, LDefinition(vreg, LDefinition::GENERAL, LGeneralReg(ins->loc().reg)));
#else
#  error expected either JS_NUNBOX32 or JS_PUNBOX64
#endif

  ins->setVirtualRegister(vreg);
  add(lir, ins);
}

void LIRGenerator::visitWasmStackResultArea(MWasmStackResultArea* ins) {
  MOZ_ASSERT(ins->type() == MIRType::StackResults);
  auto* lir = new (alloc()) LWasmStackResultArea(temp());
  uint32_t vreg = getVirtualRegister();
  lir->setDef(0,
              LDefinition(vreg, LDefinition::STACKRESULTS, LDefinition::STACK));
  ins->setVirtualRegister(vreg);
  add(lir, ins);
}

void LIRGenerator::visitWasmStackResult(MWasmStackResult* ins) {
  MWasmStackResultArea* area = ins->resultArea()->toWasmStackResultArea();
  LDefinition::Policy pol = LDefinition::STACK;

  if (ins->type() == MIRType::Int64) {
    auto* lir = new (alloc()) LWasmStackResult64;
    lir->setOperand(0, use(area, LUse(LUse::STACK, /* usedAtStart = */ true)));
    uint32_t vreg = getVirtualRegister();
    LDefinition::Type typ = LDefinition::GENERAL;
#if defined(JS_NUNBOX32)
    getVirtualRegister();
    lir->setDef(INT64LOW_INDEX, LDefinition(vreg + INT64LOW_INDEX, typ, pol));
    lir->setDef(INT64HIGH_INDEX, LDefinition(vreg + INT64HIGH_INDEX, typ, pol));
#else
    lir->setDef(0, LDefinition(vreg, typ, pol));
#endif
    ins->setVirtualRegister(vreg);
    add(lir, ins);
    return;
  }

  auto* lir = new (alloc()) LWasmStackResult;
  lir->setOperand(0, use(area, LUse(LUse::STACK, /* usedAtStart = */ true)));
  uint32_t vreg = getVirtualRegister();
  LDefinition::Type typ = LDefinition::TypeFrom(ins->type());
  lir->setDef(0, LDefinition(vreg, typ, pol));
  ins->setVirtualRegister(vreg);
  add(lir, ins);
}

void LIRGenerator::visitWasmStackSwitchToSuspendable(
    MWasmStackSwitchToSuspendable* ins) {
#ifdef ENABLE_WASM_JSPI
  auto* lir = new (alloc()) LWasmStackSwitchToSuspendable(
      useFixedAtStart(ins->suspender(), ABINonArgReg0),
      useFixedAtStart(ins->fn(), ABINonArgReg1),
      useFixedAtStart(ins->data(), ABINonArgReg2));

  add(lir, ins);
  assignWasmSafepoint(lir);
#else
  MOZ_CRASH("NYI");
#endif
}

void LIRGenerator::visitWasmStackSwitchToMain(MWasmStackSwitchToMain* ins) {
#ifdef ENABLE_WASM_JSPI
  auto* lir = new (alloc())
      LWasmStackSwitchToMain(useFixedAtStart(ins->suspender(), ABINonArgReg0),
                             useFixedAtStart(ins->fn(), ABINonArgReg1),
                             useFixedAtStart(ins->data(), ABINonArgReg2));

  defineReturn(lir, ins);
  assignWasmSafepoint(lir);
#else
  MOZ_CRASH("NYI");
#endif
}

void LIRGenerator::visitWasmStackContinueOnSuspendable(
    MWasmStackContinueOnSuspendable* ins) {
#ifdef ENABLE_WASM_JSPI
  auto* lir = new (alloc()) LWasmStackContinueOnSuspendable(
      useFixedAtStart(ins->suspender(), ABINonArgReg0),
      useFixedAtStart(ins->result(), ABINonArgReg2));

  add(lir, ins);
  assignWasmSafepoint(lir);
#else
  MOZ_CRASH("NYI");
#endif
}

template <class MWasmCallT>
void LIRGenerator::visitWasmCall(MWasmCallT ins) {
  bool needsBoundsCheck = true;
  mozilla::Maybe<uint32_t> tableSize;

  if (ins->callee().isTable()) {
    MDefinition* index = ins->getOperand(ins->numArgs());

    if (ins->callee().which() == wasm::CalleeDesc::WasmTable) {
      uint64_t minLength = ins->callee().wasmTableMinLength();
      mozilla::Maybe<uint64_t> maxLength = ins->callee().wasmTableMaxLength();
      if (index->isConstant() &&
          uint32_t(index->toConstant()->toInt32()) < minLength) {
        needsBoundsCheck = false;
      }
      if (minLength <= UINT32_MAX && maxLength.isSome() &&
          *maxLength == minLength) {
        tableSize = maxLength;
      }
    }
  }

  auto* lir = allocateVariadic<LWasmCall>(ins->numOperands(), needsBoundsCheck,
                                          tableSize);
  if (!lir) {
    abort(AbortReason::Alloc, "OOM: LIRGenerator::lowerWasmCall");
    return;
  }

  for (unsigned i = 0; i < ins->numArgs(); i++) {
    lir->setOperand(
        i, useFixedAtStart(ins->getOperand(i), ins->registerForArg(i)));
  }

  if (ins->callee().isTable()) {
    MDefinition* index = ins->getOperand(ins->numArgs());
    lir->setOperand(ins->numArgs(),
                    useFixedAtStart(index, WasmTableCallIndexReg));
  }
  if (ins->callee().isFuncRef()) {
    MDefinition* ref = ins->getOperand(ins->numArgs());
    lir->setOperand(ins->numArgs(), useFixedAtStart(ref, WasmCallRefReg));
  }

  add(lir, ins);
  assignWasmSafepoint(lir);

  // WasmCall with WasmTable has two call instructions, and they both need a
  // safepoint associated with them.  Create a second safepoint here; the node
  // otherwise does nothing, and codegen for it only marks the safepoint at the
  // node.
  if ((ins->callee().which() == wasm::CalleeDesc::WasmTable ||
       ins->callee().which() == wasm::CalleeDesc::FuncRef) &&
      !ins->isWasmReturnCall()) {
    auto* adjunctSafepoint = new (alloc()) LWasmCallIndirectAdjunctSafepoint();
    add(adjunctSafepoint);
    assignWasmSafepoint(adjunctSafepoint);
    lir->setAdjunctSafepoint(adjunctSafepoint);
  }
}

void LIRGenerator::visitWasmCallCatchable(MWasmCallCatchable* ins) {
  visitWasmCall(ins);
}

void LIRGenerator::visitWasmCallUncatchable(MWasmCallUncatchable* ins) {
  visitWasmCall(ins);
}

void LIRGenerator::visitWasmReturnCall(MWasmReturnCall* ins) {
  visitWasmCall(ins);
}

void LIRGenerator::visitWasmCallLandingPrePad(MWasmCallLandingPrePad* ins) {
  add(new (alloc()) LWasmCallLandingPrePad, ins);
}

void LIRGenerator::visitSetDOMProperty(MSetDOMProperty* ins) {
  MDefinition* val = ins->value();

  Register cxReg, objReg, privReg, valueReg;
  GetTempRegForIntArg(0, 0, &cxReg);
  GetTempRegForIntArg(1, 0, &objReg);
  GetTempRegForIntArg(2, 0, &privReg);
  GetTempRegForIntArg(3, 0, &valueReg);

  // Keep using GetTempRegForIntArg, since we want to make sure we
  // don't clobber registers we're already using.
  Register tempReg1, tempReg2;
  GetTempRegForIntArg(4, 0, &tempReg1);
  mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(5, 0, &tempReg2);
  MOZ_ASSERT(ok, "How can we not have six temp registers?");

  auto* lir = new (alloc()) LSetDOMProperty(
      useFixedAtStart(ins->object(), objReg),
      useBoxFixedAtStart(val, tempReg1, tempReg2), tempFixed(cxReg),
      tempFixed(privReg), tempFixed(valueReg));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGetDOMProperty(MGetDOMProperty* ins) {
  Register cxReg, objReg, privReg, valueReg;
  GetTempRegForIntArg(0, 0, &cxReg);
  GetTempRegForIntArg(1, 0, &objReg);
  GetTempRegForIntArg(2, 0, &privReg);
  mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(3, 0, &valueReg);
  MOZ_ASSERT(ok, "How can we not have four temp registers?");
  auto* lir = new (alloc())
      LGetDOMProperty(useFixedAtStart(ins->object(), objReg), tempFixed(cxReg),
                      tempFixed(privReg), tempFixed(valueReg));

  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGetDOMMember(MGetDOMMember* ins) {
  MOZ_ASSERT(ins->isDomMovable(), "Members had better be movable");
  // We wish we could assert that ins->domAliasSet() == JSJitInfo::AliasNone,
  // but some MGetDOMMembers are for [Pure], not [Constant] properties, whose
  // value can in fact change as a result of DOM setters and method calls.
  MOZ_ASSERT(ins->domAliasSet() != JSJitInfo::AliasEverything,
             "Member gets had better not alias the world");

  MDefinition* obj = ins->object();
  MOZ_ASSERT(obj->type() == MIRType::Object);

  MIRType type = ins->type();

  if (type == MIRType::Value) {
    LGetDOMMemberV* lir = new (alloc()) LGetDOMMemberV(useRegisterAtStart(obj));
    defineBox(lir, ins);
  } else {
    LGetDOMMemberT* lir =
        new (alloc()) LGetDOMMemberT(useRegisterForTypedLoad(obj, type));
    define(lir, ins);
  }
}

void LIRGenerator::visitLoadDOMExpandoValue(MLoadDOMExpandoValue* ins) {
  MOZ_ASSERT(ins->proxy()->type() == MIRType::Object);
  auto* lir =
      new (alloc()) LLoadDOMExpandoValue(useRegisterAtStart(ins->proxy()));
  defineBox(lir, ins);
}

void LIRGenerator::visitLoadDOMExpandoValueGuardGeneration(
    MLoadDOMExpandoValueGuardGeneration* ins) {
  MOZ_ASSERT(ins->proxy()->type() == MIRType::Object);
  auto* lir = new (alloc())
      LLoadDOMExpandoValueGuardGeneration(useRegisterAtStart(ins->proxy()));
  assignSnapshot(lir, ins->bailoutKind());
  defineBox(lir, ins);
}

void LIRGenerator::visitLoadDOMExpandoValueIgnoreGeneration(
    MLoadDOMExpandoValueIgnoreGeneration* ins) {
  MOZ_ASSERT(ins->proxy()->type() == MIRType::Object);
  auto* lir = new (alloc())
      LLoadDOMExpandoValueIgnoreGeneration(useRegisterAtStart(ins->proxy()));
  defineBox(lir, ins);
}

void LIRGenerator::visitGuardDOMExpandoMissingOrGuardShape(
    MGuardDOMExpandoMissingOrGuardShape* ins) {
  MOZ_ASSERT(ins->expando()->type() == MIRType::Value);
  auto* lir = new (alloc())
      LGuardDOMExpandoMissingOrGuardShape(useBox(ins->expando()), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->expando());
}

void LIRGenerator::visitIncrementWarmUpCounter(MIncrementWarmUpCounter* ins) {
  LIncrementWarmUpCounter* lir = new (alloc()) LIncrementWarmUpCounter(temp());
  add(lir, ins);
}

void LIRGenerator::visitLexicalCheck(MLexicalCheck* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::Value);
  LLexicalCheck* lir = new (alloc()) LLexicalCheck(useBox(input));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, input);
}

void LIRGenerator::visitThrowRuntimeLexicalError(
    MThrowRuntimeLexicalError* ins) {
  LThrowRuntimeLexicalError* lir = new (alloc()) LThrowRuntimeLexicalError();
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitThrowMsg(MThrowMsg* ins) {
  LThrowMsg* lir = new (alloc()) LThrowMsg();
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGlobalDeclInstantiation(MGlobalDeclInstantiation* ins) {
  LGlobalDeclInstantiation* lir = new (alloc()) LGlobalDeclInstantiation();
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitDebugger(MDebugger* ins) {
  auto* lir = new (alloc()) LDebugger(tempFixed(CallTempReg0));
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
}

void LIRGenerator::visitAtomicIsLockFree(MAtomicIsLockFree* ins) {
  define(new (alloc()) LAtomicIsLockFree(useRegister(ins->input())), ins);
}

void LIRGenerator::visitAtomicPause(MAtomicPause* ins) {
  add(new (alloc()) LAtomicPause());
}

void LIRGenerator::visitCheckReturn(MCheckReturn* ins) {
  MDefinition* retVal = ins->returnValue();
  MDefinition* thisVal = ins->thisValue();
  MOZ_ASSERT(retVal->type() == MIRType::Value);
  MOZ_ASSERT(thisVal->type() == MIRType::Value);

  auto* lir =
      new (alloc()) LCheckReturn(useBoxAtStart(retVal), useBoxAtStart(thisVal));
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCheckIsObj(MCheckIsObj* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::Value);

  LCheckIsObj* lir = new (alloc()) LCheckIsObj(useBox(input));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

#ifdef JS_PUNBOX64
void LIRGenerator::visitCheckScriptedProxyGetResult(
    MCheckScriptedProxyGetResult* ins) {
  MDefinition* target = ins->target();
  MDefinition* id = ins->id();
  MDefinition* value = ins->value();

  LCheckScriptedProxyGetResult* lir =
      new (alloc()) LCheckScriptedProxyGetResult(useBox(target), useBox(id),
                                                 useBox(value), temp(), temp());
  add(lir, ins);
  assignSafepoint(lir, ins);
}
#endif

void LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins) {
  MDefinition* checkVal = ins->checkValue();
  MOZ_ASSERT(checkVal->type() == MIRType::Value);

  auto* lir = new (alloc()) LCheckObjCoercible(useBoxAtStart(checkVal));
  redefine(ins, checkVal);
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCheckClassHeritage(MCheckClassHeritage* ins) {
  MDefinition* heritage = ins->heritage();
  MOZ_ASSERT(heritage->type() == MIRType::Value);

  auto* lir =
      new (alloc()) LCheckClassHeritage(useBox(heritage), temp(), temp());
  redefine(ins, heritage);
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCheckThis(MCheckThis* ins) {
  MDefinition* thisValue = ins->thisValue();
  MOZ_ASSERT(thisValue->type() == MIRType::Value);

  auto* lir = new (alloc()) LCheckThis(useBoxAtStart(thisValue));
  redefine(ins, thisValue);
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCheckThisReinit(MCheckThisReinit* ins) {
  MDefinition* thisValue = ins->thisValue();
  MOZ_ASSERT(thisValue->type() == MIRType::Value);

  auto* lir = new (alloc()) LCheckThisReinit(useBoxAtStart(thisValue));
  redefine(ins, thisValue);
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGenerator(MGenerator* ins) {
  auto* lir =
      new (alloc()) LGenerator(useRegisterAtStart(ins->callee()),
                               useRegisterAtStart(ins->environmentChain()),
                               useRegisterAtStart(ins->argsObject()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitAsyncResolve(MAsyncResolve* ins) {
  auto* lir = new (alloc()) LAsyncResolve(useRegisterAtStart(ins->generator()),
                                          useBoxAtStart(ins->value()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitAsyncReject(MAsyncReject* ins) {
  auto* lir = new (alloc())
      LAsyncReject(useRegisterAtStart(ins->generator()),
                   useBoxAtStart(ins->reason()), useBoxAtStart(ins->stack()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitAsyncAwait(MAsyncAwait* ins) {
  MOZ_ASSERT(ins->generator()->type() == MIRType::Object);
  auto* lir = new (alloc()) LAsyncAwait(useBoxAtStart(ins->value()),
                                        useRegisterAtStart(ins->generator()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCanSkipAwait(MCanSkipAwait* ins) {
  auto* lir = new (alloc()) LCanSkipAwait(useBoxAtStart(ins->value()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMaybeExtractAwaitValue(MMaybeExtractAwaitValue* ins) {
  auto* lir = new (alloc()) LMaybeExtractAwaitValue(
      useBoxAtStart(ins->value()), useRegisterAtStart(ins->canSkip()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins) {
  MDefinition* checkVal = ins->checkValue();
  MOZ_ASSERT(checkVal->type() == MIRType::Value);

  LDebugCheckSelfHosted* lir =
      new (alloc()) LDebugCheckSelfHosted(useBoxAtStart(checkVal));
  redefine(ins, checkVal);
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitIsPackedArray(MIsPackedArray* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Boolean);

  auto lir = new (alloc()) LIsPackedArray(useRegister(ins->object()), temp());
  define(lir, ins);
}

void LIRGenerator::visitGuardArrayIsPacked(MGuardArrayIsPacked* ins) {
  MOZ_ASSERT(ins->array()->type() == MIRType::Object);

  auto* lir = new (alloc())
      LGuardArrayIsPacked(useRegister(ins->array()), temp(), temp());
  assignSnapshot(lir, ins->bailoutKind());
  add(lir, ins);
  redefine(ins, ins->array());
}

void LIRGenerator::visitGetPrototypeOf(MGetPrototypeOf* ins) {
  MOZ_ASSERT(ins->target()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Value);

  auto lir = new (alloc()) LGetPrototypeOf(useRegister(ins->target()));
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitObjectWithProto(MObjectWithProto* ins) {
  MOZ_ASSERT(ins->prototype()->type() == MIRType::Value);
  MOZ_ASSERT(ins->type() == MIRType::Object);

  auto* lir = new (alloc()) LObjectWithProto(useBoxAtStart(ins->prototype()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitObjectStaticProto(MObjectStaticProto* ins) {
  MOZ_ASSERT(ins->object()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Object);

  auto* lir =
      new (alloc()) LObjectStaticProto(useRegisterAtStart(ins->object()));
  define(lir, ins);
};

void LIRGenerator::visitBuiltinObject(MBuiltinObject* ins) {
  MOZ_ASSERT(ins->type() == MIRType::Object);

  auto* lir = new (alloc()) LBuiltinObject();
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitReturn(MReturn* ret) {
  return visitReturnImpl(ret->getOperand(0));
}

void LIRGenerator::visitGeneratorReturn(MGeneratorReturn* ret) {
  return visitReturnImpl(ret->getOperand(0), true);
}

void LIRGenerator::visitSuperFunction(MSuperFunction* ins) {
  MOZ_ASSERT(ins->callee()->type() == MIRType::Object);
  MOZ_ASSERT(ins->type() == MIRType::Value);

  auto* lir = new (alloc()) LSuperFunction(useRegister(ins->callee()), temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitInitHomeObject(MInitHomeObject* ins) {
  MDefinition* function = ins->function();
  MOZ_ASSERT(function->type() == MIRType::Object);

  MDefinition* homeObject = ins->homeObject();
  MOZ_ASSERT(homeObject->type() == MIRType::Value);

  MOZ_ASSERT(ins->type() == MIRType::Object);

  auto* lir = new (alloc())
      LInitHomeObject(useRegisterAtStart(function), useBoxAtStart(homeObject));
  redefine(ins, function);
  add(lir, ins);
}

void LIRGenerator::visitIsTypedArrayConstructor(MIsTypedArrayConstructor* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  auto* lir = new (alloc()) LIsTypedArrayConstructor(useRegister(object));
  define(lir, ins);
}

void LIRGenerator::visitLoadValueTag(MLoadValueTag* ins) {
  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Value);

  define(new (alloc()) LLoadValueTag(useBoxAtStart(value)), ins);
}

void LIRGenerator::visitGuardTagNotEqual(MGuardTagNotEqual* ins) {
  MDefinition* lhs = ins->lhs();
  MOZ_ASSERT(lhs->type() == MIRType::Int32);

  MDefinition* rhs = ins->rhs();
  MOZ_ASSERT(rhs->type() == MIRType::Int32);

  auto* guard =
      new (alloc()) LGuardTagNotEqual(useRegister(lhs), useRegister(rhs));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
}

void LIRGenerator::visitLoadWrapperTarget(MLoadWrapperTarget* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  auto* lir = new (alloc()) LLoadWrapperTarget(useRegisterAtStart(object));
  if (ins->fallible()) {
    assignSnapshot(lir, ins->bailoutKind());
  }
  define(lir, ins);
}

void LIRGenerator::visitGuardHasGetterSetter(MGuardHasGetterSetter* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  auto* guard = new (alloc())
      LGuardHasGetterSetter(useRegisterAtStart(object), tempFixed(CallTempReg0),
                            tempFixed(CallTempReg1), tempFixed(CallTempReg2));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, object);
}

void LIRGenerator::visitGuardIsExtensible(MGuardIsExtensible* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  auto* guard = new (alloc()) LGuardIsExtensible(useRegister(object), temp());
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, object);
}

void LIRGenerator::visitGuardInt32IsNonNegative(MGuardInt32IsNonNegative* ins) {
  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  auto* guard = new (alloc()) LGuardInt32IsNonNegative(useRegister(index));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, index);
}

void LIRGenerator::visitGuardInt32Range(MGuardInt32Range* ins) {
  MDefinition* input = ins->input();
  MOZ_ASSERT(input->type() == MIRType::Int32);

  auto* guard = new (alloc()) LGuardInt32Range(useRegister(input));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, input);
}

void LIRGenerator::visitGuardIndexIsNotDenseElement(
    MGuardIndexIsNotDenseElement* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  LDefinition spectreTemp =
      BoundsCheckNeedsSpectreTemp() ? temp() : LDefinition::BogusTemp();

  auto* guard = new (alloc()) LGuardIndexIsNotDenseElement(
      useRegister(object), useRegister(index), temp(), spectreTemp);
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, index);
}

void LIRGenerator::visitGuardIndexIsValidUpdateOrAdd(
    MGuardIndexIsValidUpdateOrAdd* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  LDefinition spectreTemp =
      BoundsCheckNeedsSpectreTemp() ? temp() : LDefinition::BogusTemp();

  auto* guard = new (alloc()) LGuardIndexIsValidUpdateOrAdd(
      useRegister(object), useRegister(index), temp(), spectreTemp);
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, index);
}

void LIRGenerator::visitCallAddOrUpdateSparseElement(
    MCallAddOrUpdateSparseElement* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  MDefinition* value = ins->value();
  MOZ_ASSERT(value->type() == MIRType::Value);

  auto* lir = new (alloc()) LCallAddOrUpdateSparseElement(
      useRegisterAtStart(object), useRegisterAtStart(index),
      useBoxAtStart(value));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCallGetSparseElement(MCallGetSparseElement* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  auto* lir = new (alloc()) LCallGetSparseElement(useRegisterAtStart(object),
                                                  useRegisterAtStart(index));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCallNativeGetElement(MCallNativeGetElement* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  auto* lir = new (alloc()) LCallNativeGetElement(useRegisterAtStart(object),
                                                  useRegisterAtStart(index));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCallNativeGetElementSuper(
    MCallNativeGetElementSuper* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  MDefinition* receiver = ins->receiver();

  auto* lir = new (alloc()) LCallNativeGetElementSuper(
      useRegisterAtStart(object), useRegisterAtStart(index),
      useBoxAtStart(receiver));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCallObjectHasSparseElement(
    MCallObjectHasSparseElement* ins) {
  MDefinition* object = ins->object();
  MOZ_ASSERT(object->type() == MIRType::Object);

  MDefinition* index = ins->index();
  MOZ_ASSERT(index->type() == MIRType::Int32);

  auto* lir = new (alloc()) LCallObjectHasSparseElement(
      useRegisterAtStart(object), useRegisterAtStart(index),
      tempFixed(CallTempReg0), tempFixed(CallTempReg1));
  assignSnapshot(lir, ins->bailoutKind());
  defineReturn(lir, ins);
}

void LIRGenerator::visitBigIntAsIntN(MBigIntAsIntN* ins) {
  MOZ_ASSERT(ins->bits()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->input()->type() == MIRType::BigInt);

  auto* lir = new (alloc()) LBigIntAsIntN(useRegisterAtStart(ins->bits()),
                                          useRegisterAtStart(ins->input()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitBigIntAsUintN(MBigIntAsUintN* ins) {
  MOZ_ASSERT(ins->bits()->type() == MIRType::Int32);
  MOZ_ASSERT(ins->input()->type() == MIRType::BigInt);

  auto* lir = new (alloc()) LBigIntAsUintN(useRegisterAtStart(ins->bits()),
                                           useRegisterAtStart(ins->input()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitGuardNonGCThing(MGuardNonGCThing* ins) {
  MDefinition* input = ins->input();

  auto* guard = new (alloc()) LGuardNonGCThing(useBox(input));
  assignSnapshot(guard, ins->bailoutKind());
  add(guard, ins);
  redefine(ins, input);
}

void LIRGenerator::visitToHashableNonGCThing(MToHashableNonGCThing* ins) {
  auto* lir =
      new (alloc()) LToHashableNonGCThing(useBox(ins->input()), tempDouble());
  defineBox(lir, ins);
}

void LIRGenerator::visitToHashableString(MToHashableString* ins) {
  auto* lir = new (alloc()) LToHashableString(useRegister(ins->input()));
  define(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitToHashableValue(MToHashableValue* ins) {
  auto* lir =
      new (alloc()) LToHashableValue(useBox(ins->input()), tempDouble());
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitHashNonGCThing(MHashNonGCThing* ins) {
  auto* lir = new (alloc()) LHashNonGCThing(useBox(ins->input()), temp());
  define(lir, ins);
}

void LIRGenerator::visitHashString(MHashString* ins) {
  auto* lir = new (alloc()) LHashString(useRegister(ins->input()), temp());
  define(lir, ins);
}

void LIRGenerator::visitHashSymbol(MHashSymbol* ins) {
  auto* lir = new (alloc()) LHashSymbol(useRegister(ins->input()));
  define(lir, ins);
}

void LIRGenerator::visitHashBigInt(MHashBigInt* ins) {
  auto* lir = new (alloc())
      LHashBigInt(useRegister(ins->input()), temp(), temp(), temp());
  define(lir, ins);
}

void LIRGenerator::visitHashObject(MHashObject* ins) {
  auto* lir =
      new (alloc()) LHashObject(useRegister(ins->set()), useBox(ins->input()),
                                temp(), temp(), temp(), temp());
  define(lir, ins);
}

void LIRGenerator::visitHashValue(MHashValue* ins) {
  auto* lir =
      new (alloc()) LHashValue(useRegister(ins->set()), useBox(ins->input()),
                               temp(), temp(), temp(), temp());
  define(lir, ins);
}

void LIRGenerator::visitSetObjectHasNonBigInt(MSetObjectHasNonBigInt* ins) {
  auto* lir = new (alloc())
      LSetObjectHasNonBigInt(useRegister(ins->set()), useBox(ins->value()),
                             useRegister(ins->hash()), temp(), temp());
  define(lir, ins);
}

void LIRGenerator::visitSetObjectHasBigInt(MSetObjectHasBigInt* ins) {
  auto* lir = new (alloc()) LSetObjectHasBigInt(
      useRegister(ins->set()), useBox(ins->value()), useRegister(ins->hash()),
      temp(), temp(), temp(), temp());
  define(lir, ins);
}

void LIRGenerator::visitSetObjectHasValue(MSetObjectHasValue* ins) {
  auto* lir = new (alloc()) LSetObjectHasValue(
      useRegister(ins->set()), useBox(ins->value()), useRegister(ins->hash()),
      temp(), temp(), temp(), temp());
  define(lir, ins);
}

void LIRGenerator::visitSetObjectHasValueVMCall(MSetObjectHasValueVMCall* ins) {
  auto* lir = new (alloc()) LSetObjectHasValueVMCall(
      useRegisterAtStart(ins->set()), useBoxAtStart(ins->value()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitSetObjectDelete(MSetObjectDelete* ins) {
  auto* lir = new (alloc()) LSetObjectDelete(
      useRegisterAtStart(ins->setObject()), useBoxAtStart(ins->key()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitSetObjectAdd(MSetObjectAdd* ins) {
  auto* lir = new (alloc()) LSetObjectAdd(useRegisterAtStart(ins->setObject()),
                                          useBoxAtStart(ins->key()));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitSetObjectSize(MSetObjectSize* ins) {
  auto* lir = new (alloc()) LSetObjectSize(useRegisterAtStart(ins->set()));
  define(lir, ins);
}

void LIRGenerator::visitMapObjectHasNonBigInt(MMapObjectHasNonBigInt* ins) {
  auto* lir = new (alloc())
      LMapObjectHasNonBigInt(useRegister(ins->map()), useBox(ins->value()),
                             useRegister(ins->hash()), temp(), temp());
  define(lir, ins);
}

void LIRGenerator::visitMapObjectHasBigInt(MMapObjectHasBigInt* ins) {
  auto* lir = new (alloc()) LMapObjectHasBigInt(
      useRegister(ins->map()), useBox(ins->value()), useRegister(ins->hash()),
      temp(), temp(), temp(), temp());
  define(lir, ins);
}

void LIRGenerator::visitMapObjectHasValue(MMapObjectHasValue* ins) {
  auto* lir = new (alloc()) LMapObjectHasValue(
      useRegister(ins->map()), useBox(ins->value()), useRegister(ins->hash()),
      temp(), temp(), temp(), temp());
  define(lir, ins);
}

void LIRGenerator::visitMapObjectHasValueVMCall(MMapObjectHasValueVMCall* ins) {
  auto* lir = new (alloc()) LMapObjectHasValueVMCall(
      useRegisterAtStart(ins->map()), useBoxAtStart(ins->value()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMapObjectGetNonBigInt(MMapObjectGetNonBigInt* ins) {
  auto* lir = new (alloc())
      LMapObjectGetNonBigInt(useRegister(ins->map()), useBox(ins->value()),
                             useRegister(ins->hash()), temp(), temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitMapObjectGetBigInt(MMapObjectGetBigInt* ins) {
  auto* lir = new (alloc()) LMapObjectGetBigInt(
      useRegister(ins->map()), useBox(ins->value()), useRegister(ins->hash()),
      temp(), temp(), temp(), temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitMapObjectGetValue(MMapObjectGetValue* ins) {
  auto* lir = new (alloc()) LMapObjectGetValue(
      useRegister(ins->map()), useBox(ins->value()), useRegister(ins->hash()),
      temp(), temp(), temp(), temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitMapObjectGetValueVMCall(MMapObjectGetValueVMCall* ins) {
  auto* lir = new (alloc()) LMapObjectGetValueVMCall(
      useRegisterAtStart(ins->map()), useBoxAtStart(ins->value()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMapObjectDelete(MMapObjectDelete* ins) {
  auto* lir = new (alloc()) LMapObjectDelete(
      useRegisterAtStart(ins->mapObject()), useBoxAtStart(ins->key()));
  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMapObjectSet(MMapObjectSet* ins) {
  auto* lir = new (alloc())
      LMapObjectSet(useRegisterAtStart(ins->mapObject()),
                    useBoxAtStart(ins->key()), useBoxAtStart(ins->value()));
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitMapObjectSize(MMapObjectSize* ins) {
  auto* lir = new (alloc()) LMapObjectSize(useRegisterAtStart(ins->map()));
  define(lir, ins);
}

void LIRGenerator::visitDateFillLocalTimeSlots(MDateFillLocalTimeSlots* ins) {
  auto* lir =
      new (alloc()) LDateFillLocalTimeSlots(useRegister(ins->date()), temp());
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitDateHoursFromSecondsIntoYear(
    MDateHoursFromSecondsIntoYear* ins) {
  auto* lir = new (alloc()) LDateHoursFromSecondsIntoYear(
      useBox(ins->secondsIntoYear()), temp(), temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitDateMinutesFromSecondsIntoYear(
    MDateMinutesFromSecondsIntoYear* ins) {
  auto* lir = new (alloc()) LDateMinutesFromSecondsIntoYear(
      useBox(ins->secondsIntoYear()), temp(), temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitDateSecondsFromSecondsIntoYear(
    MDateSecondsFromSecondsIntoYear* ins) {
  auto* lir = new (alloc()) LDateSecondsFromSecondsIntoYear(
      useBox(ins->secondsIntoYear()), temp(), temp());
  defineBox(lir, ins);
}

void LIRGenerator::visitPostIntPtrConversion(MPostIntPtrConversion* ins) {
  // This operation is a no-op.
  redefine(ins, ins->input());
}

void LIRGenerator::visitConstant(MConstant* ins) {
  if (!IsFloatingPointType(ins->type()) && ins->canEmitAtUses()) {
    emitAtUses(ins);
    return;
  }

  switch (ins->type()) {
    case MIRType::Double:
      define(new (alloc()) LDouble(ins->toDouble()), ins);
      break;
    case MIRType::Float32:
      define(new (alloc()) LFloat32(ins->toFloat32()), ins);
      break;
    case MIRType::Boolean:
      define(new (alloc()) LInteger(ins->toBoolean()), ins);
      break;
    case MIRType::Int32:
      define(new (alloc()) LInteger(ins->toInt32()), ins);
      break;
    case MIRType::Int64:
      defineInt64(new (alloc()) LInteger64(ins->toInt64()), ins);
      break;
    case MIRType::IntPtr:
#ifdef JS_64BIT
      defineInt64(new (alloc()) LInteger64(ins->toIntPtr()), ins);
#else
      define(new (alloc()) LInteger(ins->toIntPtr()), ins);
#endif
      break;
    case MIRType::String:
      define(new (alloc()) LPointer(ins->toString()), ins);
      break;
    case MIRType::Symbol:
      define(new (alloc()) LPointer(ins->toSymbol()), ins);
      break;
    case MIRType::BigInt:
      define(new (alloc()) LPointer(ins->toBigInt()), ins);
      break;
    case MIRType::Object:
      define(new (alloc()) LPointer(&ins->toObject()), ins);
      break;
    case MIRType::Shape:
      MOZ_ASSERT(ins->isEmittedAtUses());
      break;
    default:
      // Constants of special types (undefined, null) should never flow into
      // here directly. Operations blindly consuming them require a Box.
      MOZ_CRASH("unexpected constant type");
  }
}

void LIRGenerator::visitConstantProto(MConstantProto* ins) {
  JSObject* obj = &ins->protoObject()->toConstant()->toObject();
  define(new (alloc()) LPointer(obj), ins);
}

void LIRGenerator::visitWasmNullConstant(MWasmNullConstant* ins) {
  define(new (alloc()) LWasmNullConstant(), ins);
}

void LIRGenerator::visitWasmFloatConstant(MWasmFloatConstant* ins) {
  switch (ins->type()) {
    case MIRType::Double:
      define(new (alloc()) LDouble(ins->toDouble()), ins);
      break;
    case MIRType::Float32:
      define(new (alloc()) LFloat32(ins->toFloat32()), ins);
      break;
#ifdef ENABLE_WASM_SIMD
    case MIRType::Simd128:
      define(new (alloc()) LSimd128(ins->toSimd128()), ins);
      break;
#endif
    default:
      MOZ_CRASH("unexpected constant type");
  }
}

#ifdef JS_JITSPEW
static void SpewResumePoint(MBasicBlock* block, MInstruction* ins,
                            MResumePoint* resumePoint) {
  Fprinter& out = JitSpewPrinter();
  out.printf("Current resume point %p details:\n", (void*)resumePoint);
  out.printf("    frame count: %u\n", resumePoint->frameCount());

  if (ins) {
    out.printf("    taken after: ");
    ins->printName(out);
  } else {
    out.printf("    taken at block %u entry", block->id());
  }
  out.printf("\n");

  out.printf("    pc: %p (script: %p, offset: %d)\n", (void*)resumePoint->pc(),
             (void*)resumePoint->block()->info().script(),
             int(resumePoint->block()->info().script()->pcToOffset(
                 resumePoint->pc())));

  for (size_t i = 0, e = resumePoint->numOperands(); i < e; i++) {
    MDefinition* in = resumePoint->getOperand(i);
    out.printf("    slot%u: ", (unsigned)i);
    in->printName(out);
    out.printf("\n");
  }
}
#endif

void LIRGenerator::visitInstructionDispatch(MInstruction* ins) {
#ifdef JS_CODEGEN_NONE
  // Don't compile the switch-statement below so that we don't have to define
  // the platform-specific visit* methods for the none-backend.
  MOZ_CRASH();
#else
  switch (ins->op()) {
#  define MIR_OP(op)              \
    case MDefinition::Opcode::op: \
      visit##op(ins->to##op());   \
      break;
    MIR_OPCODE_LIST(MIR_OP)
#  undef MIR_OP
    default:
      MOZ_CRASH("Invalid instruction");
  }
#endif
}

void LIRGeneratorShared::visitEmittedAtUses(MInstruction* ins) {
  static_cast<LIRGenerator*>(this)->visitInstructionDispatch(ins);
}

bool LIRGenerator::visitInstruction(MInstruction* ins) {
  MOZ_ASSERT(!errored());

  if (ins->isRecoveredOnBailout()) {
    MOZ_ASSERT(!JitOptions.disableRecoverIns);
    return true;
  }

  if (!gen->ensureBallast()) {
    return false;
  }
  visitInstructionDispatch(ins);

  if (ins->resumePoint()) {
    updateResumeState(ins);
  }

#ifdef DEBUG
  ins->setInWorklistUnchecked();
#endif

  // If no safepoint was created, there's no need for an OSI point.
  if (LOsiPoint* osiPoint = popOsiPoint()) {
    add(osiPoint);
  }

  return !errored();
}

bool LIRGenerator::definePhis() {
  size_t lirIndex = 0;
  MBasicBlock* block = current->mir();
  for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) {
    if (phi->type() == MIRType::Value) {
      defineUntypedPhi(*phi, lirIndex);
      lirIndex += BOX_PIECES;
    } else if (phi->type() == MIRType::Int64) {
      defineInt64Phi(*phi, lirIndex);
      lirIndex += INT64_PIECES;
    } else {
      defineTypedPhi(*phi, lirIndex);
      lirIndex += 1;
    }
  }
  return !errored();
}

void LIRGenerator::updateResumeState(MInstruction* ins) {
  lastResumePoint_ = ins->resumePoint();
#ifdef JS_JITSPEW
  if (JitSpewEnabled(JitSpew_IonSnapshots) && lastResumePoint_) {
    SpewResumePoint(nullptr, ins, lastResumePoint_);
  }
#endif
}

void LIRGenerator::updateResumeState(MBasicBlock* block) {
  // Note: RangeAnalysis can flag blocks as unreachable, but they are only
  // removed iff GVN (including UCE) is enabled.
  MOZ_ASSERT_IF(!mir()->compilingWasm() && !block->unreachable(),
                block->entryResumePoint());
  MOZ_ASSERT_IF(block->unreachable(), !mir()->optimizationInfo().gvnEnabled());
  lastResumePoint_ = block->entryResumePoint();
#ifdef JS_JITSPEW
  if (JitSpewEnabled(JitSpew_IonSnapshots) && lastResumePoint_) {
    SpewResumePoint(block, nullptr, lastResumePoint_);
  }
#endif
}

bool LIRGenerator::visitBlock(MBasicBlock* block) {
  current = block->lir();
  updateResumeState(block);

  if (!definePhis()) {
    return false;
  }

  MOZ_ASSERT_IF(block->unreachable(), !mir()->optimizationInfo().gvnEnabled());
  for (MInstructionIterator iter = block->begin(); *iter != block->lastIns();
       iter++) {
    if (!visitInstruction(*iter)) {
      return false;
    }
  }

  if (block->successorWithPhis()) {
    // If we have a successor with phis, lower the phi input now that we
    // are approaching the join point.
    MBasicBlock* successor = block->successorWithPhis();
    uint32_t position = block->positionInPhiSuccessor();
    size_t lirIndex = 0;
    for (MPhiIterator phi(successor->phisBegin()); phi != successor->phisEnd();
         phi++) {
      if (!gen->ensureBallast()) {
        return false;
      }

      MDefinition* opd = phi->getOperand(position);
      ensureDefined(opd);

      MOZ_ASSERT(opd->type() == phi->type());

      if (phi->type() == MIRType::Value) {
        lowerUntypedPhiInput(*phi, position, successor->lir(), lirIndex);
        lirIndex += BOX_PIECES;
      } else if (phi->type() == MIRType::Int64) {
        lowerInt64PhiInput(*phi, position, successor->lir(), lirIndex);
        lirIndex += INT64_PIECES;
      } else {
        lowerTypedPhiInput(*phi, position, successor->lir(), lirIndex);
        lirIndex += 1;
      }
    }
  }

  // Now emit the last instruction, which is some form of branch.
  if (!visitInstruction(block->lastIns())) {
    return false;
  }

  return true;
}

void LIRGenerator::visitNaNToZero(MNaNToZero* ins) {
  MDefinition* input = ins->input();

  if (ins->operandIsNeverNaN() && ins->operandIsNeverNegativeZero()) {
    redefine(ins, input);
    return;
  }
  LNaNToZero* lir =
      new (alloc()) LNaNToZero(useRegisterAtStart(input), tempDouble());
  defineReuseInput(lir, ins, 0);
}

bool LIRGenerator::generate() {
  // Create all blocks and prep all phis beforehand.
  for (ReversePostorderIterator block(graph.rpoBegin());
       block != graph.rpoEnd(); block++) {
    if (gen->shouldCancel("Lowering (preparation loop)")) {
      return false;
    }

    if (!lirGraph_.initBlock(*block)) {
      return false;
    }
  }

  for (ReversePostorderIterator block(graph.rpoBegin());
       block != graph.rpoEnd(); block++) {
    if (gen->shouldCancel("Lowering (main loop)")) {
      return false;
    }

    if (!visitBlock(*block)) {
      return false;
    }
  }

  lirGraph_.setArgumentSlotCount(maxargslots_);
  return true;
}

void LIRGenerator::visitPhi(MPhi* phi) {
  // Phi nodes are not lowered because they are only meaningful for the register
  // allocator.
  MOZ_CRASH("Unexpected Phi node during Lowering.");
}

void LIRGenerator::visitBeta(MBeta* beta) {
  // Beta nodes are supposed to be removed before because they are
  // only used to carry the range information for Range analysis
  MOZ_CRASH("Unexpected Beta node during Lowering.");
}

void LIRGenerator::visitObjectState(MObjectState* objState) {
  // ObjectState nodes are always recovered on bailouts
  MOZ_CRASH("Unexpected ObjectState node during Lowering.");
}

void LIRGenerator::visitArrayState(MArrayState* objState) {
  // ArrayState nodes are always recovered on bailouts
  MOZ_CRASH("Unexpected ArrayState node during Lowering.");
}

void LIRGenerator::visitIonToWasmCall(MIonToWasmCall* ins) {
  // The instruction needs a temp register:
  // - that's not the FramePointer, since wasm is going to use it in the
  // function.
  // - that's not aliasing an input register.
  LDefinition scratch = tempFixed(ABINonArgReg0);

  // Note that since this is a LIR call instruction, regalloc will prevent
  // the use*AtStart below from reusing any of the temporaries.

  LInstruction* lir;
  if (ins->type() == MIRType::Value) {
    lir = allocateVariadic<LIonToWasmCallV>(ins->numOperands(), scratch);
  } else if (ins->type() == MIRType::Int64) {
    lir = allocateVariadic<LIonToWasmCallI64>(ins->numOperands(), scratch);
  } else {
    lir = allocateVariadic<LIonToWasmCall>(ins->numOperands(), scratch);
  }
  if (!lir) {
    abort(AbortReason::Alloc, "OOM: LIRGenerator::visitIonToWasmCall");
    return;
  }

  ABIArgGenerator abi;
  for (unsigned i = 0; i < ins->numOperands(); i++) {
    MDefinition* argDef = ins->getOperand(i);
    ABIArg arg = abi.next(ToMIRType(argDef->type()));
    switch (arg.kind()) {
      case ABIArg::GPR:
      case ABIArg::FPU:
        lir->setOperand(i, useFixedAtStart(argDef, arg.reg()));
        break;
      case ABIArg::Stack:
        lir->setOperand(i, useAtStart(argDef));
        break;
#ifdef JS_CODEGEN_REGISTER_PAIR
      case ABIArg::GPR_PAIR:
        MOZ_CRASH(
            "no way to pass i64, and wasm uses hardfp for function calls");
#endif
      case ABIArg::Uninitialized:
        MOZ_CRASH("Uninitialized ABIArg kind");
    }
  }

  defineReturn(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
  MDefinition* condExpr = ins->condExpr();

  // Pick off specific cases that we can do with LWasmCompareAndSelect to avoid
  // generating a boolean that we then have to test again.
  if (condExpr->isCompare() && condExpr->isEmittedAtUses()) {
    MCompare* comp = condExpr->toCompare();
    MCompare::CompareType compTy = comp->compareType();
    if (canSpecializeWasmCompareAndSelect(compTy, ins->type())) {
      JSOp jsop = comp->jsop();
      // We don't currently generate any other JSOPs for the comparison, and if
      // that changes, we want to know about it.  Hence this assertion.
      MOZ_ASSERT(jsop == JSOp::Eq || jsop == JSOp::Ne || jsop == JSOp::Lt ||
                 jsop == JSOp::Gt || jsop == JSOp::Le || jsop == JSOp::Ge);
      MDefinition* lhs = comp->lhs();
      MDefinition* rhs = comp->rhs();
      jsop = ReorderComparison(jsop, &lhs, &rhs);
      lowerWasmCompareAndSelect(ins, lhs, rhs, compTy, jsop);
      return;
    }
  }
  // Fall through to code that generates a boolean and selects on that.

  if (ins->type() == MIRType::Int64) {
    lowerWasmSelectI64(ins);
    return;
  }

  lowerWasmSelectI(ins);
}

void LIRGenerator::visitWasmFence(MWasmFence* ins) {
  add(new (alloc()) LWasmFence, ins);
}

void LIRGenerator::visitWasmLoadField(MWasmLoadField* ins) {
  uint32_t offs = ins->offset();
  LAllocation obj = useRegister(ins->obj());
  MWideningOp wideningOp = ins->wideningOp();
  if (ins->type() == MIRType::Int64) {
    MOZ_RELEASE_ASSERT(wideningOp == MWideningOp::None);
    defineInt64(new (alloc()) LWasmLoadSlotI64(obj, offs, ins->maybeTrap()),
                ins);
  } else {
    define(new (alloc()) LWasmLoadSlot(obj, offs, ins->type(), wideningOp,
                                       ins->maybeTrap()),
           ins);
  }
}

void LIRGenerator::visitWasmLoadFieldKA(MWasmLoadFieldKA* ins) {
  uint32_t offs = ins->offset();
  LAllocation obj = useRegister(ins->obj());
  MWideningOp wideningOp = ins->wideningOp();
  if (ins->type() == MIRType::Int64) {
    MOZ_RELEASE_ASSERT(wideningOp == MWideningOp::None);
    defineInt64(new (alloc()) LWasmLoadSlotI64(obj, offs, ins->maybeTrap()),
                ins);
  } else {
    define(new (alloc()) LWasmLoadSlot(obj, offs, ins->type(), wideningOp,
                                       ins->maybeTrap()),
           ins);
  }
  add(new (alloc()) LKeepAliveObject(useKeepalive(ins->ka())), ins);
}

void LIRGenerator::visitWasmLoadElementKA(MWasmLoadElementKA* ins) {
  LAllocation base = useRegister(ins->base());
  LAllocation index = useRegister(ins->index());
  MWideningOp wideningOp = ins->wideningOp();
  Scale scale = ins->scale();
  if (ins->type() == MIRType::Int64) {
    MOZ_RELEASE_ASSERT(wideningOp == MWideningOp::None);
    defineInt64(
        new (alloc()) LWasmLoadElementI64(base, index, ins->maybeTrap()), ins);
  } else {
    LDefinition tmp =
        ins->type() == MIRType::Simd128 ? temp() : LDefinition::BogusTemp();
    define(new (alloc()) LWasmLoadElement(base, index, tmp, ins->type(),
                                          wideningOp, scale, ins->maybeTrap()),
           ins);
  }
  add(new (alloc()) LKeepAliveObject(useKeepalive(ins->ka())), ins);
}

void LIRGenerator::visitWasmStoreFieldKA(MWasmStoreFieldKA* ins) {
  MDefinition* value = ins->value();
  uint32_t offs = ins->offset();
  MNarrowingOp narrowingOp = ins->narrowingOp();
  LAllocation obj = useRegister(ins->obj());
  LInstruction* lir;
  if (value->type() == MIRType::Int64) {
    MOZ_RELEASE_ASSERT(narrowingOp == MNarrowingOp::None);
    lir = new (alloc())
        LWasmStoreSlotI64(useInt64Register(value), obj, offs, ins->maybeTrap());
  } else {
    lir = new (alloc())
        LWasmStoreSlot(useRegister(value), obj, offs, value->type(),
                       narrowingOp, ins->maybeTrap());
  }
  add(lir, ins);
  add(new (alloc()) LKeepAliveObject(useKeepalive(ins->ka())), ins);
}

void LIRGenerator::visitWasmStoreFieldRefKA(MWasmStoreFieldRefKA* ins) {
  LAllocation instance = useRegister(ins->instance());
  LAllocation obj = useFixed(ins->obj(), PreBarrierReg);
  LAllocation value = useRegister(ins->value());
  uint32_t offset = ins->offset();
  add(new (alloc()) LWasmStoreRef(instance, obj, value, temp(), offset,
                                  ins->maybeTrap(), ins->preBarrierKind()),
      ins);
  add(new (alloc()) LKeepAliveObject(useKeepalive(ins->ka())), ins);
}

void LIRGenerator::visitWasmStoreElementKA(MWasmStoreElementKA* ins) {
  LAllocation base = useRegister(ins->base());
  LAllocation index = useRegister(ins->index());
  MDefinition* value = ins->value();
  MNarrowingOp narrowingOp = ins->narrowingOp();
  Scale scale = ins->scale();
  LInstruction* lir;
  if (value->type() == MIRType::Int64) {
    MOZ_RELEASE_ASSERT(narrowingOp == MNarrowingOp::None);
    lir = new (alloc()) LWasmStoreElementI64(
        base, index, useInt64Register(value), ins->maybeTrap());
  } else {
    LDefinition tmp =
        value->type() == MIRType::Simd128 ? temp() : LDefinition::BogusTemp();
    lir = new (alloc())
        LWasmStoreElement(base, index, useRegister(value), tmp, value->type(),
                          narrowingOp, scale, ins->maybeTrap());
  }
  add(lir, ins);
  add(new (alloc()) LKeepAliveObject(useKeepalive(ins->ka())), ins);
}

void LIRGenerator::visitWasmStoreElementRefKA(MWasmStoreElementRefKA* ins) {
  LAllocation instance = useRegister(ins->instance());
  LAllocation base = useFixed(ins->base(), PreBarrierReg);
  LAllocation index = useRegister(ins->index());
  LAllocation value = useRegister(ins->value());
  bool needTemps = ins->preBarrierKind() == WasmPreBarrierKind::Normal;
  LDefinition temp0 = needTemps ? temp() : LDefinition::BogusTemp();
  LDefinition temp1 = needTemps ? temp() : LDefinition::BogusTemp();
  add(new (alloc())
          LWasmStoreElementRef(instance, base, index, value, temp0, temp1,
                               ins->maybeTrap(), ins->preBarrierKind()),
      ins);
  add(new (alloc()) LKeepAliveObject(useKeepalive(ins->ka())), ins);
}

WasmRefIsSubtypeDefs LIRGenerator::useWasmRefIsSubtype(wasm::RefType destType,
                                                       MDefinition* superSTV) {
  BranchWasmRefIsSubtypeRegisters needs =
      MacroAssembler::regsForBranchWasmRefIsSubtype(destType);
  return WasmRefIsSubtypeDefs{
      .superSTV = needs.needSuperSTV ? useRegister(superSTV) : LAllocation(),
      .scratch1 = needs.needScratch1 ? temp() : LDefinition(),
      .scratch2 = needs.needScratch2 ? temp() : LDefinition(),
  };
}

void LIRGenerator::visitWasmRefIsSubtypeOfAbstract(
    MWasmRefIsSubtypeOfAbstract* ins) {
  if (CanEmitAtUseForSingleTest(ins)) {
    emitAtUses(ins);
    return;
  }

  LAllocation ref = useRegister(ins->ref());
  WasmRefIsSubtypeDefs regs =
      useWasmRefIsSubtype(ins->destType(), /*superSTV=*/nullptr);
  define(new (alloc()) LWasmRefIsSubtypeOfAbstract(ref, regs.scratch1), ins);
}

void LIRGenerator::visitWasmRefIsSubtypeOfConcrete(
    MWasmRefIsSubtypeOfConcrete* ins) {
  if (CanEmitAtUseForSingleTest(ins)) {
    emitAtUses(ins);
    return;
  }

  LAllocation ref = useRegister(ins->ref());
  WasmRefIsSubtypeDefs regs =
      useWasmRefIsSubtype(ins->destType(), ins->superSTV());
  define(new (alloc()) LWasmRefIsSubtypeOfConcrete(
             ref, regs.superSTV, regs.scratch1, regs.scratch2),
         ins);
}

void LIRGenerator::visitWasmNewStructObject(MWasmNewStructObject* ins) {
  LWasmNewStructObject* lir = new (alloc())
      LWasmNewStructObject(useFixed(ins->instance(), InstanceReg),
                           useRegister(ins->typeDefData()), temp(), temp());
  define(lir, ins);
  assignWasmSafepoint(lir);
}

void LIRGenerator::visitWasmNewArrayObject(MWasmNewArrayObject* ins) {
  LWasmNewArrayObject* lir = new (alloc())
      LWasmNewArrayObject(useFixed(ins->instance(), InstanceReg),
                          useRegisterOrConstant(ins->numElements()),
                          useRegister(ins->typeDefData()), temp(), temp());
  define(lir, ins);
  assignWasmSafepoint(lir);
}

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
void LIRGenerator::visitAddDisposableResource(MAddDisposableResource* ins) {
  MDefinition* env = ins->environment();

  MOZ_ASSERT(env->type() == MIRType::Object);

  MDefinition* resource = ins->resource();
  MDefinition* method = ins->method();
  MDefinition* needsClosure = ins->needsClosure();

  uint8_t hint = ins->hint();

  auto* lir = new (alloc()) LAddDisposableResource(
      useRegisterAtStart(env), useBoxAtStart(resource), useBoxAtStart(method),
      useRegisterAtStart(needsClosure), hint);
  add(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitTakeDisposeCapability(MTakeDisposeCapability* ins) {
  MDefinition* env = ins->environment();

  LTakeDisposeCapability* lir =
      new (alloc()) LTakeDisposeCapability(useRegister(env));
  defineBox(lir, ins);
  assignSafepoint(lir, ins);
}

void LIRGenerator::visitCreateSuppressedError(MCreateSuppressedError* ins) {
  MDefinition* error = ins->error();
  MDefinition* suppressed = ins->suppressed();

  LCreateSuppressedError* lir = new (alloc())
      LCreateSuppressedError(useBoxAtStart(error), useBoxAtStart(suppressed));
  define(lir, ins);
  assignSafepoint(lir, ins);
}
#endif

#ifdef FUZZING_JS_FUZZILLI
void LIRGenerator::visitFuzzilliHash(MFuzzilliHash* ins) {
  MDefinition* value = ins->input();

  switch (value->type()) {
    case MIRType::Undefined:
    case MIRType::Null: {
      auto* lir =
          new (alloc()) LFuzzilliHashT(LAllocation(), temp(), tempDouble());
      define(lir, ins);
      break;
    }

    case MIRType::Int32:
    case MIRType::Double:
    case MIRType::Float32:
    case MIRType::Boolean: {
      auto* lir = new (alloc())
          LFuzzilliHashT(useRegister(value), temp(), tempDouble());
      define(lir, ins);
      break;
    }

    case MIRType::BigInt:
    case MIRType::Object: {
      auto* lir = new (alloc())
          LFuzzilliHashT(useRegister(value), LDefinition::BogusTemp(),
                         LDefinition::BogusTemp());
      define(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }

    case MIRType::Value: {
      auto* lir =
          new (alloc()) LFuzzilliHashV(useBox(value), temp(), tempDouble());
      define(lir, ins);
      assignSafepoint(lir, ins);
      break;
    }

    case MIRType::String:
    case MIRType::Symbol: {
      define(new (alloc()) LInteger(0), ins);
      break;
    }

    default:
      MOZ_CRASH("Bad type");
  }
}

void LIRGenerator::visitFuzzilliHashStore(MFuzzilliHashStore* ins) {
  MDefinition* value = ins->input();
  MOZ_ASSERT(value->type() == MIRType::Int32);
  add(new (alloc()) LFuzzilliHashStore(useRegister(value), temp(), temp()),
      ins);
}
#endif

static_assert(!std::is_polymorphic_v<LIRGenerator>,
              "LIRGenerator should not have any virtual methods");

#ifdef JS_CODEGEN_NONE
void LIRGenerator::visitReturnImpl(MDefinition*, bool) { MOZ_CRASH(); }
#endif

Messung V0.5 in Prozent
C=97 H=99 G=97

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.207Bemerkung:  (vorverarbeitet am  2026-04-26) ¤

*Bot Zugriff






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators

     Produkte
     Quellcodebibliothek

letze Version des Agenda Kalenders

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

letze Version der Autor Authoringsoftware

     letze Version des Demonstrationsprogramms Goedel
     letze Version des Bille Abgleichprogramms
     Bilder

Jenseits des Üblichen ....
    

Besucher

Besucher

Monitoring

Montastic status badge