diff options
Diffstat (limited to 'js/src/jit/Lowering.cpp')
-rw-r--r-- | js/src/jit/Lowering.cpp | 4930 |
1 files changed, 4930 insertions, 0 deletions
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp new file mode 100644 index 000000000..13e50820e --- /dev/null +++ b/js/src/jit/Lowering.cpp @@ -0,0 +1,4930 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "jit/JitSpewer.h" +#include "jit/LIR.h" +#include "jit/MIR.h" +#include "jit/MIRGraph.h" +#include "wasm/WasmSignalHandlers.h" + +#include "jsobjinlines.h" +#include "jsopcodeinlines.h" + +#include "jit/shared/Lowering-shared-inl.h" + +using namespace js; +using namespace jit; + +using mozilla::DebugOnly; +using JS::GenericNaN; + +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::visitCloneLiteral(MCloneLiteral* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Object); + MOZ_ASSERT(ins->input()->type() == MIRType::Object); + + LCloneLiteral* lir = new(alloc()) LCloneLiteral(useRegisterAtStart(ins->input())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +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); +} + +static void +TryToUseImplicitInterruptCheck(MIRGraph& graph, MBasicBlock* backedge) +{ + // Implicit interrupt checks require wasm signal handlers to be installed. + if (!wasm::HaveSignalHandlers() || JitOptions.ionInterruptWithoutSignals) + return; + + // To avoid triggering expensive interrupts (backedge patching) in + // requestMajorGC and requestMinorGC, use an implicit interrupt check only + // if the loop body can not trigger GC or affect GC state like the store + // buffer. We do this by checking there are no safepoints attached to LIR + // instructions inside the loop. + + MBasicBlockIterator block = graph.begin(backedge->loopHeaderOfBackedge()); + LInterruptCheck* check = nullptr; + while (true) { + LBlock* lir = block->lir(); + for (LInstructionIterator iter = lir->begin(); iter != lir->end(); iter++) { + if (iter->isInterruptCheck()) { + if (!check) { + MOZ_ASSERT(*block == backedge->loopHeaderOfBackedge()); + check = iter->toInterruptCheck(); + } + continue; + } + + MOZ_ASSERT_IF(iter->isPostWriteBarrierO() || iter->isPostWriteBarrierV(), + iter->safepoint()); + + if (iter->safepoint()) + return; + } + if (*block == backedge) + break; + block++; + } + + check->setImplicit(); +} + +void +LIRGenerator::visitGoto(MGoto* ins) +{ + if (!gen->compilingWasm() && ins->block()->isLoopBackedge()) + TryToUseImplicitInterruptCheck(graph, ins->block()); + + 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::visitDefVar(MDefVar* ins) +{ + LDefVar* lir = new(alloc()) LDefVar(useRegisterAtStart(ins->environmentChain())); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitDefLexical(MDefLexical* ins) +{ + LDefLexical* lir = new(alloc()) LDefLexical(); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitDefFun(MDefFun* ins) +{ + MDefinition* fun = ins->fun(); + MOZ_ASSERT(fun->type() == MIRType::Object); + + LDefFun* lir = new(alloc()) LDefFun(useRegisterAtStart(fun), + useRegisterAtStart(ins->environmentChain())); + 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::visitNewArrayCopyOnWrite(MNewArrayCopyOnWrite* ins) +{ + LNewArrayCopyOnWrite* lir = new(alloc()) LNewArrayCopyOnWrite(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::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::visitNewObject(MNewObject* ins) +{ + LNewObject* lir = new(alloc()) LNewObject(temp()); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitNewTypedObject(MNewTypedObject* ins) +{ + LNewTypedObject* lir = new(alloc()) LNewTypedObject(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::visitNewSingletonCallObject(MNewSingletonCallObject* ins) +{ + LNewSingletonCallObject* lir = new(alloc()) LNewSingletonCallObject(temp()); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitNewDerivedTypedObject(MNewDerivedTypedObject* ins) +{ + LNewDerivedTypedObject* lir = + new(alloc()) LNewDerivedTypedObject(useRegisterAtStart(ins->type()), + useRegisterAtStart(ins->owner()), + useRegisterAtStart(ins->offset())); + 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::visitInitElem(MInitElem* ins) +{ + LInitElem* lir = new(alloc()) LInitElem(useRegisterAtStart(ins->getObject()), + useBoxAtStart(ins->getId()), + useBoxAtStart(ins->getValue())); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitInitElemGetterSetter(MInitElemGetterSetter* ins) +{ + LInitElemGetterSetter* lir = + new(alloc()) LInitElemGetterSetter(useRegisterAtStart(ins->object()), + useBoxAtStart(ins->idValue()), + useRegisterAtStart(ins->value())); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitMutateProto(MMutateProto* ins) +{ + LMutateProto* lir = new(alloc()) LMutateProto(useRegisterAtStart(ins->getObject()), + useBoxAtStart(ins->getValue())); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitInitProp(MInitProp* ins) +{ + LInitProp* lir = new(alloc()) LInitProp(useRegisterAtStart(ins->getObject()), + useBoxAtStart(ins->getValue())); + 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::visitCreateThisWithTemplate(MCreateThisWithTemplate* ins) +{ + LCreateThisWithTemplate* lir = new(alloc()) LCreateThisWithTemplate(temp()); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCreateThisWithProto(MCreateThisWithProto* ins) +{ + LCreateThisWithProto* lir = + new(alloc()) LCreateThisWithProto(useRegisterOrConstantAtStart(ins->getCallee()), + useRegisterOrConstantAtStart(ins->getNewTarget()), + useRegisterOrConstantAtStart(ins->getPrototype())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCreateThis(MCreateThis* ins) +{ + LCreateThis* lir = new(alloc()) LCreateThis(useRegisterOrConstantAtStart(ins->getCallee()), + useRegisterOrConstantAtStart(ins->getNewTarget())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCreateArgumentsObject(MCreateArgumentsObject* ins) +{ + LAllocation callObj = useFixedAtStart(ins->getCallObject(), CallTempReg0); + LCreateArgumentsObject* lir = new(alloc()) LCreateArgumentsObject(callObj, tempFixed(CallTempReg1), + tempFixed(CallTempReg2), + tempFixed(CallTempReg3)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitGetArgumentsObjectArg(MGetArgumentsObjectArg* ins) +{ + LAllocation argsObj = useRegister(ins->getArgsObject()); + LGetArgumentsObjectArg* lir = new(alloc()) LGetArgumentsObjectArg(argsObj, temp()); + defineBox(lir, ins); +} + +void +LIRGenerator::visitSetArgumentsObjectArg(MSetArgumentsObjectArg* ins) +{ + LAllocation argsObj = useRegister(ins->getArgsObject()); + LSetArgumentsObjectArg* lir = + new(alloc()) LSetArgumentsObjectArg(argsObj, useBox(ins->getValue()), temp()); + add(lir, ins); +} + +void +LIRGenerator::visitReturnFromCtor(MReturnFromCtor* ins) +{ + LReturnFromCtor* lir = new(alloc()) LReturnFromCtor(useBox(ins->getValue()), + useRegister(ins->getObject())); + define(lir, ins); +} + +void +LIRGenerator::visitComputeThis(MComputeThis* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Value); + MOZ_ASSERT(ins->input()->type() == MIRType::Value); + + // Don't use useBoxAtStart because ComputeThis has a safepoint and needs to + // have its inputs in different registers than its return value so that + // they aren't clobbered. + LComputeThis* lir = new(alloc()) LComputeThis(useBox(ins->input())); + defineBox(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitArrowNewTarget(MArrowNewTarget* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Value); + MOZ_ASSERT(ins->callee()->type() == MIRType::Object); + + LArrowNewTarget* lir = new(alloc()) LArrowNewTarget(useRegister(ins->callee())); + defineBox(lir, ins); +} + +bool +LIRGenerator::lowerCallArguments(MCall* 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(argslot, useBox(arg)); + add(stack); + } else { + // Known types can move constant types and/or payloads. + LStackArgT* stack = new(alloc()) LStackArgT(argslot, arg->type(), useRegisterOrConstant(arg)); + add(stack); + } + + if (!alloc().ensureBallast()) + return false; + } + return true; +} + +void +LIRGenerator::visitCall(MCall* call) +{ + MOZ_ASSERT(CallTempReg0 != CallTempReg1); + MOZ_ASSERT(CallTempReg0 != ArgumentsRectifierReg); + MOZ_ASSERT(CallTempReg1 != ArgumentsRectifierReg); + MOZ_ASSERT(call->getFunction()->type() == MIRType::Object); + + // In case of oom, skip the rest of the allocations. + if (!lowerCallArguments(call)) { + gen->abort("OOM: LIRGenerator::visitCall"); + return; + } + + WrappedFunction* target = call->getSingleTarget(); + + LInstruction* lir; + + if (call->isCallDOMNative()) { + // Call DOM functions. + MOZ_ASSERT(target && target->isNative()); + 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->isNative()) { + 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(useFixedAtStart(call->getFunction(), CallTempReg0), + tempFixed(CallTempReg2)); + } + } else { + // Call anything, using the most generic code. + lir = new(alloc()) LCallGeneric(useFixedAtStart(call->getFunction(), CallTempReg0), + tempFixed(ArgumentsRectifierReg), + tempFixed(CallTempReg2)); + } + defineReturn(lir, call); + assignSafepoint(lir, call); +} + +void +LIRGenerator::visitApplyArgs(MApplyArgs* apply) +{ + MOZ_ASSERT(apply->getFunction()->type() == MIRType::Object); + + // Assert if we cannot build a rectifier frame. + MOZ_ASSERT(CallTempReg0 != ArgumentsRectifierReg); + MOZ_ASSERT(CallTempReg1 != ArgumentsRectifierReg); + + // Assert if the return value is already erased. + MOZ_ASSERT(CallTempReg2 != JSReturnReg_Type); + MOZ_ASSERT(CallTempReg2 != JSReturnReg_Data); + + LApplyArgsGeneric* lir = new(alloc()) LApplyArgsGeneric( + useFixedAtStart(apply->getFunction(), CallTempReg3), + useFixedAtStart(apply->getArgc(), CallTempReg0), + useBoxFixedAtStart(apply->getThis(), CallTempReg4, CallTempReg5), + tempFixed(CallTempReg1), // object register + tempFixed(CallTempReg2)); // stack counter register + + // Bailout is needed in the case of possible non-JSFunction callee or too + // many values in the arguments array. I'm going to use NonJSFunctionCallee + // for the code even if that is not an adequate description. + assignSnapshot(lir, Bailout_NonJSFunctionCallee); + + defineReturn(lir, apply); + assignSafepoint(lir, apply); +} + +void +LIRGenerator::visitApplyArray(MApplyArray* apply) +{ + MOZ_ASSERT(apply->getFunction()->type() == MIRType::Object); + + // Assert if we cannot build a rectifier frame. + MOZ_ASSERT(CallTempReg0 != ArgumentsRectifierReg); + MOZ_ASSERT(CallTempReg1 != ArgumentsRectifierReg); + + // Assert if the return value is already erased. + MOZ_ASSERT(CallTempReg2 != JSReturnReg_Type); + MOZ_ASSERT(CallTempReg2 != JSReturnReg_Data); + + LApplyArrayGeneric* lir = new(alloc()) LApplyArrayGeneric( + useFixedAtStart(apply->getFunction(), CallTempReg3), + useFixedAtStart(apply->getElements(), CallTempReg0), + useBoxFixedAtStart(apply->getThis(), CallTempReg4, CallTempReg5), + tempFixed(CallTempReg1), // object register + tempFixed(CallTempReg2)); // stack counter register + + // Bailout is needed in the case of possible non-JSFunction callee, + // too many values in the array, or empty space at the end of the + // array. I'm going to use NonJSFunctionCallee for the code even + // if that is not an adequate description. + assignSnapshot(lir, Bailout_NonJSFunctionCallee); + + defineReturn(lir, apply); + assignSafepoint(lir, apply); +} + +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, Bailout_Inevitable); + add(lir, mir); +} + +void +LIRGenerator::visitAssertFloat32(MAssertFloat32* assertion) +{ + MIRType type = assertion->input()->type(); + DebugOnly<bool> checkIsFloat32 = assertion->mustBeFloat32(); + + if (type != MIRType::Value && !JitOptions.eagerCompilation) { + 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."); +} + +void +LIRGenerator::visitArraySplice(MArraySplice* ins) +{ + LArraySplice* lir = new(alloc()) LArraySplice(useRegisterAtStart(ins->object()), + useRegisterAtStart(ins->start()), + useRegisterAtStart(ins->deleteCount())); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitGetDynamicName(MGetDynamicName* ins) +{ + MDefinition* envChain = ins->getEnvironmentChain(); + MOZ_ASSERT(envChain->type() == MIRType::Object); + + MDefinition* name = ins->getName(); + MOZ_ASSERT(name->type() == MIRType::String); + + LGetDynamicName* lir = new(alloc()) LGetDynamicName(useFixedAtStart(envChain, CallTempReg0), + useFixedAtStart(name, CallTempReg1), + tempFixed(CallTempReg2), + tempFixed(CallTempReg3), + tempFixed(CallTempReg4)); + + assignSnapshot(lir, Bailout_DynamicNameNotFound); + defineReturn(lir, ins); +} + +void +LIRGenerator::visitCallDirectEval(MCallDirectEval* ins) +{ + MDefinition* envChain = ins->getEnvironmentChain(); + MOZ_ASSERT(envChain->type() == MIRType::Object); + + MDefinition* string = ins->getString(); + MOZ_ASSERT(string->type() == MIRType::String); + + MDefinition* newTargetValue = ins->getNewTargetValue(); + + LInstruction* lir = new(alloc()) LCallDirectEval(useRegisterAtStart(envChain), + useRegisterAtStart(string), + useBoxAtStart(newTargetValue)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +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) { + LDefinition temp0, temp1; + if (test->operandMightEmulateUndefined()) { + temp0 = temp(); + temp1 = temp(); + } else { + temp0 = LDefinition::BogusTemp(); + temp1 = LDefinition::BogusTemp(); + } + LTestVAndBranch* lir = + new(alloc()) LTestVAndBranch(ifTrue, ifFalse, useBox(opd), tempDouble(), temp0, temp1); + add(lir, test); + return; + } + + if (opd->type() == MIRType::ObjectOrNull) { + LDefinition temp0 = test->operandMightEmulateUndefined() ? temp() : LDefinition::BogusTemp(); + add(new(alloc()) LTestOAndBranch(useRegister(opd), ifTrue, ifFalse, temp0), test); + return; + } + + // Objects are truthy, except if it might emulate undefined. + if (opd->type() == MIRType::Object) { + if (test->operandMightEmulateUndefined()) + add(new(alloc()) LTestOAndBranch(useRegister(opd), ifTrue, ifFalse, temp()), test); + else + add(new(alloc()) LGoto(ifTrue)); + 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; + } + + // 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 || left->type() == MIRType::ObjectOrNull) { + MOZ_ASSERT(left->type() == MIRType::ObjectOrNull || + comp->operandMightEmulateUndefined(), + "MCompare::tryFold should handle the never-emulates-undefined case"); + + LDefinition tmp = + comp->operandMightEmulateUndefined() ? temp() : LDefinition::BogusTemp(); + LIsNullOrLikeUndefinedAndBranchT* lir = + new(alloc()) LIsNullOrLikeUndefinedAndBranchT(comp, useRegister(left), + ifTrue, ifFalse, tmp); + add(lir, test); + return; + } + + LDefinition tmp, tmpToUnbox; + if (comp->operandMightEmulateUndefined()) { + tmp = temp(); + tmpToUnbox = tempToUnbox(); + } else { + tmp = LDefinition::BogusTemp(); + tmpToUnbox = LDefinition::BogusTemp(); + } + + LIsNullOrLikeUndefinedAndBranchV* lir = + new(alloc()) LIsNullOrLikeUndefinedAndBranchV(comp, ifTrue, ifFalse, useBox(left), + tmp, tmpToUnbox); + add(lir, test); + return; + } + + // Compare and branch booleans. + if (comp->compareType() == MCompare::Compare_Boolean) { + MOZ_ASSERT(left->type() == MIRType::Value); + MOZ_ASSERT(right->type() == MIRType::Boolean); + + LCompareBAndBranch* lir = new(alloc()) LCompareBAndBranch(comp, useBox(left), + useRegisterOrConstant(right), + ifTrue, ifFalse); + add(lir, test); + return; + } + + // Compare and branch Int32 or Object pointers. + if (comp->isInt32Comparison() || + comp->compareType() == MCompare::Compare_UInt32 || + comp->compareType() == MCompare::Compare_Object) + { + JSOp op = ReorderComparison(comp->jsop(), &left, &right); + LAllocation lhs = useRegister(left); + LAllocation rhs; + if (comp->isInt32Comparison() || comp->compareType() == MCompare::Compare_UInt32) + rhs = useAnyOrConstant(right); + else + rhs = useRegister(right); + LCompareAndBranch* lir = new(alloc()) LCompareAndBranch(comp, op, lhs, rhs, + ifTrue, ifFalse); + 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); + LCompareI64AndBranch* lir = new(alloc()) LCompareI64AndBranch(comp, op, + useInt64Register(left), + useInt64OrConstant(right), + ifTrue, ifFalse); + add(lir, test); + return; + } + + // Compare and branch doubles. + if (comp->isDoubleComparison()) { + LAllocation lhs = useRegister(left); + LAllocation rhs = useRegister(right); + LCompareDAndBranch* lir = new(alloc()) LCompareDAndBranch(comp, lhs, rhs, + ifTrue, ifFalse); + add(lir, test); + return; + } + + // Compare and branch floats. + if (comp->isFloat32Comparison()) { + LAllocation lhs = useRegister(left); + LAllocation rhs = useRegister(right); + LCompareFAndBranch* lir = new(alloc()) LCompareFAndBranch(comp, lhs, rhs, + ifTrue, ifFalse); + add(lir, test); + return; + } + + // Compare values. + if (comp->compareType() == MCompare::Compare_Bitwise) { + LCompareBitwiseAndBranch* lir = + new(alloc()) LCompareBitwiseAndBranch(comp, ifTrue, ifFalse, + useBoxAtStart(left), + useBoxAtStart(right)); + 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()) { + MDefinition* lhs = opd->getOperand(0); + MDefinition* rhs = opd->getOperand(1); + if (lhs->type() == MIRType::Int32 && rhs->type() == MIRType::Int32) { + ReorderCommutative(&lhs, &rhs, test); + lowerForBitAndAndBranch(new(alloc()) LBitAndAndBranch(ifTrue, ifFalse), test, lhs, rhs); + return; + } + } + + 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->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; + } + + switch (opd->type()) { + case MIRType::Double: + add(new(alloc()) LTestDAndBranch(useRegister(opd), ifTrue, ifFalse)); + break; + case MIRType::Float32: + add(new(alloc()) LTestFAndBranch(useRegister(opd), ifTrue, ifFalse)); + break; + case MIRType::Int32: + case MIRType::Boolean: + add(new(alloc()) LTestIAndBranch(useRegister(opd), ifTrue, ifFalse)); + break; + case MIRType::Int64: + add(new(alloc()) LTestI64AndBranch(useInt64Register(opd), ifTrue, ifFalse)); + break; + default: + MOZ_CRASH("Bad type"); + } +} + +void +LIRGenerator::visitGotoWithFake(MGotoWithFake* gotoWithFake) +{ + add(new(alloc()) LGoto(gotoWithFake->target())); +} + +void +LIRGenerator::visitFunctionDispatch(MFunctionDispatch* ins) +{ + LFunctionDispatch* lir = new(alloc()) LFunctionDispatch(useRegister(ins->input())); + add(lir, ins); +} + +void +LIRGenerator::visitObjectGroupDispatch(MObjectGroupDispatch* ins) +{ + LObjectGroupDispatch* lir = new(alloc()) LObjectGroupDispatch(useRegister(ins->input()), temp()); + add(lir, ins); +} + +static inline bool +CanEmitCompareAtUses(MInstruction* ins) +{ + if (!ins->canEmitAtUses()) + return false; + + bool foundTest = false; + for (MUseIterator iter(ins->usesBegin()); iter != ins->usesEnd(); iter++) { + MNode* node = iter->consumer(); + if (!node->isDefinition()) + return false; + if (!node->toDefinition()->isTest()) + return false; + if (foundTest) + return false; + foundTest = true; + } + return true; +} + +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) { + LCompareS* lir = new(alloc()) LCompareS(useRegister(left), useRegister(right)); + define(lir, comp); + assignSafepoint(lir, comp); + return; + } + + // Strict compare between value and string + if (comp->compareType() == MCompare::Compare_StrictString) { + MOZ_ASSERT(left->type() == MIRType::Value); + MOZ_ASSERT(right->type() == MIRType::String); + + LCompareStrictS* lir = new(alloc()) LCompareStrictS(useBox(left), useRegister(right), + tempToUnbox()); + define(lir, comp); + assignSafepoint(lir, comp); + return; + } + + // Unknown/unspecialized compare use a VM call. + if (comp->compareType() == MCompare::Compare_Unknown) { + LCompareVM* lir = new(alloc()) LCompareVM(useBoxAtStart(left), useBoxAtStart(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 || left->type() == MIRType::ObjectOrNull) { + MOZ_ASSERT(left->type() == MIRType::ObjectOrNull || + comp->operandMightEmulateUndefined(), + "MCompare::tryFold should have folded this away"); + + define(new(alloc()) LIsNullOrLikeUndefinedT(useRegister(left)), comp); + return; + } + + LDefinition tmp, tmpToUnbox; + if (comp->operandMightEmulateUndefined()) { + tmp = temp(); + tmpToUnbox = tempToUnbox(); + } else { + tmp = LDefinition::BogusTemp(); + tmpToUnbox = LDefinition::BogusTemp(); + } + + LIsNullOrLikeUndefinedV* lir = new(alloc()) LIsNullOrLikeUndefinedV(useBox(left), + tmp, tmpToUnbox); + define(lir, comp); + return; + } + + // Compare booleans. + if (comp->compareType() == MCompare::Compare_Boolean) { + MOZ_ASSERT(left->type() == MIRType::Value); + MOZ_ASSERT(right->type() == MIRType::Boolean); + + LCompareB* lir = new(alloc()) LCompareB(useBox(left), useRegisterOrConstant(right)); + define(lir, comp); + return; + } + + // Compare Int32 or Object pointers. + if (comp->isInt32Comparison() || + comp->compareType() == MCompare::Compare_UInt32 || + comp->compareType() == MCompare::Compare_Object) + { + JSOp op = ReorderComparison(comp->jsop(), &left, &right); + LAllocation lhs = useRegister(left); + LAllocation rhs; + if (comp->isInt32Comparison() || + comp->compareType() == MCompare::Compare_UInt32) + { + rhs = useAnyOrConstant(right); + } else { + rhs = useRegister(right); + } + define(new(alloc()) LCompare(op, lhs, rhs), 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(op, useInt64Register(left), useInt64OrConstant(right)), + 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 values. + if (comp->compareType() == MCompare::Compare_Bitwise) { + LCompareBitwise* lir = new(alloc()) LCompareBitwise(useBoxAtStart(left), + useBoxAtStart(right)); + define(lir, comp); + return; + } + + MOZ_CRASH("Unrecognized compare type."); +} + +void +LIRGenerator::lowerBitOp(JSOp op, MInstruction* ins) +{ + MDefinition* lhs = ins->getOperand(0); + MDefinition* rhs = ins->getOperand(1); + + if (lhs->type() == MIRType::Int32) { + MOZ_ASSERT(rhs->type() == MIRType::Int32); + ReorderCommutative(&lhs, &rhs, ins); + lowerForALU(new(alloc()) LBitOpI(op), ins, lhs, rhs); + return; + } + + if (lhs->type() == MIRType::Int64) { + MOZ_ASSERT(rhs->type() == MIRType::Int64); + ReorderCommutative(&lhs, &rhs, ins); + lowerForALUInt64(new(alloc()) LBitOpI64(op), ins, lhs, rhs); + return; + } + + LBitOpV* lir = new(alloc()) LBitOpV(op, useBoxAtStart(lhs), useBoxAtStart(rhs)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitTypeOf(MTypeOf* ins) +{ + MDefinition* opd = ins->input(); + MOZ_ASSERT(opd->type() == MIRType::Value); + + LTypeOfV* lir = new(alloc()) LTypeOfV(useBox(opd), tempToUnbox()); + define(lir, ins); +} + +void +LIRGenerator::visitToAsync(MToAsync* ins) +{ + LToAsync* lir = new(alloc()) LToAsync(useRegisterAtStart(ins->input())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitToId(MToId* ins) +{ + LToIdV* lir = new(alloc()) LToIdV(useBox(ins->input()), tempDouble()); + defineBox(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitBitNot(MBitNot* ins) +{ + MDefinition* input = ins->getOperand(0); + + if (input->type() == MIRType::Int32) { + lowerForALU(new(alloc()) LBitNotI(), ins, input); + return; + } + + LBitNotV* lir = new(alloc()) LBitNotV(useBoxAtStart(input)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +static bool +CanEmitBitAndAtUses(MInstruction* ins) +{ + if (!ins->canEmitAtUses()) + return false; + + if (ins->getOperand(0)->type() != MIRType::Int32 || ins->getOperand(1)->type() != MIRType::Int32) + return false; + + MUseIterator iter(ins->usesBegin()); + if (iter == ins->usesEnd()) + return false; + + MNode* node = iter->consumer(); + if (!node->isDefinition()) + return false; + + if (!node->toDefinition()->isTest()) + return false; + + iter++; + return iter == ins->usesEnd(); +} + +void +LIRGenerator::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::lowerShiftOp(JSOp op, MShiftInstruction* ins) +{ + MDefinition* lhs = ins->getOperand(0); + MDefinition* rhs = ins->getOperand(1); + + if (lhs->type() == MIRType::Int32) { + MOZ_ASSERT(rhs->type() == MIRType::Int32); + + if (ins->type() == MIRType::Double) { + MOZ_ASSERT(op == JSOP_URSH); + lowerUrshD(ins->toUrsh()); + return; + } + + LShiftI* lir = new(alloc()) LShiftI(op); + if (op == JSOP_URSH) { + if (ins->toUrsh()->fallible()) + assignSnapshot(lir, Bailout_OverflowInvalidate); + } + lowerForShift(lir, ins, lhs, rhs); + return; + } + + if (lhs->type() == MIRType::Int64) { + MOZ_ASSERT(rhs->type() == MIRType::Int64); + lowerForShiftInt64(new(alloc()) LShiftI64(op), ins, lhs, rhs); + return; + } + + MOZ_ASSERT(ins->specialization() == MIRType::None); + + if (op == JSOP_URSH) { + // Result is either int32 or double so we have to use BinaryV. + lowerBinaryV(JSOP_URSH, ins); + return; + } + + LBitOpV* lir = new(alloc()) LBitOpV(op, useBoxAtStart(lhs), useBoxAtStart(rhs)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +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::visitSignExtend(MSignExtend* ins) +{ + LInstructionHelper<1, 1, 0>* lir; + + if (ins->mode() == MSignExtend::Byte) + lir = new(alloc()) LSignExtend(useByteOpRegisterAtStart(ins->input()), ins->mode()); + else + lir = new(alloc()) LSignExtend(useRegisterAtStart(ins->input()), ins->mode()); + + 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, Bailout_Round); + 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, Bailout_Round); + 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, Bailout_Round); + define(lir, ins); +} + +void +LIRGenerator::visitMinMax(MMinMax* ins) +{ + MDefinition* first = ins->getOperand(0); + MDefinition* second = ins->getOperand(1); + + ReorderCommutative(&first, &second, ins); + + LMinMaxBase* lir; + switch (ins->specialization()) { + 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(); + } + + defineReuseInput(lir, ins, 0); +} + +void +LIRGenerator::visitAbs(MAbs* ins) +{ + MDefinition* num = ins->input(); + MOZ_ASSERT(IsNumberType(num->type())); + + LInstructionHelper<1, 1, 0>* lir; + switch (num->type()) { + case MIRType::Int32: + lir = new(alloc()) LAbsI(useRegisterAtStart(num)); + // needed to handle abs(INT32_MIN) + if (ins->fallible()) + assignSnapshot(lir, Bailout_Overflow); + break; + case MIRType::Float32: + lir = new(alloc()) LAbsF(useRegisterAtStart(num)); + break; + case MIRType::Double: + lir = new(alloc()) LAbsD(useRegisterAtStart(num)); + break; + default: + MOZ_CRASH(); + } + + defineReuseInput(lir, ins, 0); +} + +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), + tempFixed(CallTempReg0)); + 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)), + tempFixed(CallTempReg0)); + break; + case 3: + lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)), + useRegisterAtStart(ins->getOperand(1)), + useRegisterAtStart(ins->getOperand(2)), + tempFixed(CallTempReg0)); + break; + case 4: + lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)), + useRegisterAtStart(ins->getOperand(1)), + useRegisterAtStart(ins->getOperand(2)), + useRegisterAtStart(ins->getOperand(3)), + tempFixed(CallTempReg0)); + break; + default: + MOZ_CRASH("Unexpected number of arguments to LHypot."); + } + + defineReturn(lir, ins); +} + +void +LIRGenerator::visitPow(MPow* ins) +{ + MDefinition* input = ins->input(); + MOZ_ASSERT(input->type() == MIRType::Double); + + MDefinition* power = ins->power(); + MOZ_ASSERT(power->type() == MIRType::Int32 || power->type() == MIRType::Double); + + LInstruction* lir; + if (power->type() == MIRType::Int32) { + // Note: useRegisterAtStart here is safe, the temp is a GP register so + // it will never get the same register. + lir = new(alloc()) LPowI(useRegisterAtStart(input), useFixedAtStart(power, CallTempReg1), + tempFixed(CallTempReg0)); + } else { + lir = new(alloc()) LPowD(useRegisterAtStart(input), useRegisterAtStart(power), + tempFixed(CallTempReg0)); + } + defineReturn(lir, ins); +} + +void +LIRGenerator::visitMathFunction(MMathFunction* ins) +{ + MOZ_ASSERT(IsFloatingPointType(ins->type())); + MOZ_ASSERT_IF(ins->input()->type() != MIRType::SinCosDouble, + ins->type() == ins->input()->type()); + + if (ins->input()->type() == MIRType::SinCosDouble) { + MOZ_ASSERT(ins->type() == MIRType::Double); + redefine(ins, ins->input(), ins->function()); + return; + } + + LInstruction* lir; + if (ins->type() == MIRType::Double) { + // Note: useRegisterAtStart is safe here, the temp is not a FP register. + lir = new(alloc()) LMathFunctionD(useRegisterAtStart(ins->input()), + tempFixed(CallTempReg0)); + } else { + lir = new(alloc()) LMathFunctionF(useRegisterAtStart(ins->input()), + tempFixed(CallTempReg0)); + } + defineReturn(lir, ins); +} + +// Try to mark an add or sub instruction as able to recover its input when +// bailing out. +template <typename S, typename T> +static void +MaybeSetRecoversInput(S* mir, T* lir) +{ + MOZ_ASSERT(lir->mirRaw() == mir); + if (!mir->fallible() || !lir->snapshot()) + return; + + if (lir->output()->policy() != LDefinition::MUST_REUSE_INPUT) + return; + + // The original operands to an add or sub can't be recovered if they both + // use the same register. + if (lir->lhs()->isUse() && lir->rhs()->isUse() && + lir->lhs()->toUse()->virtualRegister() == lir->rhs()->toUse()->virtualRegister()) + { + return; + } + + // Add instructions that are on two different values can recover + // the input they clobbered via MUST_REUSE_INPUT. Thus, a copy + // of that input does not need to be kept alive in the snapshot + // for the instruction. + + lir->setRecoversInput(); + + const LUse* input = lir->getOperand(lir->output()->getReusedInput())->toUse(); + lir->snapshot()->rewriteRecoveredInput(*input); +} + +void +LIRGenerator::visitAdd(MAdd* ins) +{ + MDefinition* lhs = ins->getOperand(0); + MDefinition* rhs = ins->getOperand(1); + + MOZ_ASSERT(lhs->type() == rhs->type()); + + if (ins->specialization() == MIRType::Int32) { + MOZ_ASSERT(lhs->type() == MIRType::Int32); + ReorderCommutative(&lhs, &rhs, ins); + LAddI* lir = new(alloc()) LAddI; + + if (ins->fallible()) + assignSnapshot(lir, Bailout_OverflowInvalidate); + + lowerForALU(lir, ins, lhs, rhs); + MaybeSetRecoversInput(ins, lir); + return; + } + + if (ins->specialization() == MIRType::Int64) { + MOZ_ASSERT(lhs->type() == MIRType::Int64); + ReorderCommutative(&lhs, &rhs, ins); + LAddI64* lir = new(alloc()) LAddI64; + lowerForALUInt64(lir, ins, lhs, rhs); + return; + } + + if (ins->specialization() == MIRType::Double) { + MOZ_ASSERT(lhs->type() == MIRType::Double); + ReorderCommutative(&lhs, &rhs, ins); + lowerForFPU(new(alloc()) LMathD(JSOP_ADD), ins, lhs, rhs); + return; + } + + if (ins->specialization() == MIRType::Float32) { + MOZ_ASSERT(lhs->type() == MIRType::Float32); + ReorderCommutative(&lhs, &rhs, ins); + lowerForFPU(new(alloc()) LMathF(JSOP_ADD), ins, lhs, rhs); + return; + } + + lowerBinaryV(JSOP_ADD, ins); +} + +void +LIRGenerator::visitSub(MSub* ins) +{ + MDefinition* lhs = ins->lhs(); + MDefinition* rhs = ins->rhs(); + + MOZ_ASSERT(lhs->type() == rhs->type()); + + if (ins->specialization() == MIRType::Int32) { + MOZ_ASSERT(lhs->type() == MIRType::Int32); + + LSubI* lir = new(alloc()) LSubI; + if (ins->fallible()) + assignSnapshot(lir, Bailout_Overflow); + + lowerForALU(lir, ins, lhs, rhs); + MaybeSetRecoversInput(ins, lir); + return; + } + + if (ins->specialization() == MIRType::Int64) { + MOZ_ASSERT(lhs->type() == MIRType::Int64); + LSubI64* lir = new(alloc()) LSubI64; + lowerForALUInt64(lir, ins, lhs, rhs); + return; + } + + if (ins->specialization() == MIRType::Double) { + MOZ_ASSERT(lhs->type() == MIRType::Double); + lowerForFPU(new(alloc()) LMathD(JSOP_SUB), ins, lhs, rhs); + return; + } + + if (ins->specialization() == MIRType::Float32) { + MOZ_ASSERT(lhs->type() == MIRType::Float32); + lowerForFPU(new(alloc()) LMathF(JSOP_SUB), ins, lhs, rhs); + return; + } + + lowerBinaryV(JSOP_SUB, ins); +} + +void +LIRGenerator::visitMul(MMul* ins) +{ + MDefinition* lhs = ins->lhs(); + MDefinition* rhs = ins->rhs(); + MOZ_ASSERT(lhs->type() == rhs->type()); + + if (ins->specialization() == MIRType::Int32) { + MOZ_ASSERT(lhs->type() == MIRType::Int32); + ReorderCommutative(&lhs, &rhs, ins); + + // If our RHS is a constant -1 and we don't have to worry about + // overflow, we can optimize to an LNegI. + if (!ins->fallible() && rhs->isConstant() && rhs->toConstant()->toInt32() == -1) + defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(lhs)), ins, 0); + else + lowerMulI(ins, lhs, rhs); + return; + } + + if (ins->specialization() == MIRType::Int64) { + MOZ_ASSERT(lhs->type() == MIRType::Int64); + ReorderCommutative(&lhs, &rhs, ins); + LMulI64* lir = new(alloc()) LMulI64; + lowerForMulInt64(lir, ins, lhs, rhs); + return; + } + + if (ins->specialization() == MIRType::Double) { + MOZ_ASSERT(lhs->type() == MIRType::Double); + ReorderCommutative(&lhs, &rhs, ins); + + // If our RHS is a constant -1.0, we can optimize to an LNegD. + if (!ins->mustPreserveNaN() && rhs->isConstant() && rhs->toConstant()->toDouble() == -1.0) + defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(lhs)), ins, 0); + else + lowerForFPU(new(alloc()) LMathD(JSOP_MUL), ins, lhs, rhs); + return; + } + + if (ins->specialization() == MIRType::Float32) { + MOZ_ASSERT(lhs->type() == MIRType::Float32); + ReorderCommutative(&lhs, &rhs, ins); + + // We apply the same optimizations as for doubles + if (!ins->mustPreserveNaN() && rhs->isConstant() && rhs->toConstant()->toFloat32() == -1.0f) + defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(lhs)), ins, 0); + else + lowerForFPU(new(alloc()) LMathF(JSOP_MUL), ins, lhs, rhs); + return; + } + + lowerBinaryV(JSOP_MUL, ins); +} + +void +LIRGenerator::visitDiv(MDiv* ins) +{ + MDefinition* lhs = ins->lhs(); + MDefinition* rhs = ins->rhs(); + MOZ_ASSERT(lhs->type() == rhs->type()); + + if (ins->specialization() == MIRType::Int32) { + MOZ_ASSERT(lhs->type() == MIRType::Int32); + lowerDivI(ins); + return; + } + + if (ins->specialization() == MIRType::Int64) { + MOZ_ASSERT(lhs->type() == MIRType::Int64); + lowerDivI64(ins); + return; + } + + if (ins->specialization() == MIRType::Double) { + MOZ_ASSERT(lhs->type() == MIRType::Double); + lowerForFPU(new(alloc()) LMathD(JSOP_DIV), ins, lhs, rhs); + return; + } + + if (ins->specialization() == MIRType::Float32) { + MOZ_ASSERT(lhs->type() == MIRType::Float32); + lowerForFPU(new(alloc()) LMathF(JSOP_DIV), ins, lhs, rhs); + return; + } + + lowerBinaryV(JSOP_DIV, ins); +} + +void +LIRGenerator::visitMod(MMod* ins) +{ + MOZ_ASSERT(ins->lhs()->type() == ins->rhs()->type()); + + if (ins->specialization() == MIRType::Int32) { + MOZ_ASSERT(ins->type() == MIRType::Int32); + MOZ_ASSERT(ins->lhs()->type() == MIRType::Int32); + lowerModI(ins); + return; + } + + if (ins->specialization() == MIRType::Int64) { + MOZ_ASSERT(ins->type() == MIRType::Int64); + MOZ_ASSERT(ins->lhs()->type() == MIRType::Int64); + lowerModI64(ins); + return; + } + + if (ins->specialization() == MIRType::Double) { + MOZ_ASSERT(ins->type() == MIRType::Double); + MOZ_ASSERT(ins->lhs()->type() == MIRType::Double); + MOZ_ASSERT(ins->rhs()->type() == MIRType::Double); + + // Note: useRegisterAtStart is safe here, the temp is not a FP register. + LModD* lir = new(alloc()) LModD(useRegisterAtStart(ins->lhs()), useRegisterAtStart(ins->rhs()), + tempFixed(CallTempReg0)); + defineReturn(lir, ins); + return; + } + + lowerBinaryV(JSOP_MOD, ins); +} + +void +LIRGenerator::lowerBinaryV(JSOp op, MBinaryInstruction* ins) +{ + MDefinition* lhs = ins->getOperand(0); + MDefinition* rhs = ins->getOperand(1); + + MOZ_ASSERT(lhs->type() == MIRType::Value); + MOZ_ASSERT(rhs->type() == MIRType::Value); + + LBinaryV* lir = new(alloc()) LBinaryV(op, useBoxAtStart(lhs), useBoxAtStart(rhs)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitConcat(MConcat* ins) +{ + MDefinition* lhs = ins->getOperand(0); + MDefinition* rhs = ins->getOperand(1); + + MOZ_ASSERT(lhs->type() == MIRType::String); + MOZ_ASSERT(rhs->type() == MIRType::String); + MOZ_ASSERT(ins->type() == MIRType::String); + + LConcat* lir = new(alloc()) LConcat(useFixedAtStart(lhs, CallTempReg0), + useFixedAtStart(rhs, CallTempReg1), + tempFixed(CallTempReg0), + tempFixed(CallTempReg1), + tempFixed(CallTempReg2), + tempFixed(CallTempReg3), + tempFixed(CallTempReg4)); + defineFixed(lir, ins, LAllocation(AnyRegister(CallTempReg5))); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCharCodeAt(MCharCodeAt* ins) +{ + MDefinition* str = ins->getOperand(0); + MDefinition* idx = ins->getOperand(1); + + MOZ_ASSERT(str->type() == MIRType::String); + MOZ_ASSERT(idx->type() == MIRType::Int32); + + LCharCodeAt* lir = new(alloc()) LCharCodeAt(useRegister(str), useRegister(idx)); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitFromCharCode(MFromCharCode* ins) +{ + MDefinition* code = ins->getOperand(0); + + MOZ_ASSERT(code->type() == MIRType::Int32); + + LFromCharCode* lir = new(alloc()) LFromCharCode(useRegister(code)); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitFromCodePoint(MFromCodePoint* ins) +{ + MDefinition* codePoint = ins->getOperand(0); + + MOZ_ASSERT(codePoint->type() == MIRType::Int32); + + LFromCodePoint* lir = new(alloc()) LFromCodePoint(useRegister(codePoint)); + assignSnapshot(lir, Bailout_BoundsCheck); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitStart(MStart* start) +{ + LStart* lir = new(alloc()) LStart; + + // Create a snapshot that captures the initial state of the function. + assignSnapshot(lir, Bailout_ArgumentCheck); + if (start->block()->graph().entryBlock() == start->block()) + lirGraph_.setEntrySnapshot(lir->snapshot()); + + add(lir); +} + +void +LIRGenerator::visitNop(MNop* nop) +{ +} + +void +LIRGenerator::visitLimitedTruncate(MLimitedTruncate* nop) +{ + redefine(nop, nop->input()); +} + +void +LIRGenerator::visitOsrEntry(MOsrEntry* entry) +{ + LOsrEntry* lir = new(alloc()) LOsrEntry(temp()); + defineFixed(lir, entry, LAllocation(AnyRegister(OsrFrameReg))); +} + +void +LIRGenerator::visitOsrValue(MOsrValue* value) +{ + LOsrValue* lir = new(alloc()) LOsrValue(useRegister(value->entry())); + defineBox(lir, value); +} + +void +LIRGenerator::visitOsrReturnValue(MOsrReturnValue* value) +{ + LOsrReturnValue* lir = new(alloc()) LOsrReturnValue(useRegister(value->entry())); + defineBox(lir, value); +} + +void +LIRGenerator::visitOsrEnvironmentChain(MOsrEnvironmentChain* object) +{ + LOsrEnvironmentChain* lir = new(alloc()) LOsrEnvironmentChain(useRegister(object->entry())); + define(lir, object); +} + +void +LIRGenerator::visitOsrArgumentsObject(MOsrArgumentsObject* object) +{ + LOsrArgumentsObject* lir = new(alloc()) LOsrArgumentsObject(useRegister(object->entry())); + define(lir, object); +} + +void +LIRGenerator::visitToDouble(MToDouble* convert) +{ + MDefinition* opd = convert->input(); + mozilla::DebugOnly<MToFPInstruction::ConversionKind> conversion = convert->conversion(); + + switch (opd->type()) { + case MIRType::Value: + { + LValueToDouble* lir = new(alloc()) LValueToDouble(useBox(opd)); + assignSnapshot(lir, Bailout_NonPrimitiveInput); + define(lir, convert); + break; + } + + case MIRType::Null: + MOZ_ASSERT(conversion != MToFPInstruction::NumbersOnly && + conversion != MToFPInstruction::NonNullNonStringPrimitives); + lowerConstantDouble(0, convert); + break; + + case MIRType::Undefined: + MOZ_ASSERT(conversion != MToFPInstruction::NumbersOnly); + lowerConstantDouble(GenericNaN(), convert); + break; + + case MIRType::Boolean: + MOZ_ASSERT(conversion != MToFPInstruction::NumbersOnly); + MOZ_FALLTHROUGH; + + case MIRType::Int32: + { + LInt32ToDouble* lir = new(alloc()) LInt32ToDouble(useRegisterAtStart(opd)); + define(lir, convert); + break; + } + + case MIRType::Float32: + { + LFloat32ToDouble* lir = new (alloc()) LFloat32ToDouble(useRegisterAtStart(opd)); + define(lir, convert); + break; + } + + case MIRType::Double: + redefine(convert, opd); + break; + + default: + // Objects might be effectful. Symbols will throw. + // Strings are complicated - we don't handle them yet. + MOZ_CRASH("unexpected type"); + } +} + +void +LIRGenerator::visitToFloat32(MToFloat32* convert) +{ + MDefinition* opd = convert->input(); + mozilla::DebugOnly<MToFloat32::ConversionKind> conversion = convert->conversion(); + + switch (opd->type()) { + case MIRType::Value: + { + LValueToFloat32* lir = new(alloc()) LValueToFloat32(useBox(opd)); + assignSnapshot(lir, Bailout_NonPrimitiveInput); + define(lir, convert); + break; + } + + case MIRType::Null: + MOZ_ASSERT(conversion != MToFPInstruction::NumbersOnly && + conversion != MToFPInstruction::NonNullNonStringPrimitives); + lowerConstantFloat32(0, convert); + break; + + case MIRType::Undefined: + MOZ_ASSERT(conversion != MToFPInstruction::NumbersOnly); + lowerConstantFloat32(GenericNaN(), convert); + break; + + case MIRType::Boolean: + MOZ_ASSERT(conversion != MToFPInstruction::NumbersOnly); + MOZ_FALLTHROUGH; + + case MIRType::Int32: + { + LInt32ToFloat32* lir = new(alloc()) LInt32ToFloat32(useRegisterAtStart(opd)); + define(lir, convert); + break; + } + + case MIRType::Double: + { + LDoubleToFloat32* lir = new(alloc()) LDoubleToFloat32(useRegisterAtStart(opd)); + define(lir, convert); + break; + } + + case MIRType::Float32: + redefine(convert, opd); + break; + + default: + // Objects might be effectful. Symbols will throw. + // Strings are complicated - we don't handle them yet. + MOZ_CRASH("unexpected type"); + } +} + +void +LIRGenerator::visitToInt32(MToInt32* convert) +{ + MDefinition* opd = convert->input(); + + switch (opd->type()) { + case MIRType::Value: + { + LValueToInt32* lir = + new(alloc()) LValueToInt32(useBox(opd), tempDouble(), temp(), LValueToInt32::NORMAL); + assignSnapshot(lir, Bailout_NonPrimitiveInput); + define(lir, convert); + assignSafepoint(lir, convert); + break; + } + + case MIRType::Null: + MOZ_ASSERT(convert->conversion() == MacroAssembler::IntConversion_Any); + define(new(alloc()) LInteger(0), convert); + break; + + case MIRType::Boolean: + MOZ_ASSERT(convert->conversion() == MacroAssembler::IntConversion_Any || + convert->conversion() == MacroAssembler::IntConversion_NumbersOrBoolsOnly); + redefine(convert, opd); + break; + + case MIRType::Int32: + redefine(convert, opd); + break; + + case MIRType::Float32: + { + LFloat32ToInt32* lir = new(alloc()) LFloat32ToInt32(useRegister(opd)); + assignSnapshot(lir, Bailout_PrecisionLoss); + define(lir, convert); + break; + } + + case MIRType::Double: + { + LDoubleToInt32* lir = new(alloc()) LDoubleToInt32(useRegister(opd)); + assignSnapshot(lir, Bailout_PrecisionLoss); + define(lir, convert); + break; + } + + case MIRType::String: + case MIRType::Symbol: + case MIRType::Object: + case MIRType::Undefined: + // Objects might be effectful. Symbols throw. Undefined coerces to NaN, not int32. + MOZ_CRASH("ToInt32 invalid input type"); + + default: + MOZ_CRASH("unexpected type"); + } +} + +void +LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate) +{ + MDefinition* opd = truncate->input(); + + switch (opd->type()) { + case MIRType::Value: + { + LValueToInt32* lir = new(alloc()) LValueToInt32(useBox(opd), tempDouble(), temp(), + LValueToInt32::TRUNCATE); + assignSnapshot(lir, Bailout_NonPrimitiveInput); + define(lir, truncate); + assignSafepoint(lir, truncate); + break; + } + + case MIRType::Null: + case MIRType::Undefined: + define(new(alloc()) LInteger(0), truncate); + break; + + case MIRType::Int32: + case MIRType::Boolean: + redefine(truncate, opd); + break; + + case MIRType::Double: + lowerTruncateDToInt32(truncate); + break; + + case MIRType::Float32: + lowerTruncateFToInt32(truncate); + break; + + default: + // Objects might be effectful. Symbols throw. + // Strings are complicated - we don't handle them yet. + MOZ_CRASH("unexpected type"); + } +} + +void +LIRGenerator::visitWasmTruncateToInt32(MWasmTruncateToInt32* ins) +{ + MDefinition* input = ins->input(); + switch (input->type()) { + case MIRType::Double: + case MIRType::Float32: { + auto* lir = new(alloc()) LWasmTruncateToInt32(useRegisterAtStart(input)); + define(lir, ins); + break; + } + default: + MOZ_CRASH("unexpected type in WasmTruncateToInt32"); + } +} + +void +LIRGenerator::visitWrapInt64ToInt32(MWrapInt64ToInt32* ins) +{ + define(new(alloc()) LWrapInt64ToInt32(useInt64AtStart(ins->input())), ins); +} + +void +LIRGenerator::visitToString(MToString* ins) +{ + MDefinition* opd = ins->input(); + + switch (opd->type()) { + case MIRType::Null: { + const JSAtomState& names = GetJitContext()->runtime->names(); + LPointer* lir = new(alloc()) LPointer(names.null); + define(lir, ins); + break; + } + + case MIRType::Undefined: { + const JSAtomState& names = GetJitContext()->runtime->names(); + LPointer* lir = new(alloc()) LPointer(names.undefined); + define(lir, ins); + break; + } + + case MIRType::Boolean: { + LBooleanToString* lir = new(alloc()) LBooleanToString(useRegister(opd)); + define(lir, ins); + break; + } + + case MIRType::Double: { + LDoubleToString* lir = new(alloc()) LDoubleToString(useRegister(opd), temp()); + + define(lir, ins); + assignSafepoint(lir, ins); + break; + } + + case MIRType::Int32: { + LIntToString* lir = new(alloc()) LIntToString(useRegister(opd)); + + define(lir, ins); + assignSafepoint(lir, ins); + break; + } + + case MIRType::String: + redefine(ins, ins->input()); + break; + + case MIRType::Value: { + LValueToString* lir = new(alloc()) LValueToString(useBox(opd), tempToUnbox()); + if (ins->fallible()) + assignSnapshot(lir, Bailout_NonPrimitiveInput); + define(lir, ins); + assignSafepoint(lir, ins); + break; + } + + default: + // Float32, symbols, and objects are not supported. + MOZ_CRASH("unexpected type"); + } +} + +void +LIRGenerator::visitToObjectOrNull(MToObjectOrNull* ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType::Value); + + LValueToObjectOrNull* lir = new(alloc()) LValueToObjectOrNull(useBox(ins->input())); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitRegExp(MRegExp* ins) +{ + if (ins->mustClone()) { + LRegExp* lir = new(alloc()) LRegExp(); + defineReturn(lir, ins); + assignSafepoint(lir, ins); + } else { + RegExpObject* source = ins->source(); + define(new(alloc()) LPointer(source), ins); + } +} + +void +LIRGenerator::visitRegExpMatcher(MRegExpMatcher* ins) +{ + MOZ_ASSERT(ins->regexp()->type() == MIRType::Object); + MOZ_ASSERT(ins->string()->type() == MIRType::String); + MOZ_ASSERT(ins->lastIndex()->type() == MIRType::Int32); + + LRegExpMatcher* lir = new(alloc()) LRegExpMatcher(useFixedAtStart(ins->regexp(), RegExpMatcherRegExpReg), + useFixedAtStart(ins->string(), RegExpMatcherStringReg), + useFixedAtStart(ins->lastIndex(), RegExpMatcherLastIndexReg)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitRegExpSearcher(MRegExpSearcher* ins) +{ + MOZ_ASSERT(ins->regexp()->type() == MIRType::Object); + MOZ_ASSERT(ins->string()->type() == MIRType::String); + MOZ_ASSERT(ins->lastIndex()->type() == MIRType::Int32); + + LRegExpSearcher* lir = new(alloc()) LRegExpSearcher(useFixedAtStart(ins->regexp(), RegExpTesterRegExpReg), + useFixedAtStart(ins->string(), RegExpTesterStringReg), + useFixedAtStart(ins->lastIndex(), RegExpTesterLastIndexReg)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitRegExpTester(MRegExpTester* ins) +{ + MOZ_ASSERT(ins->regexp()->type() == MIRType::Object); + MOZ_ASSERT(ins->string()->type() == MIRType::String); + MOZ_ASSERT(ins->lastIndex()->type() == MIRType::Int32); + + LRegExpTester* lir = new(alloc()) LRegExpTester(useFixedAtStart(ins->regexp(), RegExpTesterRegExpReg), + useFixedAtStart(ins->string(), RegExpTesterStringReg), + useFixedAtStart(ins->lastIndex(), RegExpTesterLastIndexReg)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitRegExpPrototypeOptimizable(MRegExpPrototypeOptimizable* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Boolean); + LRegExpPrototypeOptimizable* lir = new(alloc()) LRegExpPrototypeOptimizable(useRegister(ins->object()), + temp()); + define(lir, ins); +} + +void +LIRGenerator::visitRegExpInstanceOptimizable(MRegExpInstanceOptimizable* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->proto()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Boolean); + LRegExpInstanceOptimizable* lir = new(alloc()) LRegExpInstanceOptimizable(useRegister(ins->object()), + useRegister(ins->proto()), + temp()); + define(lir, ins); +} + +void +LIRGenerator::visitGetFirstDollarIndex(MGetFirstDollarIndex* ins) +{ + MOZ_ASSERT(ins->str()->type() == MIRType::String); + MOZ_ASSERT(ins->type() == MIRType::Int32); + LGetFirstDollarIndex* lir = new(alloc()) LGetFirstDollarIndex(useRegister(ins->str()), + temp(), temp(), temp()); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitStringReplace(MStringReplace* ins) +{ + MOZ_ASSERT(ins->pattern()->type() == MIRType::String); + MOZ_ASSERT(ins->string()->type() == MIRType::String); + MOZ_ASSERT(ins->replacement()->type() == MIRType::String); + + LStringReplace* lir = new(alloc()) LStringReplace(useRegisterOrConstantAtStart(ins->string()), + useRegisterAtStart(ins->pattern()), + useRegisterOrConstantAtStart(ins->replacement())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitBinarySharedStub(MBinarySharedStub* ins) +{ + MDefinition* lhs = ins->getOperand(0); + MDefinition* rhs = ins->getOperand(1); + + MOZ_ASSERT(ins->type() == MIRType::Value); + MOZ_ASSERT(ins->type() == MIRType::Value); + + LBinarySharedStub* lir = new(alloc()) LBinarySharedStub(useBoxFixedAtStart(lhs, R0), + useBoxFixedAtStart(rhs, R1)); + defineSharedStubReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitUnarySharedStub(MUnarySharedStub* ins) +{ + MDefinition* input = ins->getOperand(0); + MOZ_ASSERT(ins->type() == MIRType::Value); + + LUnarySharedStub* lir = new(alloc()) LUnarySharedStub(useBoxFixedAtStart(input, R0)); + defineSharedStubReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitNullarySharedStub(MNullarySharedStub* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Value); + + LNullarySharedStub* lir = new(alloc()) LNullarySharedStub(); + + defineSharedStubReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitLambda(MLambda* ins) +{ + if (ins->info().singletonType || ins->info().useSingletonForClone) { + // If the function has a singleton type, this instruction will only be + // executed once so we don't bother inlining it. + // + // If UseSingletonForClone is true, we will assign a singleton type to + // the clone and we have to clone the script, we can't do that inline. + LLambdaForSingleton* lir = new(alloc()) + LLambdaForSingleton(useRegisterAtStart(ins->environmentChain())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); + } else { + LLambda* lir = new(alloc()) LLambda(useRegister(ins->environmentChain()), temp()); + define(lir, ins); + assignSafepoint(lir, ins); + } +} + +void +LIRGenerator::visitLambdaArrow(MLambdaArrow* ins) +{ + MOZ_ASSERT(ins->environmentChain()->type() == MIRType::Object); + MOZ_ASSERT(ins->newTargetDef()->type() == MIRType::Value); + + LLambdaArrow* lir = new(alloc()) LLambdaArrow(useRegister(ins->environmentChain()), + useBox(ins->newTargetDef())); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitKeepAliveObject(MKeepAliveObject* ins) +{ + MDefinition* obj = ins->object(); + MOZ_ASSERT(obj->type() == MIRType::Object); + + add(new(alloc()) LKeepAliveObject(useKeepalive(obj)), ins); +} + +void +LIRGenerator::visitSlots(MSlots* ins) +{ + define(new(alloc()) LSlots(useRegisterAtStart(ins->object())), ins); +} + +void +LIRGenerator::visitElements(MElements* ins) +{ + define(new(alloc()) LElements(useRegisterAtStart(ins->object())), ins); +} + +void +LIRGenerator::visitConstantElements(MConstantElements* ins) +{ + define(new(alloc()) LPointer(ins->value().unwrap(/*safe - pointer does not flow back to C++*/), + LPointer::NON_GC_THING), + ins); +} + +void +LIRGenerator::visitConvertElementsToDoubles(MConvertElementsToDoubles* ins) +{ + LInstruction* check = new(alloc()) LConvertElementsToDoubles(useRegister(ins->elements())); + add(check, ins); + assignSafepoint(check, ins); +} + +void +LIRGenerator::visitMaybeToDoubleElement(MMaybeToDoubleElement* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + MOZ_ASSERT(ins->value()->type() == MIRType::Int32); + + LMaybeToDoubleElement* lir = new(alloc()) LMaybeToDoubleElement(useRegisterAtStart(ins->elements()), + useRegisterAtStart(ins->value()), + tempDouble()); + defineBox(lir, ins); +} + +void +LIRGenerator::visitMaybeCopyElementsForWrite(MMaybeCopyElementsForWrite* ins) +{ + LInstruction* check = new(alloc()) LMaybeCopyElementsForWrite(useRegister(ins->object()), temp()); + add(check, ins); + assignSafepoint(check, ins); +} + +void +LIRGenerator::visitLoadSlot(MLoadSlot* ins) +{ + switch (ins->type()) { + case MIRType::Value: + defineBox(new(alloc()) LLoadSlotV(useRegisterAtStart(ins->slots())), ins); + break; + + case MIRType::Undefined: + case MIRType::Null: + MOZ_CRASH("typed load must have a payload"); + + default: + define(new(alloc()) LLoadSlotT(useRegisterForTypedLoad(ins->slots(), ins->type())), ins); + break; + } +} + +void +LIRGenerator::visitFunctionEnvironment(MFunctionEnvironment* ins) +{ + define(new(alloc()) LFunctionEnvironment(useRegisterAtStart(ins->function())), ins); +} + +void +LIRGenerator::visitInterruptCheck(MInterruptCheck* ins) +{ + LInstruction* lir = new(alloc()) LInterruptCheck(); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitWasmTrap(MWasmTrap* ins) +{ + add(new(alloc()) LWasmTrap, ins); +} + +void +LIRGenerator::visitWasmReinterpret(MWasmReinterpret* ins) +{ + if (ins->type() == MIRType::Int64) + defineInt64(new(alloc()) LWasmReinterpretToI64(useRegisterAtStart(ins->input())), ins); + else if (ins->input()->type() == MIRType::Int64) + define(new(alloc()) LWasmReinterpretFromI64(useInt64RegisterAtStart(ins->input())), ins); + else + define(new(alloc()) LWasmReinterpret(useRegisterAtStart(ins->input())), ins); +} + +void +LIRGenerator::visitStoreSlot(MStoreSlot* ins) +{ + LInstruction* lir; + + switch (ins->value()->type()) { + case MIRType::Value: + lir = new(alloc()) LStoreSlotV(useRegister(ins->slots()), useBox(ins->value())); + add(lir, ins); + break; + + case MIRType::Double: + add(new(alloc()) LStoreSlotT(useRegister(ins->slots()), useRegister(ins->value())), ins); + break; + + case MIRType::Float32: + MOZ_CRASH("Float32 shouldn't be stored in a slot."); + + default: + add(new(alloc()) LStoreSlotT(useRegister(ins->slots()), + useRegisterOrConstant(ins->value())), ins); + break; + } +} + +void +LIRGenerator::visitFilterTypeSet(MFilterTypeSet* ins) +{ + redefine(ins, ins->input()); +} + +void +LIRGenerator::visitTypeBarrier(MTypeBarrier* ins) +{ + // Requesting a non-GC pointer is safe here since we never re-enter C++ + // from inside a type barrier test. + + const TemporaryTypeSet* types = ins->resultTypeSet(); + bool needTemp = !types->unknownObject() && types->getObjectCount() > 0; + + MIRType inputType = ins->getOperand(0)->type(); + MOZ_ASSERT(inputType == ins->type()); + + // Handle typebarrier that will always bail. + // (Emit LBail for visibility). + if (ins->alwaysBails()) { + LBail* bail = new(alloc()) LBail(); + assignSnapshot(bail, Bailout_Inevitable); + add(bail, ins); + redefine(ins, ins->input()); + return; + } + + // Handle typebarrier with Value as input. + if (inputType == MIRType::Value) { + LDefinition tmp = needTemp ? temp() : tempToUnbox(); + LTypeBarrierV* barrier = new(alloc()) LTypeBarrierV(useBox(ins->input()), tmp); + assignSnapshot(barrier, Bailout_TypeBarrierV); + add(barrier, ins); + redefine(ins, ins->input()); + return; + } + + // The payload needs to be tested if it either might be null or might have + // an object that should be excluded from the barrier. + bool needsObjectBarrier = false; + if (inputType == MIRType::ObjectOrNull) + needsObjectBarrier = true; + if (inputType == MIRType::Object && !types->hasType(TypeSet::AnyObjectType()) && + ins->barrierKind() != BarrierKind::TypeTagOnly) + { + needsObjectBarrier = true; + } + + if (needsObjectBarrier) { + LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp(); + LTypeBarrierO* barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp); + assignSnapshot(barrier, Bailout_TypeBarrierO); + add(barrier, ins); + redefine(ins, ins->getOperand(0)); + return; + } + + // Handle remaining cases: No-op, unbox did everything. + redefine(ins, ins->getOperand(0)); +} + +void +LIRGenerator::visitMonitorTypes(MMonitorTypes* ins) +{ + // Requesting a non-GC pointer is safe here since we never re-enter C++ + // from inside a type check. + + const TemporaryTypeSet* types = ins->typeSet(); + bool needTemp = !types->unknownObject() && types->getObjectCount() > 0; + LDefinition tmp = needTemp ? temp() : tempToUnbox(); + + LMonitorTypes* lir = new(alloc()) LMonitorTypes(useBox(ins->input()), tmp); + assignSnapshot(lir, Bailout_MonitorTypes); + add(lir, ins); +} + +// Returns true iff |def| is a constant that's either not a GC thing or is not +// allocated in the nursery. +static bool +IsNonNurseryConstant(MDefinition* def) +{ + if (!def->isConstant()) + return false; + Value v = def->toConstant()->toJSValue(); + return !v.isMarkable() || !IsInsideNursery(v.toMarkablePointer()); +} + +void +LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + // LPostWriteBarrier assumes that if it has a constant object then that + // object is tenured, and does not need to be tested for being in the + // nursery. Ensure that assumption holds by lowering constant nursery + // objects to a register. + bool useConstantObject = IsNonNurseryConstant(ins->object()); + + switch (ins->value()->type()) { + case MIRType::Object: + case MIRType::ObjectOrNull: { + LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); + LPostWriteBarrierO* lir = + new(alloc()) LPostWriteBarrierO(useConstantObject + ? useOrConstant(ins->object()) + : useRegister(ins->object()), + useRegister(ins->value()), tmp); + add(lir, ins); + assignSafepoint(lir, ins); + break; + } + case MIRType::Value: { + LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); + LPostWriteBarrierV* lir = + new(alloc()) LPostWriteBarrierV(useConstantObject + ? useOrConstant(ins->object()) + : useRegister(ins->object()), + useBox(ins->value()), + tmp); + add(lir, ins); + assignSafepoint(lir, ins); + break; + } + default: + // Currently, only objects can be in the nursery. Other instruction + // types cannot hold nursery pointers. + break; + } +} + +void +LIRGenerator::visitPostWriteElementBarrier(MPostWriteElementBarrier* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + // LPostWriteElementBarrier assumes that if it has a constant object then that + // object is tenured, and does not need to be tested for being in the + // nursery. Ensure that assumption holds by lowering constant nursery + // objects to a register. + bool useConstantObject = + ins->object()->isConstant() && + !IsInsideNursery(&ins->object()->toConstant()->toObject()); + + switch (ins->value()->type()) { + case MIRType::Object: + case MIRType::ObjectOrNull: { + LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); + LPostWriteElementBarrierO* lir = + new(alloc()) LPostWriteElementBarrierO(useConstantObject + ? useOrConstant(ins->object()) + : useRegister(ins->object()), + useRegister(ins->value()), + useRegister(ins->index()), + tmp); + add(lir, ins); + assignSafepoint(lir, ins); + break; + } + case MIRType::Value: { + LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); + LPostWriteElementBarrierV* lir = + new(alloc()) LPostWriteElementBarrierV(useConstantObject + ? useOrConstant(ins->object()) + : useRegister(ins->object()), + useRegister(ins->index()), + useBox(ins->value()), + tmp); + add(lir, ins); + assignSafepoint(lir, ins); + break; + } + default: + // Currently, only objects can be in the nursery. Other instruction + // types cannot hold nursery pointers. + break; + } +} + +void +LIRGenerator::visitArrayLength(MArrayLength* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + define(new(alloc()) LArrayLength(useRegisterAtStart(ins->elements())), ins); +} + +void +LIRGenerator::visitSetArrayLength(MSetArrayLength* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + MOZ_ASSERT(ins->index()->isConstant()); + add(new(alloc()) LSetArrayLength(useRegister(ins->elements()), + useRegisterOrConstant(ins->index())), ins); +} + +void +LIRGenerator::visitGetNextEntryForIterator(MGetNextEntryForIterator* ins) +{ + MOZ_ASSERT(ins->iter()->type() == MIRType::Object); + MOZ_ASSERT(ins->result()->type() == MIRType::Object); + auto lir = new(alloc()) LGetNextEntryForIterator(useRegister(ins->iter()), + useRegister(ins->result()), + temp(), temp(), temp()); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitTypedArrayLength(MTypedArrayLength* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + define(new(alloc()) LTypedArrayLength(useRegisterAtStart(ins->object())), ins); +} + +void +LIRGenerator::visitTypedArrayElements(MTypedArrayElements* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Elements); + define(new(alloc()) LTypedArrayElements(useRegisterAtStart(ins->object())), ins); +} + +void +LIRGenerator::visitSetDisjointTypedElements(MSetDisjointTypedElements* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::None); + + MDefinition* target = ins->target(); + MOZ_ASSERT(target->type() == MIRType::Object); + + MDefinition* targetOffset = ins->targetOffset(); + MOZ_ASSERT(targetOffset->type() == MIRType::Int32); + + MDefinition* source = ins->source(); + MOZ_ASSERT(source->type() == MIRType::Object); + + auto lir = new(alloc()) LSetDisjointTypedElements(useRegister(target), + useRegister(targetOffset), + useRegister(source), + temp()); + add(lir, ins); +} + +void +LIRGenerator::visitTypedObjectDescr(MTypedObjectDescr* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Object); + define(new(alloc()) LTypedObjectDescr(useRegisterAtStart(ins->object())), ins); +} + +void +LIRGenerator::visitTypedObjectElements(MTypedObjectElements* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Elements); + define(new(alloc()) LTypedObjectElements(useRegister(ins->object())), ins); +} + +void +LIRGenerator::visitSetTypedObjectOffset(MSetTypedObjectOffset* ins) +{ + add(new(alloc()) LSetTypedObjectOffset(useRegister(ins->object()), + useRegister(ins->offset()), + temp(), temp()), + ins); +} + +void +LIRGenerator::visitInitializedLength(MInitializedLength* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + define(new(alloc()) LInitializedLength(useRegisterAtStart(ins->elements())), ins); +} + +void +LIRGenerator::visitSetInitializedLength(MSetInitializedLength* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + MOZ_ASSERT(ins->index()->isConstant()); + add(new(alloc()) LSetInitializedLength(useRegister(ins->elements()), + useRegisterOrConstant(ins->index())), ins); +} + +void +LIRGenerator::visitUnboxedArrayLength(MUnboxedArrayLength* ins) +{ + define(new(alloc()) LUnboxedArrayLength(useRegisterAtStart(ins->object())), ins); +} + +void +LIRGenerator::visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins) +{ + define(new(alloc()) LUnboxedArrayInitializedLength(useRegisterAtStart(ins->object())), ins); +} + +void +LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins) +{ + add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins); +} + +void +LIRGenerator::visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins) +{ + add(new(alloc()) LSetUnboxedArrayInitializedLength(useRegister(ins->object()), + useRegisterOrConstant(ins->length()), + temp()), ins); +} + +void +LIRGenerator::visitNot(MNot* ins) +{ + MDefinition* op = ins->input(); + + // String is converted to length of string in the type analysis phase (see + // TestPolicy). + MOZ_ASSERT(op->type() != MIRType::String); + + // - boolean: x xor 1 + // - int32: LCompare(x, 0) + // - double: LCompare(x, 0) + // - null or undefined: true + // - object: false if it never emulates undefined, else LNotO(x) + switch (op->type()) { + case MIRType::Boolean: { + MConstant* cons = MConstant::New(alloc(), Int32Value(1)); + ins->block()->insertBefore(ins, cons); + lowerForALU(new(alloc()) LBitOpI(JSOP_BITXOR), ins, op, cons); + break; + } + case MIRType::Int32: + define(new(alloc()) LNotI(useRegisterAtStart(op)), ins); + break; + case MIRType::Int64: + define(new(alloc()) LNotI64(useInt64RegisterAtStart(op)), ins); + break; + case MIRType::Double: + define(new(alloc()) LNotD(useRegister(op)), ins); + break; + case MIRType::Float32: + define(new(alloc()) LNotF(useRegister(op)), ins); + break; + case MIRType::Undefined: + case MIRType::Null: + define(new(alloc()) LInteger(1), ins); + break; + case MIRType::Symbol: + define(new(alloc()) LInteger(0), ins); + break; + case MIRType::Object: + if (!ins->operandMightEmulateUndefined()) { + // Objects that don't emulate undefined can be constant-folded. + define(new(alloc()) LInteger(0), ins); + } else { + // All others require further work. + define(new(alloc()) LNotO(useRegister(op)), ins); + } + break; + case MIRType::Value: { + LDefinition temp0, temp1; + if (ins->operandMightEmulateUndefined()) { + temp0 = temp(); + temp1 = temp(); + } else { + temp0 = LDefinition::BogusTemp(); + temp1 = LDefinition::BogusTemp(); + } + + LNotV* lir = new(alloc()) LNotV(useBox(op), tempDouble(), temp0, temp1); + define(lir, ins); + break; + } + + default: + MOZ_CRASH("Unexpected MIRType."); + } +} + +void +LIRGenerator::visitBoundsCheck(MBoundsCheck* ins) +{ + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + MOZ_ASSERT(ins->length()->type() == MIRType::Int32); + MOZ_ASSERT(ins->type() == MIRType::Int32); + + if (!ins->fallible()) + return; + + LInstruction* check; + if (ins->minimum() || ins->maximum()) { + check = new(alloc()) LBoundsCheckRange(useRegisterOrConstant(ins->index()), + useAny(ins->length()), + temp()); + } else { + check = new(alloc()) LBoundsCheck(useRegisterOrConstant(ins->index()), + useAnyOrConstant(ins->length())); + } + assignSnapshot(check, Bailout_BoundsCheck); + add(check, ins); +} + +void +LIRGenerator::visitBoundsCheckLower(MBoundsCheckLower* ins) +{ + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + if (!ins->fallible()) + return; + + LInstruction* check = new(alloc()) LBoundsCheckLower(useRegister(ins->index())); + assignSnapshot(check, Bailout_BoundsCheck); + add(check, ins); +} + +void +LIRGenerator::visitInArray(MInArray* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + MOZ_ASSERT(ins->initLength()->type() == MIRType::Int32); + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Boolean); + + LAllocation object; + if (ins->needsNegativeIntCheck()) + object = useRegister(ins->object()); + + LInArray* lir = new(alloc()) LInArray(useRegister(ins->elements()), + useRegisterOrConstant(ins->index()), + useRegister(ins->initLength()), + object); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitLoadElement(MLoadElement* ins) +{ + MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + switch (ins->type()) { + case MIRType::Value: + { + LLoadElementV* lir = new(alloc()) LLoadElementV(useRegister(ins->elements()), + useRegisterOrConstant(ins->index())); + if (ins->fallible()) + assignSnapshot(lir, Bailout_Hole); + defineBox(lir, ins); + break; + } + case MIRType::Undefined: + case MIRType::Null: + MOZ_CRASH("typed load must have a payload"); + + default: + { + LLoadElementT* lir = new(alloc()) LLoadElementT(useRegister(ins->elements()), + useRegisterOrConstant(ins->index())); + if (ins->fallible()) + assignSnapshot(lir, Bailout_Hole); + define(lir, ins); + break; + } + } +} + +void +LIRGenerator::visitLoadElementHole(MLoadElementHole* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + MOZ_ASSERT(ins->initLength()->type() == MIRType::Int32); + MOZ_ASSERT(ins->type() == MIRType::Value); + + LLoadElementHole* lir = new(alloc()) LLoadElementHole(useRegister(ins->elements()), + useRegisterOrConstant(ins->index()), + useRegister(ins->initLength())); + if (ins->needsNegativeIntCheck()) + assignSnapshot(lir, Bailout_NegativeIndex); + defineBox(lir, ins); +} + +void +LIRGenerator::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins) +{ + MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + if (ins->type() == MIRType::Object || ins->type() == MIRType::ObjectOrNull) { + LLoadUnboxedPointerT* lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()), + useRegisterOrConstant(ins->index())); + if (ins->nullBehavior() == MLoadUnboxedObjectOrNull::BailOnNull) + assignSnapshot(lir, Bailout_TypeBarrierO); + define(lir, ins); + } else { + MOZ_ASSERT(ins->type() == MIRType::Value); + MOZ_ASSERT(ins->nullBehavior() != MLoadUnboxedObjectOrNull::BailOnNull); + + LLoadUnboxedPointerV* lir = new(alloc()) LLoadUnboxedPointerV(useRegister(ins->elements()), + useRegisterOrConstant(ins->index())); + defineBox(lir, ins); + } +} + +void +LIRGenerator::visitLoadUnboxedString(MLoadUnboxedString* ins) +{ + MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + MOZ_ASSERT(ins->type() == MIRType::String); + + LLoadUnboxedPointerT* lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()), + useRegisterOrConstant(ins->index())); + define(lir, ins); +} + +void +LIRGenerator::visitStoreElement(MStoreElement* ins) +{ + MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + const LUse elements = useRegister(ins->elements()); + const LAllocation index = useRegisterOrConstant(ins->index()); + + switch (ins->value()->type()) { + case MIRType::Value: + { + LInstruction* lir = new(alloc()) LStoreElementV(elements, index, useBox(ins->value())); + if (ins->fallible()) + assignSnapshot(lir, Bailout_Hole); + add(lir, ins); + break; + } + + default: + { + const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); + LInstruction* lir = new(alloc()) LStoreElementT(elements, index, value); + if (ins->fallible()) + assignSnapshot(lir, Bailout_Hole); + add(lir, ins); + break; + } + } +} + +void +LIRGenerator::visitStoreElementHole(MStoreElementHole* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + const LUse object = useRegister(ins->object()); + const LUse elements = useRegister(ins->elements()); + const LAllocation index = useRegisterOrConstant(ins->index()); + + // Use a temp register when adding new elements to unboxed arrays. + LDefinition tempDef = LDefinition::BogusTemp(); + if (ins->unboxedType() != JSVAL_TYPE_MAGIC) + tempDef = temp(); + + LInstruction* lir; + switch (ins->value()->type()) { + case MIRType::Value: + lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value()), + tempDef); + break; + + default: + { + const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); + lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, tempDef); + break; + } + } + + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitFallibleStoreElement(MFallibleStoreElement* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + const LUse object = useRegister(ins->object()); + const LUse elements = useRegister(ins->elements()); + const LAllocation index = useRegisterOrConstant(ins->index()); + + // Use a temp register when adding new elements to unboxed arrays. + LDefinition tempDef = LDefinition::BogusTemp(); + if (ins->unboxedType() != JSVAL_TYPE_MAGIC) + tempDef = temp(); + + LInstruction* lir; + switch (ins->value()->type()) { + case MIRType::Value: + lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value()), + tempDef); + break; + default: + const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); + lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value, tempDef); + break; + } + + add(lir, ins); + assignSafepoint(lir, ins); +} + + +void +LIRGenerator::visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins) +{ + MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + MOZ_ASSERT(ins->value()->type() == MIRType::Object || + ins->value()->type() == MIRType::Null || + ins->value()->type() == MIRType::ObjectOrNull); + + const LUse elements = useRegister(ins->elements()); + const LAllocation index = useRegisterOrNonDoubleConstant(ins->index()); + const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); + + LInstruction* lir = new(alloc()) LStoreUnboxedPointer(elements, index, value); + add(lir, ins); +} + +void +LIRGenerator::visitStoreUnboxedString(MStoreUnboxedString* ins) +{ + MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + MOZ_ASSERT(ins->value()->type() == MIRType::String); + + const LUse elements = useRegister(ins->elements()); + const LAllocation index = useRegisterOrConstant(ins->index()); + const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); + + LInstruction* lir = new(alloc()) LStoreUnboxedPointer(elements, index, value); + add(lir, ins); +} + +void +LIRGenerator::visitConvertUnboxedObjectToNative(MConvertUnboxedObjectToNative* ins) +{ + LInstruction* check = new(alloc()) LConvertUnboxedObjectToNative(useRegister(ins->object())); + add(check, ins); + assignSafepoint(check, ins); +} + +void +LIRGenerator::visitEffectiveAddress(MEffectiveAddress* ins) +{ + define(new(alloc()) LEffectiveAddress(useRegister(ins->base()), useRegister(ins->index())), ins); +} + +void +LIRGenerator::visitArrayPopShift(MArrayPopShift* ins) +{ + LUse object = useRegister(ins->object()); + + switch (ins->type()) { + case MIRType::Value: + { + LArrayPopShiftV* lir = new(alloc()) LArrayPopShiftV(object, temp(), temp()); + defineBox(lir, ins); + assignSafepoint(lir, ins); + break; + } + case MIRType::Undefined: + case MIRType::Null: + MOZ_CRASH("typed load must have a payload"); + + default: + { + LArrayPopShiftT* lir = new(alloc()) LArrayPopShiftT(object, temp(), temp()); + define(lir, ins); + assignSafepoint(lir, ins); + break; + } + } +} + +void +LIRGenerator::visitArrayPush(MArrayPush* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Int32); + + LUse object = useRegister(ins->object()); + + switch (ins->value()->type()) { + case MIRType::Value: + { + LArrayPushV* lir = new(alloc()) LArrayPushV(object, useBox(ins->value()), temp()); + define(lir, ins); + assignSafepoint(lir, ins); + break; + } + + default: + { + const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); + LArrayPushT* lir = new(alloc()) LArrayPushT(object, value, temp()); + define(lir, ins); + assignSafepoint(lir, ins); + break; + } + } +} + +void +LIRGenerator::visitArraySlice(MArraySlice* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Object); + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->begin()->type() == MIRType::Int32); + MOZ_ASSERT(ins->end()->type() == MIRType::Int32); + + LArraySlice* lir = new(alloc()) LArraySlice(useFixedAtStart(ins->object(), CallTempReg0), + useFixedAtStart(ins->begin(), CallTempReg1), + useFixedAtStart(ins->end(), CallTempReg2), + tempFixed(CallTempReg3), + tempFixed(CallTempReg4)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitArrayJoin(MArrayJoin* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::String); + MOZ_ASSERT(ins->array()->type() == MIRType::Object); + MOZ_ASSERT(ins->sep()->type() == MIRType::String); + + LArrayJoin* lir = new(alloc()) LArrayJoin(useRegisterAtStart(ins->array()), + useRegisterAtStart(ins->sep())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitSinCos(MSinCos *ins) +{ + MOZ_ASSERT(ins->type() == MIRType::SinCosDouble); + MOZ_ASSERT(ins->input()->type() == MIRType::Double || + ins->input()->type() == MIRType::Float32 || + ins->input()->type() == MIRType::Int32); + + LSinCos *lir = new (alloc()) LSinCos(useRegisterAtStart(ins->input()), + tempFixed(CallTempReg0), + temp()); + defineSinCos(lir, ins); +} + +void +LIRGenerator::visitStringSplit(MStringSplit* ins) +{ + MOZ_ASSERT(ins->type() == MIRType::Object); + MOZ_ASSERT(ins->string()->type() == MIRType::String); + MOZ_ASSERT(ins->separator()->type() == MIRType::String); + + LStringSplit* lir = new(alloc()) LStringSplit(useRegisterAtStart(ins->string()), + useRegisterAtStart(ins->separator())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins) +{ + MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + const LUse elements = useRegister(ins->elements()); + const LAllocation index = useRegisterOrConstant(ins->index()); + + MOZ_ASSERT(IsNumberType(ins->type()) || IsSimdType(ins->type()) || + ins->type() == MIRType::Boolean); + + // We need a temp register for Uint32Array with known double result. + LDefinition tempDef = LDefinition::BogusTemp(); + if (ins->readType() == Scalar::Uint32 && IsFloatingPointType(ins->type())) + tempDef = temp(); + + if (ins->requiresMemoryBarrier()) { + LMemoryBarrier* fence = new(alloc()) LMemoryBarrier(MembarBeforeLoad); + add(fence, ins); + } + LLoadUnboxedScalar* lir = new(alloc()) LLoadUnboxedScalar(elements, index, tempDef); + if (ins->fallible()) + assignSnapshot(lir, Bailout_Overflow); + define(lir, ins); + if (ins->requiresMemoryBarrier()) { + LMemoryBarrier* fence = new(alloc()) LMemoryBarrier(MembarAfterLoad); + add(fence, ins); + } +} + +void +LIRGenerator::visitClampToUint8(MClampToUint8* ins) +{ + MDefinition* in = ins->input(); + + switch (in->type()) { + case MIRType::Boolean: + redefine(ins, in); + break; + + case MIRType::Int32: + defineReuseInput(new(alloc()) LClampIToUint8(useRegisterAtStart(in)), ins, 0); + break; + + case MIRType::Double: + // LClampDToUint8 clobbers its input register. Making it available as + // a temp copy describes this behavior to the register allocator. + define(new(alloc()) LClampDToUint8(useRegisterAtStart(in), tempCopy(in, 0)), ins); + break; + + case MIRType::Value: + { + LClampVToUint8* lir = new(alloc()) LClampVToUint8(useBox(in), tempDouble()); + assignSnapshot(lir, Bailout_NonPrimitiveInput); + define(lir, ins); + assignSafepoint(lir, ins); + break; + } + + default: + MOZ_CRASH("unexpected type"); + } +} + +void +LIRGenerator::visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + MOZ_ASSERT(ins->type() == MIRType::Value); + + const LUse object = useRegister(ins->object()); + const LAllocation index = useRegisterOrConstant(ins->index()); + + LLoadTypedArrayElementHole* lir = new(alloc()) LLoadTypedArrayElementHole(object, index); + if (ins->fallible()) + assignSnapshot(lir, Bailout_Overflow); + defineBox(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic* ins) +{ + LLoadTypedArrayElementStatic* lir = + new(alloc()) LLoadTypedArrayElementStatic(useRegisterAtStart(ins->ptr())); + + // In case of out of bounds, may bail out, or may jump to ool code. + if (ins->fallible()) + assignSnapshot(lir, Bailout_BoundsCheck); + define(lir, ins); +} + +void +LIRGenerator::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) +{ + MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + if (ins->isSimdWrite()) { + MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32x4, ins->value()->type() == MIRType::Float32x4); + MOZ_ASSERT_IF(ins->writeType() == Scalar::Int8x16, ins->value()->type() == MIRType::Int8x16); + MOZ_ASSERT_IF(ins->writeType() == Scalar::Int16x8, ins->value()->type() == MIRType::Int16x8); + MOZ_ASSERT_IF(ins->writeType() == Scalar::Int32x4, ins->value()->type() == MIRType::Int32x4); + } else if (ins->isFloatWrite()) { + MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32, ins->value()->type() == MIRType::Float32); + MOZ_ASSERT_IF(ins->writeType() == Scalar::Float64, ins->value()->type() == MIRType::Double); + } else { + MOZ_ASSERT(ins->value()->type() == MIRType::Int32); + } + + LUse elements = useRegister(ins->elements()); + LAllocation index = useRegisterOrConstant(ins->index()); + LAllocation value; + + // For byte arrays, the value has to be in a byte register on x86. + if (ins->isByteWrite()) + value = useByteOpRegisterOrNonDoubleConstant(ins->value()); + else + value = useRegisterOrNonDoubleConstant(ins->value()); + + // Optimization opportunity for atomics: on some platforms there + // is a store instruction that incorporates the necessary + // barriers, and we could use that instead of separate barrier and + // store instructions. See bug #1077027. + if (ins->requiresMemoryBarrier()) { + LMemoryBarrier* fence = new(alloc()) LMemoryBarrier(MembarBeforeStore); + add(fence, ins); + } + add(new(alloc()) LStoreUnboxedScalar(elements, index, value), ins); + if (ins->requiresMemoryBarrier()) { + LMemoryBarrier* fence = new(alloc()) LMemoryBarrier(MembarAfterStore); + add(fence, ins); + } +} + +void +LIRGenerator::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole* ins) +{ + MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + MOZ_ASSERT(ins->length()->type() == MIRType::Int32); + + if (ins->isFloatWrite()) { + MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float32, ins->value()->type() == MIRType::Float32); + MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float64, ins->value()->type() == MIRType::Double); + } else { + MOZ_ASSERT(ins->value()->type() == MIRType::Int32); + } + + LUse elements = useRegister(ins->elements()); + LAllocation length = useAnyOrConstant(ins->length()); + LAllocation index = useRegisterOrConstant(ins->index()); + LAllocation value; + + // For byte arrays, the value has to be in a byte register on x86. + if (ins->isByteWrite()) + value = useByteOpRegisterOrNonDoubleConstant(ins->value()); + else + value = useRegisterOrNonDoubleConstant(ins->value()); + add(new(alloc()) LStoreTypedArrayElementHole(elements, length, index, value), ins); +} + +void +LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot* ins) +{ + MDefinition* obj = ins->object(); + MOZ_ASSERT(obj->type() == MIRType::Object); + + MIRType type = ins->type(); + + if (type == MIRType::Value) { + LLoadFixedSlotV* lir = new(alloc()) LLoadFixedSlotV(useRegisterAtStart(obj)); + defineBox(lir, ins); + } else { + LLoadFixedSlotT* lir = new(alloc()) LLoadFixedSlotT(useRegisterForTypedLoad(obj, type)); + define(lir, ins); + } +} + +void +LIRGenerator::visitLoadFixedSlotAndUnbox(MLoadFixedSlotAndUnbox* ins) +{ + MDefinition* obj = ins->object(); + MOZ_ASSERT(obj->type() == MIRType::Object); + + LLoadFixedSlotAndUnbox* lir = new(alloc()) LLoadFixedSlotAndUnbox(useRegisterAtStart(obj)); + if (ins->fallible()) + assignSnapshot(lir, ins->bailoutKind()); + + define(lir, ins); +} + +void +LIRGenerator::visitStoreFixedSlot(MStoreFixedSlot* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + if (ins->value()->type() == MIRType::Value) { + LStoreFixedSlotV* lir = new(alloc()) LStoreFixedSlotV(useRegister(ins->object()), + useBox(ins->value())); + add(lir, ins); + } else { + LStoreFixedSlotT* lir = new(alloc()) LStoreFixedSlotT(useRegister(ins->object()), + useRegisterOrConstant(ins->value())); + add(lir, ins); + } +} + +void +LIRGenerator::visitGetNameCache(MGetNameCache* ins) +{ + MOZ_ASSERT(ins->envObj()->type() == MIRType::Object); + + // Set the performs-call flag so that we don't omit the overrecursed check. + // This is necessary because the cache can attach a scripted getter stub + // that calls this script recursively. + gen->setPerformsCall(); + + LGetNameCache* lir = new(alloc()) LGetNameCache(useRegister(ins->envObj())); + defineBox(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCallGetIntrinsicValue(MCallGetIntrinsicValue* ins) +{ + LCallGetIntrinsicValue* lir = new(alloc()) LCallGetIntrinsicValue(); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitGetPropertyCache(MGetPropertyCache* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + MDefinition* id = ins->idval(); + MOZ_ASSERT(id->type() == MIRType::String || + id->type() == MIRType::Symbol || + id->type() == MIRType::Int32 || + id->type() == MIRType::Value); + + if (ins->monitoredResult()) { + // Set the performs-call flag so that we don't omit the overrecursed + // check. This is necessary because the cache can attach a scripted + // getter stub that calls this script recursively. + gen->setPerformsCall(); + } + + // If this is a GETPROP, the id is a constant string. Allow passing it as a + // constant to reduce register allocation pressure. + bool useConstId = id->type() == MIRType::String || id->type() == MIRType::Symbol; + + if (ins->type() == MIRType::Value) { + LGetPropertyCacheV* lir = + new(alloc()) LGetPropertyCacheV(useRegister(ins->object()), + useBoxOrTypedOrConstant(id, useConstId)); + defineBox(lir, ins); + assignSafepoint(lir, ins); + } else { + LGetPropertyCacheT* lir = + new(alloc()) LGetPropertyCacheT(useRegister(ins->object()), + useBoxOrTypedOrConstant(id, useConstId)); + define(lir, ins); + assignSafepoint(lir, ins); + } +} + +void +LIRGenerator::visitGetPropertyPolymorphic(MGetPropertyPolymorphic* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + if (ins->type() == MIRType::Value) { + LGetPropertyPolymorphicV* lir = + new(alloc()) LGetPropertyPolymorphicV(useRegister(ins->object())); + assignSnapshot(lir, Bailout_ShapeGuard); + defineBox(lir, ins); + } else { + LDefinition maybeTemp = (ins->type() == MIRType::Double) ? temp() : LDefinition::BogusTemp(); + LGetPropertyPolymorphicT* lir = + new(alloc()) LGetPropertyPolymorphicT(useRegister(ins->object()), maybeTemp); + assignSnapshot(lir, Bailout_ShapeGuard); + define(lir, ins); + } +} + +void +LIRGenerator::visitSetPropertyPolymorphic(MSetPropertyPolymorphic* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + if (ins->value()->type() == MIRType::Value) { + LSetPropertyPolymorphicV* lir = + new(alloc()) LSetPropertyPolymorphicV(useRegister(ins->object()), + useBox(ins->value()), + temp()); + assignSnapshot(lir, Bailout_ShapeGuard); + add(lir, ins); + } else { + LAllocation value = useRegisterOrConstant(ins->value()); + LSetPropertyPolymorphicT* lir = + new(alloc()) LSetPropertyPolymorphicT(useRegister(ins->object()), value, + ins->value()->type(), temp()); + assignSnapshot(lir, Bailout_ShapeGuard); + add(lir, ins); + } +} + +void +LIRGenerator::visitBindNameCache(MBindNameCache* ins) +{ + MOZ_ASSERT(ins->environmentChain()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Object); + + LBindNameCache* lir = new(alloc()) LBindNameCache(useRegister(ins->environmentChain())); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCallBindVar(MCallBindVar* ins) +{ + MOZ_ASSERT(ins->environmentChain()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Object); + + LCallBindVar* lir = new(alloc()) LCallBindVar(useRegister(ins->environmentChain())); + define(lir, ins); +} + +void +LIRGenerator::visitGuardObjectIdentity(MGuardObjectIdentity* ins) +{ + LGuardObjectIdentity* guard = new(alloc()) LGuardObjectIdentity(useRegister(ins->object()), + useRegister(ins->expected())); + assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard); + add(guard, ins); + redefine(ins, ins->object()); +} + +void +LIRGenerator::visitGuardClass(MGuardClass* ins) +{ + LDefinition t = temp(); + LGuardClass* guard = new(alloc()) LGuardClass(useRegister(ins->object()), t); + assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard); + add(guard, ins); +} + +void +LIRGenerator::visitGuardObject(MGuardObject* ins) +{ + // The type policy does all the work, so at this point the input + // is guaranteed to be an object. + MOZ_ASSERT(ins->input()->type() == MIRType::Object); + redefine(ins, ins->input()); +} + +void +LIRGenerator::visitGuardString(MGuardString* ins) +{ + // The type policy does all the work, so at this point the input + // is guaranteed to be a string. + MOZ_ASSERT(ins->input()->type() == MIRType::String); + redefine(ins, ins->input()); +} + +void +LIRGenerator::visitGuardSharedTypedArray(MGuardSharedTypedArray* ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType::Object); + LGuardSharedTypedArray* guard = + new(alloc()) LGuardSharedTypedArray(useRegister(ins->object()), temp()); + assignSnapshot(guard, Bailout_NonSharedTypedArrayInput); + add(guard, ins); +} + +void +LIRGenerator::visitPolyInlineGuard(MPolyInlineGuard* ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType::Object); + redefine(ins, ins->input()); +} + +void +LIRGenerator::visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Object); + + LGuardReceiverPolymorphic* guard = + new(alloc()) LGuardReceiverPolymorphic(useRegister(ins->object()), temp()); + assignSnapshot(guard, Bailout_ShapeGuard); + add(guard, ins); + redefine(ins, ins->object()); +} + +void +LIRGenerator::visitGuardUnboxedExpando(MGuardUnboxedExpando* ins) +{ + LGuardUnboxedExpando* guard = + new(alloc()) LGuardUnboxedExpando(useRegister(ins->object())); + assignSnapshot(guard, ins->bailoutKind()); + add(guard, ins); + redefine(ins, ins->object()); +} + +void +LIRGenerator::visitLoadUnboxedExpando(MLoadUnboxedExpando* ins) +{ + LLoadUnboxedExpando* lir = + new(alloc()) LLoadUnboxedExpando(useRegisterAtStart(ins->object())); + define(lir, ins); +} + +void +LIRGenerator::visitAssertRange(MAssertRange* ins) +{ + MDefinition* input = ins->input(); + LInstruction* lir = nullptr; + + switch (input->type()) { + case MIRType::Boolean: + case MIRType::Int32: + lir = new(alloc()) LAssertRangeI(useRegisterAtStart(input)); + break; + + case MIRType::Double: + lir = new(alloc()) LAssertRangeD(useRegister(input), tempDouble()); + break; + + case MIRType::Float32: + lir = new(alloc()) LAssertRangeF(useRegister(input), tempDouble(), tempDouble()); + break; + + case MIRType::Value: + lir = new(alloc()) LAssertRangeV(useBox(input), tempToUnbox(), tempDouble(), tempDouble()); + break; + + default: + MOZ_CRASH("Unexpected Range for MIRType"); + break; + } + + lir->setMir(ins); + add(lir); +} + +void +LIRGenerator::visitCallGetProperty(MCallGetProperty* ins) +{ + LCallGetProperty* lir = new(alloc()) LCallGetProperty(useBoxAtStart(ins->value())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCallGetElement(MCallGetElement* ins) +{ + MOZ_ASSERT(ins->lhs()->type() == MIRType::Value); + MOZ_ASSERT(ins->rhs()->type() == MIRType::Value); + + LCallGetElement* lir = new(alloc()) LCallGetElement(useBoxAtStart(ins->lhs()), + useBoxAtStart(ins->rhs())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCallSetProperty(MCallSetProperty* ins) +{ + LInstruction* lir = new(alloc()) LCallSetProperty(useRegisterAtStart(ins->object()), + useBoxAtStart(ins->value())); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitDeleteProperty(MDeleteProperty* ins) +{ + LCallDeleteProperty* lir = new(alloc()) LCallDeleteProperty(useBoxAtStart(ins->value())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitDeleteElement(MDeleteElement* ins) +{ + LCallDeleteElement* lir = new(alloc()) LCallDeleteElement(useBoxAtStart(ins->value()), + useBoxAtStart(ins->index())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitSetPropertyCache(MSetPropertyCache* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + MDefinition* id = ins->idval(); + MOZ_ASSERT(id->type() == MIRType::String || + id->type() == MIRType::Symbol || + id->type() == MIRType::Int32 || + id->type() == MIRType::Value); + + // If this is a SETPROP, the id is a constant string. Allow passing it as a + // constant to reduce register allocation pressure. + bool useConstId = id->type() == MIRType::String || id->type() == MIRType::Symbol; + bool useConstValue = IsNonNurseryConstant(ins->value()); + + // Set the performs-call flag so that we don't omit the overrecursed check. + // This is necessary because the cache can attach a scripted setter stub + // that calls this script recursively. + gen->setPerformsCall(); + + // If the index might be an integer, we need some extra temp registers for + // the dense and typed array element stubs. + LDefinition tempToUnboxIndex = LDefinition::BogusTemp(); + LDefinition tempD = LDefinition::BogusTemp(); + LDefinition tempF32 = LDefinition::BogusTemp(); + + if (id->mightBeType(MIRType::Int32)) { + if (id->type() != MIRType::Int32) + tempToUnboxIndex = tempToUnbox(); + tempD = tempDouble(); + tempF32 = hasUnaliasedDouble() ? tempFloat32() : LDefinition::BogusTemp(); + } + + LInstruction* lir = + new(alloc()) LSetPropertyCache(useRegister(ins->object()), + useBoxOrTypedOrConstant(id, useConstId), + useBoxOrTypedOrConstant(ins->value(), useConstValue), + temp(), + tempToUnboxIndex, tempD, tempF32); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCallSetElement(MCallSetElement* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->index()->type() == MIRType::Value); + MOZ_ASSERT(ins->value()->type() == MIRType::Value); + + LCallSetElement* lir = new(alloc()) LCallSetElement(useRegisterAtStart(ins->object()), + useBoxAtStart(ins->index()), + useBoxAtStart(ins->value())); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCallInitElementArray(MCallInitElementArray* ins) +{ + LCallInitElementArray* lir = new(alloc()) LCallInitElementArray(useRegisterAtStart(ins->object()), + useBoxAtStart(ins->value())); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitIteratorStart(MIteratorStart* ins) +{ + if (ins->object()->type() == MIRType::Value) { + LCallIteratorStartV* lir = new(alloc()) LCallIteratorStartV(useBoxAtStart(ins->object())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); + return; + } + + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + + // Call a stub if this is not a simple for-in loop. + if (ins->flags() != JSITER_ENUMERATE) { + LCallIteratorStartO* lir = new(alloc()) LCallIteratorStartO(useRegisterAtStart(ins->object())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); + } else { + LIteratorStartO* lir = new(alloc()) LIteratorStartO(useRegister(ins->object()), temp(), temp(), temp()); + define(lir, ins); + assignSafepoint(lir, ins); + } +} + +void +LIRGenerator::visitIteratorMore(MIteratorMore* ins) +{ + LIteratorMore* lir = new(alloc()) LIteratorMore(useRegister(ins->iterator()), temp()); + defineBox(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitIsNoIter(MIsNoIter* ins) +{ + MOZ_ASSERT(ins->hasOneUse()); + emitAtUses(ins); +} + +void +LIRGenerator::visitIteratorEnd(MIteratorEnd* ins) +{ + LIteratorEnd* lir = new(alloc()) LIteratorEnd(useRegister(ins->iterator()), temp(), temp(), temp()); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitStringLength(MStringLength* ins) +{ + MOZ_ASSERT(ins->string()->type() == MIRType::String); + define(new(alloc()) LStringLength(useRegisterAtStart(ins->string())), ins); +} + +void +LIRGenerator::visitArgumentsLength(MArgumentsLength* ins) +{ + define(new(alloc()) LArgumentsLength(), ins); +} + +void +LIRGenerator::visitGetFrameArgument(MGetFrameArgument* ins) +{ + LGetFrameArgument* lir = new(alloc()) LGetFrameArgument(useRegisterOrConstant(ins->index())); + defineBox(lir, ins); +} + +void +LIRGenerator::visitNewTarget(MNewTarget* ins) +{ + LNewTarget* lir = new(alloc()) LNewTarget(); + defineBox(lir, ins); +} + +void +LIRGenerator::visitSetFrameArgument(MSetFrameArgument* ins) +{ + MDefinition* input = ins->input(); + + if (input->type() == MIRType::Value) { + LSetFrameArgumentV* lir = new(alloc()) LSetFrameArgumentV(useBox(input)); + add(lir, ins); + } else if (input->type() == MIRType::Undefined || input->type() == MIRType::Null) { + Value val = input->type() == MIRType::Undefined ? UndefinedValue() : NullValue(); + LSetFrameArgumentC* lir = new(alloc()) LSetFrameArgumentC(val); + add(lir, ins); + } else { + LSetFrameArgumentT* lir = new(alloc()) LSetFrameArgumentT(useRegister(input)); + add(lir, ins); + } +} + +void +LIRGenerator::visitRunOncePrologue(MRunOncePrologue* ins) +{ + LRunOncePrologue* lir = new(alloc()) LRunOncePrologue; + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitRest(MRest* ins) +{ + MOZ_ASSERT(ins->numActuals()->type() == MIRType::Int32); + + LRest* lir = new(alloc()) LRest(useFixedAtStart(ins->numActuals(), CallTempReg0), + tempFixed(CallTempReg1), + tempFixed(CallTempReg2), + tempFixed(CallTempReg3)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitThrow(MThrow* ins) +{ + MDefinition* value = ins->getOperand(0); + MOZ_ASSERT(value->type() == MIRType::Value); + + LThrow* lir = new(alloc()) LThrow(useBoxAtStart(value)); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitIn(MIn* ins) +{ + MDefinition* lhs = ins->lhs(); + MDefinition* rhs = ins->rhs(); + + MOZ_ASSERT(lhs->type() == MIRType::Value); + MOZ_ASSERT(rhs->type() == MIRType::Object); + + LIn* lir = new(alloc()) LIn(useBoxAtStart(lhs), useRegisterAtStart(rhs)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitInstanceOf(MInstanceOf* ins) +{ + MDefinition* lhs = ins->getOperand(0); + + MOZ_ASSERT(lhs->type() == MIRType::Value || lhs->type() == MIRType::Object); + + if (lhs->type() == MIRType::Object) { + LInstanceOfO* lir = new(alloc()) LInstanceOfO(useRegister(lhs)); + define(lir, ins); + assignSafepoint(lir, ins); + } else { + LInstanceOfV* lir = new(alloc()) LInstanceOfV(useBox(lhs)); + define(lir, ins); + assignSafepoint(lir, ins); + } +} + +void +LIRGenerator::visitCallInstanceOf(MCallInstanceOf* ins) +{ + MDefinition* lhs = ins->lhs(); + MDefinition* rhs = ins->rhs(); + + MOZ_ASSERT(lhs->type() == MIRType::Value); + MOZ_ASSERT(rhs->type() == MIRType::Object); + + LCallInstanceOf* lir = new(alloc()) LCallInstanceOf(useBoxAtStart(lhs), + useRegisterAtStart(rhs)); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitIsCallable(MIsCallable* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Boolean); + define(new(alloc()) LIsCallable(useRegister(ins->object())), ins); +} + +void +LIRGenerator::visitIsConstructor(MIsConstructor* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Boolean); + define(new(alloc()) LIsConstructor(useRegister(ins->object())), ins); +} + +static bool +CanEmitIsObjectAtUses(MInstruction* ins) +{ + if (!ins->canEmitAtUses()) + return false; + + MUseIterator iter(ins->usesBegin()); + if (iter == ins->usesEnd()) + return false; + + MNode* node = iter->consumer(); + if (!node->isDefinition()) + return false; + + if (!node->toDefinition()->isTest()) + return false; + + iter++; + return iter == ins->usesEnd(); +} + +void +LIRGenerator::visitIsObject(MIsObject* ins) +{ + if (CanEmitIsObjectAtUses(ins)) { + emitAtUses(ins); + return; + } + + MDefinition* opd = ins->input(); + MOZ_ASSERT(opd->type() == MIRType::Value); + LIsObject* lir = new(alloc()) LIsObject(useBoxAtStart(opd)); + define(lir, ins); +} + +void +LIRGenerator::visitHasClass(MHasClass* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Boolean); + define(new(alloc()) LHasClass(useRegister(ins->object())), ins); +} + +void +LIRGenerator::visitWasmAddOffset(MWasmAddOffset* ins) +{ + MOZ_ASSERT(ins->base()->type() == MIRType::Int32); + MOZ_ASSERT(ins->type() == MIRType::Int32); + define(new(alloc()) LWasmAddOffset(useRegisterAtStart(ins->base())), ins); +} + +void +LIRGenerator::visitWasmBoundsCheck(MWasmBoundsCheck* ins) +{ + if (ins->isRedundant()) { + if (MOZ_LIKELY(!JitOptions.wasmAlwaysCheckBounds)) + return; + } + + MDefinition* input = ins->input(); + MOZ_ASSERT(input->type() == MIRType::Int32); + + auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(input)); + add(lir, ins); +} + +void +LIRGenerator::visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins) +{ + if (ins->type() == MIRType::Int64) + defineInt64(new(alloc()) LWasmLoadGlobalVarI64, ins); + else + define(new(alloc()) LWasmLoadGlobalVar, ins); +} + +void +LIRGenerator::visitWasmStoreGlobalVar(MWasmStoreGlobalVar* ins) +{ + MDefinition* value = ins->value(); + if (value->type() == MIRType::Int64) + add(new(alloc()) LWasmStoreGlobalVarI64(useInt64RegisterAtStart(value)), ins); + else + add(new(alloc()) LWasmStoreGlobalVar(useRegisterAtStart(value)), ins); +} + +void +LIRGenerator::visitWasmParameter(MWasmParameter* ins) +{ + ABIArg abi = ins->abi(); + if (abi.argInRegister()) { +#if defined(JS_NUNBOX32) + if (abi.isGeneralRegPair()) { + defineInt64Fixed(new(alloc()) LWasmParameterI64, ins, + LInt64Allocation(LAllocation(AnyRegister(abi.gpr64().high)), + LAllocation(AnyRegister(abi.gpr64().low)))); + return; + } +#endif + defineFixed(new(alloc()) LWasmParameter, ins, LAllocation(abi.reg())); + return; + } + if (ins->type() == MIRType::Int64) { + MOZ_ASSERT(!abi.argInRegister()); + defineInt64Fixed(new(alloc()) LWasmParameterI64, ins, +#if defined(JS_NUNBOX32) + LInt64Allocation(LArgument(abi.offsetFromArgBase() + INT64HIGH_OFFSET), + LArgument(abi.offsetFromArgBase() + INT64LOW_OFFSET)) +#else + LInt64Allocation(LArgument(abi.offsetFromArgBase())) +#endif + ); + } else { + MOZ_ASSERT(IsNumberType(ins->type()) || IsSimdType(ins->type())); + defineFixed(new(alloc()) LWasmParameter, ins, LArgument(abi.offsetFromArgBase())); + } +} + +void +LIRGenerator::visitWasmReturn(MWasmReturn* ins) +{ + MDefinition* rval = ins->getOperand(0); + + if (rval->type() == MIRType::Int64) { + LWasmReturnI64* lir = new(alloc()) LWasmReturnI64(useInt64Fixed(rval, ReturnReg64)); + + // Preserve the TLS pointer we were passed in `WasmTlsReg`. + MDefinition* tlsPtr = ins->getOperand(1); + lir->setOperand(INT64_PIECES, useFixed(tlsPtr, WasmTlsReg)); + + add(lir); + return; + } + + LWasmReturn* lir = new(alloc()) LWasmReturn; + if (rval->type() == MIRType::Float32) + lir->setOperand(0, useFixed(rval, ReturnFloat32Reg)); + else if (rval->type() == MIRType::Double) + lir->setOperand(0, useFixed(rval, ReturnDoubleReg)); + else if (IsSimdType(rval->type())) + lir->setOperand(0, useFixed(rval, ReturnSimd128Reg)); + else if (rval->type() == MIRType::Int32) + lir->setOperand(0, useFixed(rval, ReturnReg)); + else + MOZ_CRASH("Unexpected wasm return type"); + + // Preserve the TLS pointer we were passed in `WasmTlsReg`. + MDefinition* tlsPtr = ins->getOperand(1); + lir->setOperand(1, useFixed(tlsPtr, WasmTlsReg)); + + add(lir); +} + +void +LIRGenerator::visitWasmReturnVoid(MWasmReturnVoid* ins) +{ + auto* lir = new(alloc()) LWasmReturnVoid; + + // Preserve the TLS pointer we were passed in `WasmTlsReg`. + MDefinition* tlsPtr = ins->getOperand(0); + lir->setOperand(0, useFixed(tlsPtr, WasmTlsReg)); + + add(lir); +} + +void +LIRGenerator::visitWasmStackArg(MWasmStackArg* ins) +{ + if (ins->arg()->type() == MIRType::Int64) { + add(new(alloc()) LWasmStackArgI64(useInt64RegisterOrConstantAtStart(ins->arg())), ins); + } else if (IsFloatingPointType(ins->arg()->type()) || IsSimdType(ins->arg()->type())) { + MOZ_ASSERT(!ins->arg()->isEmittedAtUses()); + add(new(alloc()) LWasmStackArg(useRegisterAtStart(ins->arg())), ins); + } else { + add(new(alloc()) LWasmStackArg(useRegisterOrConstantAtStart(ins->arg())), ins); + } +} + +void +LIRGenerator::visitWasmCall(MWasmCall* ins) +{ + gen->setPerformsCall(); + + LAllocation* args = gen->allocate<LAllocation>(ins->numOperands()); + if (!args) { + gen->abort("Couldn't allocate for MWasmCall"); + return; + } + + for (unsigned i = 0; i < ins->numArgs(); i++) + args[i] = useFixedAtStart(ins->getOperand(i), ins->registerForArg(i)); + + if (ins->callee().isTable()) + args[ins->numArgs()] = useFixedAtStart(ins->getOperand(ins->numArgs()), WasmTableCallIndexReg); + + LInstruction* lir; + if (ins->type() == MIRType::Int64) + lir = new(alloc()) LWasmCallI64(args, ins->numOperands()); + else + lir = new(alloc()) LWasmCall(args, ins->numOperands()); + + if (ins->type() == MIRType::None) + add(lir, ins); + else + defineReturn(lir, ins); +} + +void +LIRGenerator::visitSetDOMProperty(MSetDOMProperty* ins) +{ + MDefinition* val = ins->value(); + + Register cxReg, objReg, privReg, valueReg; + GetTempRegForIntArg(0, 0, &cxReg); + GetTempRegForIntArg(1, 0, &objReg); + GetTempRegForIntArg(2, 0, &privReg); + GetTempRegForIntArg(3, 0, &valueReg); + + // Keep using GetTempRegForIntArg, since we want to make sure we + // don't clobber registers we're already using. + Register tempReg1, tempReg2; + GetTempRegForIntArg(4, 0, &tempReg1); + mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(5, 0, &tempReg2); + MOZ_ASSERT(ok, "How can we not have six temp registers?"); + + LSetDOMProperty* lir = new(alloc()) LSetDOMProperty(tempFixed(cxReg), + useFixedAtStart(ins->object(), objReg), + useBoxFixedAtStart(val, tempReg1, tempReg2), + tempFixed(privReg), + tempFixed(valueReg)); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitGetDOMProperty(MGetDOMProperty* ins) +{ + Register cxReg, objReg, privReg, valueReg; + GetTempRegForIntArg(0, 0, &cxReg); + GetTempRegForIntArg(1, 0, &objReg); + GetTempRegForIntArg(2, 0, &privReg); + mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(3, 0, &valueReg); + MOZ_ASSERT(ok, "How can we not have four temp registers?"); + LGetDOMProperty* lir = new(alloc()) LGetDOMProperty(tempFixed(cxReg), + useFixedAtStart(ins->object(), objReg), + tempFixed(privReg), + tempFixed(valueReg)); + + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitGetDOMMember(MGetDOMMember* ins) +{ + MOZ_ASSERT(ins->isDomMovable(), "Members had better be movable"); + // We wish we could assert that ins->domAliasSet() == JSJitInfo::AliasNone, + // but some MGetDOMMembers are for [Pure], not [Constant] properties, whose + // value can in fact change as a result of DOM setters and method calls. + MOZ_ASSERT(ins->domAliasSet() != JSJitInfo::AliasEverything, + "Member gets had better not alias the world"); + + MDefinition* obj = ins->object(); + MOZ_ASSERT(obj->type() == MIRType::Object); + + MIRType type = ins->type(); + + if (type == MIRType::Value) { + LGetDOMMemberV* lir = new(alloc()) LGetDOMMemberV(useRegisterAtStart(obj)); + defineBox(lir, ins); + } else { + LGetDOMMemberT* lir = new(alloc()) LGetDOMMemberT(useRegisterForTypedLoad(obj, type)); + define(lir, ins); + } +} + +void +LIRGenerator::visitRecompileCheck(MRecompileCheck* ins) +{ + LRecompileCheck* lir = new(alloc()) LRecompileCheck(temp()); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitSimdBox(MSimdBox* ins) +{ + MOZ_ASSERT(IsSimdType(ins->input()->type())); + LUse in = useRegister(ins->input()); + LSimdBox* lir = new(alloc()) LSimdBox(in, temp()); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitSimdUnbox(MSimdUnbox* ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType::Object); + MOZ_ASSERT(IsSimdType(ins->type())); + LUse in = useRegister(ins->input()); + LSimdUnbox* lir = new(alloc()) LSimdUnbox(in, temp()); + assignSnapshot(lir, Bailout_UnexpectedSimdInput); + define(lir, ins); +} + +void +LIRGenerator::visitSimdConstant(MSimdConstant* ins) +{ + MOZ_ASSERT(IsSimdType(ins->type())); + + switch (ins->type()) { + case MIRType::Int8x16: + case MIRType::Int16x8: + case MIRType::Int32x4: + case MIRType::Bool8x16: + case MIRType::Bool16x8: + case MIRType::Bool32x4: + define(new(alloc()) LSimd128Int(), ins); + break; + case MIRType::Float32x4: + define(new(alloc()) LSimd128Float(), ins); + break; + default: + MOZ_CRASH("Unknown SIMD kind when generating constant"); + } +} + +void +LIRGenerator::visitSimdConvert(MSimdConvert* ins) +{ + MOZ_ASSERT(IsSimdType(ins->type())); + MDefinition* input = ins->input(); + LUse use = useRegister(input); + if (ins->type() == MIRType::Int32x4) { + MOZ_ASSERT(input->type() == MIRType::Float32x4); + switch (ins->signedness()) { + case SimdSign::Signed: { + LFloat32x4ToInt32x4* lir = new(alloc()) LFloat32x4ToInt32x4(use, temp()); + if (!gen->compilingWasm()) + assignSnapshot(lir, Bailout_BoundsCheck); + define(lir, ins); + break; + } + case SimdSign::Unsigned: { + LFloat32x4ToUint32x4* lir = + new (alloc()) LFloat32x4ToUint32x4(use, temp(), temp(LDefinition::SIMD128INT)); + if (!gen->compilingWasm()) + assignSnapshot(lir, Bailout_BoundsCheck); + define(lir, ins); + break; + } + default: + MOZ_CRASH("Unexpected SimdConvert sign"); + } + } else if (ins->type() == MIRType::Float32x4) { + MOZ_ASSERT(input->type() == MIRType::Int32x4); + MOZ_ASSERT(ins->signedness() == SimdSign::Signed, "Unexpected SimdConvert sign"); + define(new(alloc()) LInt32x4ToFloat32x4(use), ins); + } else { + MOZ_CRASH("Unknown SIMD kind when generating constant"); + } +} + +void +LIRGenerator::visitSimdReinterpretCast(MSimdReinterpretCast* ins) +{ + MOZ_ASSERT(IsSimdType(ins->type()) && IsSimdType(ins->input()->type())); + MDefinition* input = ins->input(); + LUse use = useRegisterAtStart(input); + // :TODO: (Bug 1132894) We have to allocate a different register as redefine + // and/or defineReuseInput are not yet capable of reusing the same register + // with a different register type. + define(new(alloc()) LSimdReinterpretCast(use), ins); +} + +void +LIRGenerator::visitSimdAllTrue(MSimdAllTrue* ins) +{ + MDefinition* input = ins->input(); + MOZ_ASSERT(IsBooleanSimdType(input->type())); + + LUse use = useRegisterAtStart(input); + define(new(alloc()) LSimdAllTrue(use), ins); +} + +void +LIRGenerator::visitSimdAnyTrue(MSimdAnyTrue* ins) +{ + MDefinition* input = ins->input(); + MOZ_ASSERT(IsBooleanSimdType(input->type())); + + LUse use = useRegisterAtStart(input); + define(new(alloc()) LSimdAnyTrue(use), ins); +} + +void +LIRGenerator::visitSimdUnaryArith(MSimdUnaryArith* ins) +{ + MOZ_ASSERT(IsSimdType(ins->input()->type())); + MOZ_ASSERT(IsSimdType(ins->type())); + + // Cannot be at start, as the ouput is used as a temporary to store values. + LUse in = use(ins->input()); + + switch (ins->type()) { + case MIRType::Int8x16: + case MIRType::Bool8x16: + define(new (alloc()) LSimdUnaryArithIx16(in), ins); + break; + case MIRType::Int16x8: + case MIRType::Bool16x8: + define(new (alloc()) LSimdUnaryArithIx8(in), ins); + break; + case MIRType::Int32x4: + case MIRType::Bool32x4: + define(new (alloc()) LSimdUnaryArithIx4(in), ins); + break; + case MIRType::Float32x4: + define(new (alloc()) LSimdUnaryArithFx4(in), ins); + break; + default: + MOZ_CRASH("Unknown SIMD kind for unary operation"); + } +} + +void +LIRGenerator::visitSimdBinaryComp(MSimdBinaryComp* ins) +{ + MOZ_ASSERT(IsSimdType(ins->lhs()->type())); + MOZ_ASSERT(IsSimdType(ins->rhs()->type())); + MOZ_ASSERT(IsBooleanSimdType(ins->type())); + + if (ShouldReorderCommutative(ins->lhs(), ins->rhs(), ins)) + ins->reverse(); + + switch (ins->specialization()) { + case MIRType::Int8x16: { + MOZ_ASSERT(ins->signedness() == SimdSign::Signed); + LSimdBinaryCompIx16* add = new (alloc()) LSimdBinaryCompIx16(); + lowerForFPU(add, ins, ins->lhs(), ins->rhs()); + return; + } + case MIRType::Int16x8: { + MOZ_ASSERT(ins->signedness() == SimdSign::Signed); + LSimdBinaryCompIx8* add = new (alloc()) LSimdBinaryCompIx8(); + lowerForFPU(add, ins, ins->lhs(), ins->rhs()); + return; + } + case MIRType::Int32x4: { + MOZ_ASSERT(ins->signedness() == SimdSign::Signed); + LSimdBinaryCompIx4* add = new (alloc()) LSimdBinaryCompIx4(); + lowerForCompIx4(add, ins, ins->lhs(), ins->rhs()); + return; + } + case MIRType::Float32x4: { + MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); + LSimdBinaryCompFx4* add = new (alloc()) LSimdBinaryCompFx4(); + lowerForCompFx4(add, ins, ins->lhs(), ins->rhs()); + return; + } + default: + MOZ_CRASH("Unknown compare type when comparing values"); + } +} + +void +LIRGenerator::visitSimdBinaryBitwise(MSimdBinaryBitwise* ins) +{ + MOZ_ASSERT(IsSimdType(ins->lhs()->type())); + MOZ_ASSERT(IsSimdType(ins->rhs()->type())); + MOZ_ASSERT(IsSimdType(ins->type())); + + MDefinition* lhs = ins->lhs(); + MDefinition* rhs = ins->rhs(); + ReorderCommutative(&lhs, &rhs, ins); + LSimdBinaryBitwise* lir = new(alloc()) LSimdBinaryBitwise; + lowerForFPU(lir, ins, lhs, rhs); +} + +void +LIRGenerator::visitSimdShift(MSimdShift* ins) +{ + MOZ_ASSERT(IsIntegerSimdType(ins->type())); + MOZ_ASSERT(ins->lhs()->type() == ins->type()); + MOZ_ASSERT(ins->rhs()->type() == MIRType::Int32); + + LUse vector = useRegisterAtStart(ins->lhs()); + LAllocation value = useRegisterOrConstant(ins->rhs()); + // We need a temp register to mask the shift amount, but not if the shift + // amount is a constant. + LDefinition tempReg = value.isConstant() ? LDefinition::BogusTemp() : temp(); + LSimdShift* lir = new(alloc()) LSimdShift(vector, value, tempReg); + defineReuseInput(lir, ins, 0); +} + +void +LIRGenerator::visitLexicalCheck(MLexicalCheck* ins) +{ + MDefinition* input = ins->input(); + MOZ_ASSERT(input->type() == MIRType::Value); + LLexicalCheck* lir = new(alloc()) LLexicalCheck(useBox(input)); + assignSnapshot(lir, ins->bailoutKind()); + add(lir, ins); + redefine(ins, input); +} + +void +LIRGenerator::visitThrowRuntimeLexicalError(MThrowRuntimeLexicalError* ins) +{ + LThrowRuntimeLexicalError* lir = new(alloc()) LThrowRuntimeLexicalError(); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitGlobalNameConflictsCheck(MGlobalNameConflictsCheck* ins) +{ + LGlobalNameConflictsCheck* lir = new(alloc()) LGlobalNameConflictsCheck(); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitDebugger(MDebugger* ins) +{ + LDebugger* lir = new(alloc()) LDebugger(tempFixed(CallTempReg0), tempFixed(CallTempReg1)); + assignSnapshot(lir, Bailout_Debugger); + add(lir, ins); +} + +void +LIRGenerator::visitAtomicIsLockFree(MAtomicIsLockFree* ins) +{ + define(new(alloc()) LAtomicIsLockFree(useRegister(ins->input())), ins); +} + +void +LIRGenerator::visitCheckReturn(MCheckReturn* ins) +{ + MDefinition* retVal = ins->returnValue(); + MDefinition* thisVal = ins->thisValue(); + MOZ_ASSERT(retVal->type() == MIRType::Value); + MOZ_ASSERT(thisVal->type() == MIRType::Value); + + LCheckReturn* lir = new(alloc()) LCheckReturn(useBoxAtStart(retVal), useBoxAtStart(thisVal)); + assignSnapshot(lir, Bailout_BadDerivedConstructorReturn); + add(lir, ins); + redefine(ins, retVal); +} + +void +LIRGenerator::visitCheckIsObj(MCheckIsObj* ins) +{ + MDefinition* checkVal = ins->checkValue(); + MOZ_ASSERT(checkVal->type() == MIRType::Value); + + LCheckIsObj* lir = new(alloc()) LCheckIsObj(useBoxAtStart(checkVal)); + redefine(ins, checkVal); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins) +{ + MDefinition* checkVal = ins->checkValue(); + MOZ_ASSERT(checkVal->type() == MIRType::Value); + + LCheckObjCoercible* lir = new(alloc()) LCheckObjCoercible(useBoxAtStart(checkVal)); + redefine(ins, checkVal); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins) +{ + MDefinition* checkVal = ins->checkValue(); + MOZ_ASSERT(checkVal->type() == MIRType::Value); + + LDebugCheckSelfHosted* lir = new (alloc()) LDebugCheckSelfHosted(useBoxAtStart(checkVal)); + redefine(ins, checkVal); + add(lir, ins); + assignSafepoint(lir, ins); +} + +static void +SpewResumePoint(MBasicBlock* block, MInstruction* ins, MResumePoint* resumePoint) +{ + Fprinter& out = JitSpewPrinter(); + out.printf("Current resume point %p details:\n", (void*)resumePoint); + out.printf(" frame count: %u\n", resumePoint->frameCount()); + + if (ins) { + out.printf(" taken after: "); + ins->printName(out); + } else { + out.printf(" taken at block %d entry", block->id()); + } + out.printf("\n"); + + out.printf(" pc: %p (script: %p, offset: %d)\n", + (void*)resumePoint->pc(), + (void*)resumePoint->block()->info().script(), + int(resumePoint->block()->info().script()->pcToOffset(resumePoint->pc()))); + + for (size_t i = 0, e = resumePoint->numOperands(); i < e; i++) { + MDefinition* in = resumePoint->getOperand(i); + out.printf(" slot%u: ", (unsigned)i); + in->printName(out); + out.printf("\n"); + } +} + +bool +LIRGenerator::visitInstruction(MInstruction* ins) +{ + if (ins->isRecoveredOnBailout()) { + MOZ_ASSERT(!JitOptions.disableRecoverIns); + return true; + } + + if (!gen->ensureBallast()) + return false; + ins->accept(this); + + if (ins->possiblyCalls()) + gen->setPerformsCall(); + + if (ins->resumePoint()) + updateResumeState(ins); + +#ifdef DEBUG + ins->setInWorklistUnchecked(); +#endif + + // If no safepoint was created, there's no need for an OSI point. + if (LOsiPoint* osiPoint = popOsiPoint()) + add(osiPoint); + + return !gen->errored(); +} + +void +LIRGenerator::definePhis() +{ + size_t lirIndex = 0; + MBasicBlock* block = current->mir(); + for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) { + if (phi->type() == MIRType::Value) { + defineUntypedPhi(*phi, lirIndex); + lirIndex += BOX_PIECES; + } else if (phi->type() == MIRType::Int64) { + defineInt64Phi(*phi, lirIndex); + lirIndex += INT64_PIECES; + } else { + defineTypedPhi(*phi, lirIndex); + lirIndex += 1; + } + } +} + +void +LIRGenerator::updateResumeState(MInstruction* ins) +{ + lastResumePoint_ = ins->resumePoint(); + if (JitSpewEnabled(JitSpew_IonSnapshots) && lastResumePoint_) + SpewResumePoint(nullptr, ins, lastResumePoint_); +} + +void +LIRGenerator::updateResumeState(MBasicBlock* block) +{ + // As Value Numbering phase can remove edges from the entry basic block to a + // code paths reachable from the OSR entry point, we have to add fixup + // blocks to keep the dominator tree organized the same way. These fixup + // blocks are flaged as unreachable, and should only exist iff the graph has + // an OSR block. + // + // Note: RangeAnalysis can flag blocks as unreachable, but they are only + // removed iff GVN (including UCE) is enabled. + MOZ_ASSERT_IF(!mir()->compilingWasm() && !block->unreachable(), block->entryResumePoint()); + MOZ_ASSERT_IF(block->unreachable(), block->graph().osrBlock() || + !mir()->optimizationInfo().gvnEnabled()); + lastResumePoint_ = block->entryResumePoint(); + if (JitSpewEnabled(JitSpew_IonSnapshots) && lastResumePoint_) + SpewResumePoint(block, nullptr, lastResumePoint_); +} + +bool +LIRGenerator::visitBlock(MBasicBlock* block) +{ + current = block->lir(); + updateResumeState(block); + + definePhis(); + + // See fixup blocks added by Value Numbering, to keep the dominator relation + // modified by the presence of the OSR block. + MOZ_ASSERT_IF(block->unreachable(), *block->begin() == block->lastIns() || + !mir()->optimizationInfo().gvnEnabled()); + MOZ_ASSERT_IF(block->unreachable(), block->graph().osrBlock() || + !mir()->optimizationInfo().gvnEnabled()); + for (MInstructionIterator iter = block->begin(); *iter != block->lastIns(); iter++) { + if (!visitInstruction(*iter)) + return false; + } + + if (block->successorWithPhis()) { + // If we have a successor with phis, lower the phi input now that we + // are approaching the join point. + MBasicBlock* successor = block->successorWithPhis(); + uint32_t position = block->positionInPhiSuccessor(); + size_t lirIndex = 0; + for (MPhiIterator phi(successor->phisBegin()); phi != successor->phisEnd(); phi++) { + if (!gen->ensureBallast()) + return false; + + MDefinition* opd = phi->getOperand(position); + ensureDefined(opd); + + MOZ_ASSERT(opd->type() == phi->type()); + + if (phi->type() == MIRType::Value) { + lowerUntypedPhiInput(*phi, position, successor->lir(), lirIndex); + lirIndex += BOX_PIECES; + } else if (phi->type() == MIRType::Int64) { + lowerInt64PhiInput(*phi, position, successor->lir(), lirIndex); + lirIndex += INT64_PIECES; + } else { + lowerTypedPhiInput(*phi, position, successor->lir(), lirIndex); + lirIndex += 1; + } + } + } + + // Now emit the last instruction, which is some form of branch. + if (!visitInstruction(block->lastIns())) + return false; + + return true; +} + +void +LIRGenerator::visitNaNToZero(MNaNToZero *ins) +{ + MDefinition* input = ins->input(); + + if (ins->operandIsNeverNaN() && ins->operandIsNeverNegativeZero()) { + redefine(ins, input); + return; + } + LNaNToZero* lir = new(alloc()) LNaNToZero(useRegisterAtStart(input), tempDouble()); + defineReuseInput(lir, ins, 0); +} + +bool +LIRGenerator::generate() +{ + // Create all blocks and prep all phis beforehand. + for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) { + if (gen->shouldCancel("Lowering (preparation loop)")) + return false; + + if (!lirGraph_.initBlock(*block)) + return false; + } + + for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) { + if (gen->shouldCancel("Lowering (main loop)")) + return false; + + if (!visitBlock(*block)) + return false; + } + + lirGraph_.setArgumentSlotCount(maxargslots_); + return true; +} + +void +LIRGenerator::visitPhi(MPhi* phi) +{ + // Phi nodes are not lowered because they are only meaningful for the register allocator. + MOZ_CRASH("Unexpected Phi node during Lowering."); +} + +void +LIRGenerator::visitBeta(MBeta* beta) +{ + // Beta nodes are supposed to be removed before because they are + // only used to carry the range information for Range analysis + MOZ_CRASH("Unexpected Beta node during Lowering."); +} + +void +LIRGenerator::visitObjectState(MObjectState* objState) +{ + // ObjectState nodes are always recovered on bailouts + MOZ_CRASH("Unexpected ObjectState node during Lowering."); +} + +void +LIRGenerator::visitArrayState(MArrayState* objState) +{ + // ArrayState nodes are always recovered on bailouts + MOZ_CRASH("Unexpected ArrayState node during Lowering."); +} + +void +LIRGenerator::visitUnknownValue(MUnknownValue* ins) +{ + MOZ_CRASH("Can not lower unknown value."); +} |