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


Quelle  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:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=98 G=94

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© Formatika GbR, Deutschland






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

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge