Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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) {
LTableSwitchV* lir = newLTableSwitchV(tableswitch);
add(lir);
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(LDefinition::GENERAL);
}
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);