| /* -*- 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 "jsobjinlines.h" |
| #include "jsopcodeinlines.h" |
| |
| #include "jit/shared/Lowering-shared-inl.h" |
| |
| using namespace js; |
| using namespace jit; |
| |
| using mozilla::DebugOnly; |
| using JS::GenericNaN; |
| |
| void |
| LIRGenerator::useBoxAtStart(LInstruction* lir, size_t n, MDefinition* mir, LUse::Policy policy) |
| { |
| return useBox(lir, n, mir, policy, true); |
| } |
| |
| void |
| LIRGenerator::useBoxFixedAtStart(LInstruction* lir, size_t n, MDefinition* mir, ValueOperand op) |
| { |
| #if defined(JS_NUNBOX32) |
| return useBoxFixed(lir, n, mir, op.typeReg(), op.payloadReg(), true); |
| #elif defined(JS_PUNBOX64) |
| return useBoxFixed(lir, n, mir, op.valueReg(), op.scratchReg(), true); |
| #endif |
| } |
| |
| 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 defined(IS_BIG_ENDIAN) |
| ins->getDef(0)->setOutput(LArgument(offset)); |
| ins->getDef(1)->setOutput(LArgument(offset + 4)); |
| # else |
| ins->getDef(0)->setOutput(LArgument(offset + 4)); |
| ins->getDef(1)->setOutput(LArgument(offset)); |
| # endif |
| #elif defined(JS_PUNBOX64) |
| ins->getDef(0)->setOutput(LArgument(offset)); |
| #endif |
| } |
| |
| void |
| LIRGenerator::visitCallee(MCallee* ins) |
| { |
| define(new(alloc()) LCallee(), ins); |
| } |
| |
| void |
| LIRGenerator::visitIsConstructing(MIsConstructing* ins) |
| { |
| define(new(alloc()) LIsConstructing(), ins); |
| } |
| |
| void |
| LIRGenerator::visitGoto(MGoto* ins) |
| { |
| add(new(alloc()) LGoto(ins->target())); |
| } |
| |
| void |
| LIRGenerator::visitTableSwitch(MTableSwitch* tableswitch) |
| { |
| MDefinition* opd = tableswitch->getOperand(0); |
| |
| // There should be at least 1 successor. The default case! |
| MOZ_ASSERT(tableswitch->numSuccessors() > 0); |
| |
| // If there are no cases, the default case is always taken. |
| if (tableswitch->numSuccessors() == 1) { |
| add(new(alloc()) LGoto(tableswitch->getDefault())); |
| return; |
| } |
| |
| // If we don't know the type. |
| if (opd->type() == MIRType_Value) { |
| LTableSwitchV* lir = newLTableSwitchV(tableswitch); |
| useBox(lir, LTableSwitchV::InputValue, opd); |
| 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->scopeChain())); |
| 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) |
| { |
| LDefFun* lir = new(alloc()) LDefFun(useRegisterAtStart(ins->scopeChain())); |
| 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::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::visitNewDeclEnvObject(MNewDeclEnvObject* ins) |
| { |
| LNewDeclEnvObject* lir = new(alloc()) LNewDeclEnvObject(temp()); |
| define(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitNewCallObject(MNewCallObject* ins) |
| { |
| LInstruction* lir; |
| if (ins->templateObject()->isSingleton()) { |
| LNewSingletonCallObject* singletonLir = new(alloc()) LNewSingletonCallObject(temp()); |
| define(singletonLir, ins); |
| lir = singletonLir; |
| } else { |
| LNewCallObject* normalLir = new(alloc()) LNewCallObject(temp()); |
| define(normalLir, ins); |
| lir = normalLir; |
| } |
| |
| assignSafepoint(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitNewRunOnceCallObject(MNewRunOnceCallObject* 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(lir, LInitElem::IdIndex, ins->getId()); |
| useBoxAtStart(lir, LInitElem::ValueIndex, ins->getValue()); |
| add(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitInitElemGetterSetter(MInitElemGetterSetter* ins) |
| { |
| LInitElemGetterSetter* lir = |
| new(alloc()) LInitElemGetterSetter(useRegisterAtStart(ins->object()), |
| useRegisterAtStart(ins->value())); |
| useBoxAtStart(lir, LInitElemGetterSetter::IdIndex, ins->idValue()); |
| add(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitMutateProto(MMutateProto* ins) |
| { |
| LMutateProto* lir = new(alloc()) LMutateProto(useRegisterAtStart(ins->getObject())); |
| useBoxAtStart(lir, LMutateProto::ValueIndex, ins->getValue()); |
| add(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitInitProp(MInitProp* ins) |
| { |
| LInitProp* lir = new(alloc()) LInitProp(useRegisterAtStart(ins->getObject())); |
| useBoxAtStart(lir, LInitProp::ValueIndex, 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 = useRegisterAtStart(ins->getCallObject()); |
| LAllocation callObj = useFixed(ins->getCallObject(), CallTempReg0); |
| LCreateArgumentsObject* lir = new(alloc()) LCreateArgumentsObject(callObj, tempFixed(CallTempReg1)); |
| 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, temp()); |
| useBox(lir, LSetArgumentsObjectArg::ValueIndex, ins->getValue()); |
| add(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitReturnFromCtor(MReturnFromCtor* ins) |
| { |
| LReturnFromCtor* lir = new(alloc()) LReturnFromCtor(useRegister(ins->getObject())); |
| useBox(lir, LReturnFromCtor::ValueIndex, ins->getValue()); |
| define(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitComputeThis(MComputeThis* ins) |
| { |
| MOZ_ASSERT(ins->type() == MIRType_Value); |
| MOZ_ASSERT(ins->input()->type() == MIRType_Value); |
| |
| LComputeThis* lir = new(alloc()) LComputeThis(); |
| |
| // 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. |
| useBox(lir, LComputeThis::ValueIndex, 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); |
| } |
| |
| void |
| 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(stack, 0, 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); |
| } |
| } |
| } |
| |
| void |
| LIRGenerator::visitCall(MCall* call) |
| { |
| MOZ_ASSERT(CallTempReg0 != CallTempReg1); |
| MOZ_ASSERT(CallTempReg0 != ArgumentsRectifierReg); |
| MOZ_ASSERT(CallTempReg1 != ArgumentsRectifierReg); |
| MOZ_ASSERT(call->getFunction()->type() == MIRType_Object); |
| |
| lowerCallArguments(call); |
| |
| // Height of the current argument vector. |
| JSFunction* 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(useFixed(call->getFunction(), CallTempReg0), |
| tempFixed(CallTempReg2)); |
| } |
| } else { |
| // Call anything, using the most generic code. |
| lir = new(alloc()) LCallGeneric(useFixed(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( |
| useFixed(apply->getFunction(), CallTempReg3), |
| useFixed(apply->getArgc(), CallTempReg0), |
| tempFixed(CallTempReg1), // object register |
| tempFixed(CallTempReg2)); // stack counter register |
| |
| MDefinition* self = apply->getThis(); |
| useBoxFixed(lir, LApplyArgsGeneric::ThisIndex, self, CallTempReg4, CallTempReg5); |
| |
| // Bailout is only needed in the case of possible non-JSFunction callee. |
| if (!apply->getSingleTarget()) |
| 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), |
| tempFixed(CallTempReg1), // object register |
| tempFixed(CallTempReg2)); // stack counter register |
| |
| MDefinition* self = apply->getThis(); |
| useBoxFixed(lir, LApplyArrayGeneric::ThisIndex, self, CallTempReg4, CallTempReg5); |
| |
| // 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* scopeChain = ins->getScopeChain(); |
| MOZ_ASSERT(scopeChain->type() == MIRType_Object); |
| |
| MDefinition* name = ins->getName(); |
| MOZ_ASSERT(name->type() == MIRType_String); |
| |
| LGetDynamicName* lir = new(alloc()) LGetDynamicName(useFixed(scopeChain, CallTempReg0), |
| useFixed(name, CallTempReg1), |
| tempFixed(CallTempReg2), |
| tempFixed(CallTempReg3), |
| tempFixed(CallTempReg4)); |
| |
| assignSnapshot(lir, Bailout_DynamicNameNotFound); |
| defineReturn(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitCallDirectEval(MCallDirectEval* ins) |
| { |
| MDefinition* scopeChain = ins->getScopeChain(); |
| MOZ_ASSERT(scopeChain->type() == MIRType_Object); |
| |
| MDefinition* string = ins->getString(); |
| MOZ_ASSERT(string->type() == MIRType_String); |
| |
| MDefinition* newTargetValue = ins->getNewTargetValue(); |
| |
| LInstruction* lir = new(alloc()) LCallDirectEval(useRegisterAtStart(scopeChain), |
| useRegisterAtStart(string)); |
| useBoxAtStart(lir, LCallDirectEval::NewTarget, newTargetValue); |
| |
| defineReturn(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| |
| static JSOp |
| ReorderComparison(JSOp op, MDefinition** lhsp, MDefinition** rhsp) |
| { |
| MDefinition* lhs = *lhsp; |
| MDefinition* rhs = *rhsp; |
| |
| if (lhs->isConstantValue()) { |
| *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 (opd->isConstantValue() && !opd->constantValue().isMagic()) { |
| bool result = opd->constantToBoolean(); |
| add(new(alloc()) LGoto(result ? 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, tempDouble(), temp0, temp1); |
| useBox(lir, LTestVAndBranch::Input, opd); |
| 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, |
| tmp, tmpToUnbox); |
| useBox(lir, LIsNullOrLikeUndefinedAndBranchV::Value, left); |
| 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); |
| |
| LAllocation rhs = useRegisterOrConstant(right); |
| LCompareBAndBranch* lir = new(alloc()) LCompareBAndBranch(comp, rhs, ifTrue, ifFalse); |
| useBox(lir, LCompareBAndBranch::Lhs, left); |
| 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 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(lir, LCompareBitwiseAndBranch::LhsInput, left); |
| useBoxAtStart(lir, LCompareBitwiseAndBranch::RhsInput, 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(lir, LIsObjectAndBranch::Input, 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(lir, LIsNoIterAndBranch::Input, 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; |
| 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(useRegister(right), tempToUnbox()); |
| useBox(lir, LCompareStrictS::Lhs, left); |
| 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(lir, LCompareVM::LhsInput, left); |
| useBoxAtStart(lir, LCompareVM::RhsInput, 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(tmp, tmpToUnbox); |
| useBox(lir, LIsNullOrLikeUndefinedV::Value, left); |
| 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(useRegisterOrConstant(right)); |
| useBox(lir, LCompareB::Lhs, left); |
| 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 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(lir, LCompareBitwise::LhsInput, left); |
| useBoxAtStart(lir, LCompareBitwise::RhsInput, 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 && rhs->type() == MIRType_Int32) { |
| ReorderCommutative(&lhs, &rhs, ins); |
| lowerForALU(new(alloc()) LBitOpI(op), ins, lhs, rhs); |
| return; |
| } |
| |
| LBitOpV* lir = new(alloc()) LBitOpV(op); |
| useBoxAtStart(lir, LBitOpV::LhsInput, lhs); |
| useBoxAtStart(lir, LBitOpV::RhsInput, 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(tempToUnbox()); |
| useBox(lir, LTypeOfV::Input, opd); |
| define(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitToId(MToId* ins) |
| { |
| LToIdV* lir = new(alloc()) LToIdV(tempDouble()); |
| useBox(lir, LToIdV::Index, ins->input()); |
| 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(lir, LBitNotV::Input, 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 && 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; |
| } |
| |
| 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(lir, LBitOpV::LhsInput, lhs); |
| useBoxAtStart(lir, LBitOpV::RhsInput, 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::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(); |
| |
| LClzI* lir = new(alloc()) LClzI(useRegisterAtStart(num)); |
| define(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), useFixed(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_Double) { |
| MOZ_ASSERT(lhs->type() == MIRType_Double); |
| ReorderCommutative(&lhs, &rhs, ins); |
| lowerForFPU(new(alloc()) LMathD(JSOP_ADD), ins, lhs, rhs); |
| } else if (ins->specialization() == MIRType_Float32) { |
| MOZ_ASSERT(lhs->type() == MIRType_Float32); |
| ReorderCommutative(&lhs, &rhs, ins); |
| lowerForFPU(new(alloc()) LMathF(JSOP_ADD), ins, lhs, rhs); |
| } else { |
| 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_Double) { |
| MOZ_ASSERT(lhs->type() == MIRType_Double); |
| lowerForFPU(new(alloc()) LMathD(JSOP_SUB), ins, lhs, rhs); |
| } else if (ins->specialization() == MIRType_Float32) { |
| MOZ_ASSERT(lhs->type() == MIRType_Float32); |
| lowerForFPU(new(alloc()) LMathF(JSOP_SUB), ins, lhs, rhs); |
| } else { |
| 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->isConstantValue() && rhs->constantValue() == Int32Value(-1)) |
| defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(lhs)), ins, 0); |
| else |
| lowerMulI(ins, lhs, rhs); |
| } else 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 (rhs->isConstantValue() && rhs->constantValue() == DoubleValue(-1.0)) |
| defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(lhs)), ins, 0); |
| else |
| lowerForFPU(new(alloc()) LMathD(JSOP_MUL), ins, lhs, rhs); |
| } else if (ins->specialization() == MIRType_Float32) { |
| MOZ_ASSERT(lhs->type() == MIRType_Float32); |
| ReorderCommutative(&lhs, &rhs, ins); |
| |
| // We apply the same optimizations as for doubles |
| if (rhs->isConstantValue() && rhs->constantValue() == Float32Value(-1.0f)) |
| defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(lhs)), ins, 0); |
| else |
| lowerForFPU(new(alloc()) LMathF(JSOP_MUL), ins, lhs, rhs); |
| } else { |
| 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); |
| } else if (ins->specialization() == MIRType_Double) { |
| MOZ_ASSERT(lhs->type() == MIRType_Double); |
| lowerForFPU(new(alloc()) LMathD(JSOP_DIV), ins, lhs, rhs); |
| } else if (ins->specialization() == MIRType_Float32) { |
| MOZ_ASSERT(lhs->type() == MIRType_Float32); |
| lowerForFPU(new(alloc()) LMathF(JSOP_DIV), ins, lhs, rhs); |
| } else { |
| 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); |
| } else 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); |
| } else { |
| 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(lir, LBinaryV::LhsInput, lhs); |
| useBoxAtStart(lir, LBinaryV::RhsInput, 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::visitStart(MStart* start) |
| { |
| // Create a snapshot that captures the initial state of the function. |
| LStart* lir = new(alloc()) LStart; |
| assignSnapshot(lir, Bailout_InitialState); |
| |
| if (start->startType() == MStart::StartType_Default && lir->snapshot()) |
| 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::visitOsrScopeChain(MOsrScopeChain* object) |
| { |
| LOsrScopeChain* lir = new(alloc()) LOsrScopeChain(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(lir, LValueToDouble::Input, 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); |
| /* 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(lir, LValueToFloat32::Input, 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); |
| /* 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(tempDouble(), temp(), LValueToInt32::NORMAL); |
| useBox(lir, LValueToInt32::Input, opd); |
| 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(tempDouble(), temp(), |
| LValueToInt32::TRUNCATE); |
| useBox(lir, LValueToInt32::Input, opd); |
| 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::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(tempToUnbox()); |
| useBox(lir, LValueToString::Input, opd); |
| 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(lir, LValueToString::Input, ins->input()); |
| define(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| |
| static bool |
| MustCloneRegExpForCall(MCall* call, uint32_t useIndex) |
| { |
| // We have a regex literal flowing into a call. Return |false| iff |
| // this is a native call that does not let the regex escape. |
| |
| JSFunction* target = call->getSingleTarget(); |
| if (!target || !target->isNative()) |
| return true; |
| |
| if (useIndex == MCall::IndexOfThis() && |
| (target->native() == regexp_exec || target->native() == regexp_test)) |
| { |
| return false; |
| } |
| |
| if (useIndex == MCall::IndexOfArgument(0) && |
| (target->native() == str_split || |
| target->native() == str_replace || |
| target->native() == str_match || |
| target->native() == str_search)) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| static bool |
| MustCloneRegExp(MRegExp* regexp) |
| { |
| if (regexp->mustClone()) |
| return true; |
| |
| // If this regex literal only flows into known natives that don't let |
| // it escape, we don't have to clone it. |
| |
| for (MUseIterator iter(regexp->usesBegin()); iter != regexp->usesEnd(); iter++) { |
| MNode* node = iter->consumer(); |
| if (!node->isDefinition()) |
| return true; |
| |
| MDefinition* def = node->toDefinition(); |
| if (def->isRegExpTest()) { |
| MRegExpTest* test = def->toRegExpTest(); |
| if (test->indexOf(*iter) == 1) { |
| // Optimized RegExp.prototype.test. |
| MOZ_ASSERT(test->regexp() == regexp); |
| continue; |
| } |
| } else if (def->isCall()) { |
| MCall* call = def->toCall(); |
| if (!MustCloneRegExpForCall(call, call->indexOf(*iter))) |
| continue; |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| |
| void |
| LIRGenerator::visitRegExp(MRegExp* ins) |
| { |
| if (!MustCloneRegExp(ins)) { |
| RegExpObject* source = ins->source(); |
| define(new(alloc()) LPointer(source), ins); |
| } else { |
| LRegExp* lir = new(alloc()) LRegExp(); |
| defineReturn(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| } |
| |
| void |
| LIRGenerator::visitRegExpExec(MRegExpExec* ins) |
| { |
| MOZ_ASSERT(ins->regexp()->type() == MIRType_Object); |
| MOZ_ASSERT(ins->string()->type() == MIRType_String); |
| |
| LRegExpExec* lir = new(alloc()) LRegExpExec(useFixedAtStart(ins->regexp(), CallTempReg0), |
| useFixedAtStart(ins->string(), CallTempReg1)); |
| defineReturn(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitRegExpTest(MRegExpTest* ins) |
| { |
| MOZ_ASSERT(ins->regexp()->type() == MIRType_Object); |
| MOZ_ASSERT(ins->string()->type() == MIRType_String); |
| |
| LRegExpTest* lir = new(alloc()) LRegExpTest(useFixedAtStart(ins->regexp(), CallTempReg2), |
| useFixedAtStart(ins->string(), CallTempReg3)); |
| defineReturn(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitRegExpReplace(MRegExpReplace* ins) |
| { |
| MOZ_ASSERT(ins->pattern()->type() == MIRType_Object); |
| MOZ_ASSERT(ins->string()->type() == MIRType_String); |
| MOZ_ASSERT(ins->replacement()->type() == MIRType_String); |
| |
| LRegExpReplace* lir = new(alloc()) LRegExpReplace(useRegisterOrConstantAtStart(ins->string()), |
| useRegisterAtStart(ins->pattern()), |
| useRegisterOrConstantAtStart(ins->replacement())); |
| defineReturn(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(lir, LBinarySharedStub::LhsInput, lhs, R0); |
| useBoxFixedAtStart(lir, LBinarySharedStub::RhsInput, 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(lir, LUnarySharedStub::Input, input, R0); |
| |
| 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->scopeChain())); |
| defineReturn(lir, ins); |
| assignSafepoint(lir, ins); |
| } else { |
| LLambda* lir = new(alloc()) LLambda(useRegister(ins->scopeChain()), temp()); |
| define(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| } |
| |
| void |
| LIRGenerator::visitLambdaArrow(MLambdaArrow* ins) |
| { |
| MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object); |
| MOZ_ASSERT(ins->newTargetDef()->type() == MIRType_Value); |
| |
| LLambdaArrow* lir = new(alloc()) LLambdaArrow(useRegister(ins->scopeChain())); |
| useBox(lir, LLambdaArrow::NewTargetValue, 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) |
| { |
| // Implicit interrupt checks require asm.js signal handlers to be installed. |
| // They also require writable JIT code: reprotecting in patchIonBackedges |
| // would be expensive and using AutoWritableJitCode in the signal handler |
| // is complicated because there could be another AutoWritableJitCode on the |
| // stack. |
| LInstructionHelper<0, 0, 0>* lir; |
| if (GetJitContext()->runtime->canUseSignalHandlers() && |
| !ExecutableAllocator::nonWritableJitCode) |
| { |
| lir = new(alloc()) LInterruptCheckImplicit(); |
| } else { |
| lir = new(alloc()) LInterruptCheck(); |
| } |
| add(lir, ins); |
| assignSafepoint(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins) |
| { |
| gen->setPerformsCall(); |
| |
| LAsmJSInterruptCheck* lir = new(alloc()) LAsmJSInterruptCheck(ins->interruptExit(), |
| ins->funcDesc()); |
| add(lir, ins); |
| } |
| |
| void |
| LIRGenerator::visitStoreSlot(MStoreSlot* ins) |
| { |
| LInstruction* lir; |
| |
| switch (ins->value()->type()) { |
| case MIRType_Value: |
| lir = new(alloc()) LStoreSlotV(useRegister(ins->slots())); |
| useBox(lir, LStoreSlotV::Value, 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(); |
| DebugOnly<MIRType> outputType = ins->type(); |
| |
| MOZ_ASSERT(inputType == outputType); |
| |
| // 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(tmp); |
| useBox(barrier, LTypeBarrierV::Input, ins->input()); |
| 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(tmp); |
| useBox(lir, LMonitorTypes::Input, ins->input()); |
| 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()->value(); |
| return !v.isMarkable() || !IsInsideNursery(v.toGCThing()); |
| } |
| |
| void |
| LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins) |
| { |
| MOZ_ASSERT(ins->object()->type() == MIRType_Object); |
| |
| // LPostWriteBarrier assumes that if it has a constant object then that |
| // object is tenured, and does not need to be tested for being in the |
| // nursery. Ensure that assumption holds by lowering constant nursery |
| // objects to a register. |
| bool useConstantObject = IsNonNurseryConstant(ins->object()); |
| |
| switch (ins->value()->type()) { |
| case MIRType_Object: |
| 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()), |
| tmp); |
| useBox(lir, LPostWriteBarrierV::Input, ins->value()); |
| 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::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_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(tempDouble(), temp0, temp1); |
| useBox(lir, LNotV::Input, op); |
| define(lir, ins); |
| break; |
| } |
| |
| default: |
| MOZ_CRASH("Unexpected MIRType."); |
| } |
| } |
| |
| void |
| LIRGenerator::visitBoundsCheck(MBoundsCheck* ins) |
| { |
| 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) |
| { |
| 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); |
| if (ins->fallible()) |
| assignSnapshot(lir, Bailout_Hole); |
| useBox(lir, LStoreElementV::Value, ins->value()); |
| 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, tempDef); |
| useBox(lir, LStoreElementHoleV::Value, ins->value()); |
| break; |
| |
| default: |
| { |
| const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); |
| lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, tempDef); |
| break; |
| } |
| } |
| |
|