michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/Lowering.h" michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "jsanalyze.h" michael@0: michael@0: #include "jit/IonSpewer.h" michael@0: #include "jit/LIR.h" michael@0: #include "jit/MIR.h" michael@0: #include "jit/MIRGraph.h" michael@0: michael@0: #include "jsinferinlines.h" michael@0: #include "jsobjinlines.h" michael@0: #include "jsopcodeinlines.h" michael@0: michael@0: #include "jit/shared/Lowering-shared-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace jit; michael@0: michael@0: using mozilla::DebugOnly; michael@0: using JS::GenericNaN; michael@0: michael@0: bool michael@0: LIRGenerator::visitCloneLiteral(MCloneLiteral *ins) michael@0: { michael@0: JS_ASSERT(ins->type() == MIRType_Object); michael@0: JS_ASSERT(ins->input()->type() == MIRType_Object); michael@0: michael@0: LCloneLiteral *lir = new(alloc()) LCloneLiteral(useRegisterAtStart(ins->input())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitParameter(MParameter *param) michael@0: { michael@0: ptrdiff_t offset; michael@0: if (param->index() == MParameter::THIS_SLOT) michael@0: offset = THIS_FRAME_ARGSLOT; michael@0: else michael@0: offset = 1 + param->index(); michael@0: michael@0: LParameter *ins = new(alloc()) LParameter; michael@0: if (!defineBox(ins, param, LDefinition::PRESET)) michael@0: return false; michael@0: michael@0: offset *= sizeof(Value); michael@0: #if defined(JS_NUNBOX32) michael@0: # if defined(IS_BIG_ENDIAN) michael@0: ins->getDef(0)->setOutput(LArgument(offset)); michael@0: ins->getDef(1)->setOutput(LArgument(offset + 4)); michael@0: # else michael@0: ins->getDef(0)->setOutput(LArgument(offset + 4)); michael@0: ins->getDef(1)->setOutput(LArgument(offset)); michael@0: # endif michael@0: #elif defined(JS_PUNBOX64) michael@0: ins->getDef(0)->setOutput(LArgument(offset)); michael@0: #endif michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallee(MCallee *ins) michael@0: { michael@0: return define(new(alloc()) LCallee(), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGoto(MGoto *ins) michael@0: { michael@0: return add(new(alloc()) LGoto(ins->target())); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitTableSwitch(MTableSwitch *tableswitch) michael@0: { michael@0: MDefinition *opd = tableswitch->getOperand(0); michael@0: michael@0: // There should be at least 1 successor. The default case! michael@0: JS_ASSERT(tableswitch->numSuccessors() > 0); michael@0: michael@0: // If there are no cases, the default case is always taken. michael@0: if (tableswitch->numSuccessors() == 1) michael@0: return add(new(alloc()) LGoto(tableswitch->getDefault())); michael@0: michael@0: // If we don't know the type. michael@0: if (opd->type() == MIRType_Value) { michael@0: LTableSwitchV *lir = newLTableSwitchV(tableswitch); michael@0: if (!useBox(lir, LTableSwitchV::InputValue, opd)) michael@0: return false; michael@0: return add(lir); michael@0: } michael@0: michael@0: // Case indices are numeric, so other types will always go to the default case. michael@0: if (opd->type() != MIRType_Int32 && opd->type() != MIRType_Double) michael@0: return add(new(alloc()) LGoto(tableswitch->getDefault())); michael@0: michael@0: // Return an LTableSwitch, capable of handling either an integer or michael@0: // floating-point index. michael@0: LAllocation index; michael@0: LDefinition tempInt; michael@0: if (opd->type() == MIRType_Int32) { michael@0: index = useRegisterAtStart(opd); michael@0: tempInt = tempCopy(opd, 0); michael@0: } else { michael@0: index = useRegister(opd); michael@0: tempInt = temp(LDefinition::GENERAL); michael@0: } michael@0: return add(newLTableSwitch(index, tempInt, tableswitch)); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCheckOverRecursed(MCheckOverRecursed *ins) michael@0: { michael@0: LCheckOverRecursed *lir = new(alloc()) LCheckOverRecursed(); michael@0: michael@0: if (!add(lir, ins)) michael@0: return false; michael@0: if (!assignSafepoint(lir, ins)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCheckOverRecursedPar(MCheckOverRecursedPar *ins) michael@0: { michael@0: LCheckOverRecursedPar *lir = michael@0: new(alloc()) LCheckOverRecursedPar(useRegister(ins->forkJoinContext()), temp()); michael@0: if (!add(lir, ins)) michael@0: return false; michael@0: if (!assignSafepoint(lir, ins)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitDefVar(MDefVar *ins) michael@0: { michael@0: LDefVar *lir = new(alloc()) LDefVar(useRegisterAtStart(ins->scopeChain())); michael@0: if (!add(lir, ins)) michael@0: return false; michael@0: if (!assignSafepoint(lir, ins)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitDefFun(MDefFun *ins) michael@0: { michael@0: LDefFun *lir = new(alloc()) LDefFun(useRegisterAtStart(ins->scopeChain())); michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewSlots(MNewSlots *ins) michael@0: { michael@0: // No safepoint needed, since we don't pass a cx. michael@0: LNewSlots *lir = new(alloc()) LNewSlots(tempFixed(CallTempReg0), tempFixed(CallTempReg1), michael@0: tempFixed(CallTempReg2)); michael@0: if (!assignSnapshot(lir)) michael@0: return false; michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewArray(MNewArray *ins) michael@0: { michael@0: LNewArray *lir = new(alloc()) LNewArray(temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewObject(MNewObject *ins) michael@0: { michael@0: LNewObject *lir = new(alloc()) LNewObject(temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewDeclEnvObject(MNewDeclEnvObject *ins) michael@0: { michael@0: LNewDeclEnvObject *lir = new(alloc()) LNewDeclEnvObject(temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewCallObject(MNewCallObject *ins) michael@0: { michael@0: LAllocation slots; michael@0: if (ins->slots()->type() == MIRType_Slots) michael@0: slots = useRegister(ins->slots()); michael@0: else michael@0: slots = LConstantIndex::Bogus(); michael@0: michael@0: LInstruction *lir; michael@0: if (ins->templateObject()->hasSingletonType()) { michael@0: LNewSingletonCallObject *singletonLir = new(alloc()) LNewSingletonCallObject(slots); michael@0: if (!define(singletonLir, ins)) michael@0: return false; michael@0: lir = singletonLir; michael@0: } else { michael@0: LNewCallObject *normalLir = new(alloc()) LNewCallObject(slots, temp()); michael@0: if (!define(normalLir, ins)) michael@0: return false; michael@0: lir = normalLir; michael@0: } michael@0: michael@0: if (!assignSafepoint(lir, ins)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewRunOnceCallObject(MNewRunOnceCallObject *ins) michael@0: { michael@0: LAllocation slots; michael@0: if (ins->slots()->type() == MIRType_Slots) michael@0: slots = useRegister(ins->slots()); michael@0: else michael@0: slots = LConstantIndex::Bogus(); michael@0: michael@0: LNewSingletonCallObject *lir = new(alloc()) LNewSingletonCallObject(slots); michael@0: if (!define(lir, ins)) michael@0: return false; michael@0: michael@0: if (!assignSafepoint(lir, ins)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewDerivedTypedObject(MNewDerivedTypedObject *ins) michael@0: { michael@0: LNewDerivedTypedObject *lir = michael@0: new(alloc()) LNewDerivedTypedObject(useRegisterAtStart(ins->type()), michael@0: useRegisterAtStart(ins->owner()), michael@0: useRegisterAtStart(ins->offset())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewCallObjectPar(MNewCallObjectPar *ins) michael@0: { michael@0: const LAllocation &parThreadContext = useRegister(ins->forkJoinContext()); michael@0: const LDefinition &temp1 = temp(); michael@0: const LDefinition &temp2 = temp(); michael@0: michael@0: LNewCallObjectPar *lir; michael@0: if (ins->slots()->type() == MIRType_Slots) { michael@0: const LAllocation &slots = useRegister(ins->slots()); michael@0: lir = LNewCallObjectPar::NewWithSlots(alloc(), parThreadContext, slots, temp1, temp2); michael@0: } else { michael@0: lir = LNewCallObjectPar::NewSansSlots(alloc(), parThreadContext, temp1, temp2); michael@0: } michael@0: michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewStringObject(MNewStringObject *ins) michael@0: { michael@0: JS_ASSERT(ins->input()->type() == MIRType_String); michael@0: michael@0: LNewStringObject *lir = new(alloc()) LNewStringObject(useRegister(ins->input()), temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAbortPar(MAbortPar *ins) michael@0: { michael@0: LAbortPar *lir = new(alloc()) LAbortPar(); michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInitElem(MInitElem *ins) michael@0: { michael@0: LInitElem *lir = new(alloc()) LInitElem(useRegisterAtStart(ins->getObject())); michael@0: if (!useBoxAtStart(lir, LInitElem::IdIndex, ins->getId())) michael@0: return false; michael@0: if (!useBoxAtStart(lir, LInitElem::ValueIndex, ins->getValue())) michael@0: return false; michael@0: michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInitElemGetterSetter(MInitElemGetterSetter *ins) michael@0: { michael@0: LInitElemGetterSetter *lir = michael@0: new(alloc()) LInitElemGetterSetter(useRegisterAtStart(ins->object()), michael@0: useRegisterAtStart(ins->value())); michael@0: if (!useBoxAtStart(lir, LInitElemGetterSetter::IdIndex, ins->idValue())) michael@0: return false; michael@0: michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitMutateProto(MMutateProto *ins) michael@0: { michael@0: LMutateProto *lir = new(alloc()) LMutateProto(useRegisterAtStart(ins->getObject())); michael@0: if (!useBoxAtStart(lir, LMutateProto::ValueIndex, ins->getValue())) michael@0: return false; michael@0: michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInitProp(MInitProp *ins) michael@0: { michael@0: LInitProp *lir = new(alloc()) LInitProp(useRegisterAtStart(ins->getObject())); michael@0: if (!useBoxAtStart(lir, LInitProp::ValueIndex, ins->getValue())) michael@0: return false; michael@0: michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInitPropGetterSetter(MInitPropGetterSetter *ins) michael@0: { michael@0: LInitPropGetterSetter *lir = michael@0: new(alloc()) LInitPropGetterSetter(useRegisterAtStart(ins->object()), michael@0: useRegisterAtStart(ins->value())); michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins) michael@0: { michael@0: LCreateThisWithTemplate *lir = new(alloc()) LCreateThisWithTemplate(temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCreateThisWithProto(MCreateThisWithProto *ins) michael@0: { michael@0: LCreateThisWithProto *lir = michael@0: new(alloc()) LCreateThisWithProto(useRegisterOrConstantAtStart(ins->getCallee()), michael@0: useRegisterOrConstantAtStart(ins->getPrototype())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCreateThis(MCreateThis *ins) michael@0: { michael@0: LCreateThis *lir = new(alloc()) LCreateThis(useRegisterOrConstantAtStart(ins->getCallee())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCreateArgumentsObject(MCreateArgumentsObject *ins) michael@0: { michael@0: // LAllocation callObj = useRegisterAtStart(ins->getCallObject()); michael@0: LAllocation callObj = useFixed(ins->getCallObject(), CallTempReg0); michael@0: LCreateArgumentsObject *lir = new(alloc()) LCreateArgumentsObject(callObj, tempFixed(CallTempReg1)); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGetArgumentsObjectArg(MGetArgumentsObjectArg *ins) michael@0: { michael@0: LAllocation argsObj = useRegister(ins->getArgsObject()); michael@0: LGetArgumentsObjectArg *lir = new(alloc()) LGetArgumentsObjectArg(argsObj, temp()); michael@0: return defineBox(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSetArgumentsObjectArg(MSetArgumentsObjectArg *ins) michael@0: { michael@0: LAllocation argsObj = useRegister(ins->getArgsObject()); michael@0: LSetArgumentsObjectArg *lir = new(alloc()) LSetArgumentsObjectArg(argsObj, temp()); michael@0: if (!useBox(lir, LSetArgumentsObjectArg::ValueIndex, ins->getValue())) michael@0: return false; michael@0: michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitReturnFromCtor(MReturnFromCtor *ins) michael@0: { michael@0: LReturnFromCtor *lir = new(alloc()) LReturnFromCtor(useRegister(ins->getObject())); michael@0: if (!useBox(lir, LReturnFromCtor::ValueIndex, ins->getValue())) michael@0: return false; michael@0: michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitComputeThis(MComputeThis *ins) michael@0: { michael@0: JS_ASSERT(ins->type() == MIRType_Object); michael@0: JS_ASSERT(ins->input()->type() == MIRType_Value); michael@0: michael@0: LComputeThis *lir = new(alloc()) LComputeThis(); michael@0: michael@0: // Don't use useBoxAtStart because ComputeThis has a safepoint and needs to michael@0: // have its inputs in different registers than its return value so that michael@0: // they aren't clobbered. michael@0: if (!useBox(lir, LComputeThis::ValueIndex, ins->input())) michael@0: return false; michael@0: michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLoadArrowThis(MLoadArrowThis *ins) michael@0: { michael@0: JS_ASSERT(ins->type() == MIRType_Value); michael@0: JS_ASSERT(ins->callee()->type() == MIRType_Object); michael@0: michael@0: LLoadArrowThis *lir = new(alloc()) LLoadArrowThis(useRegister(ins->callee())); michael@0: return defineBox(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::lowerCallArguments(MCall *call) michael@0: { michael@0: uint32_t argc = call->numStackArgs(); michael@0: if (argc > maxargslots_) michael@0: maxargslots_ = argc; michael@0: michael@0: for (size_t i = 0; i < argc; i++) { michael@0: MDefinition *arg = call->getArg(i); michael@0: uint32_t argslot = argc - i; michael@0: michael@0: // Values take a slow path. michael@0: if (arg->type() == MIRType_Value) { michael@0: LStackArgV *stack = new(alloc()) LStackArgV(argslot); michael@0: if (!useBox(stack, 0, arg) || !add(stack)) michael@0: return false; michael@0: } else { michael@0: // Known types can move constant types and/or payloads. michael@0: LStackArgT *stack = new(alloc()) LStackArgT(argslot, arg->type(), useRegisterOrConstant(arg)); michael@0: if (!add(stack)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCall(MCall *call) michael@0: { michael@0: JS_ASSERT(CallTempReg0 != CallTempReg1); michael@0: JS_ASSERT(CallTempReg0 != ArgumentsRectifierReg); michael@0: JS_ASSERT(CallTempReg1 != ArgumentsRectifierReg); michael@0: JS_ASSERT(call->getFunction()->type() == MIRType_Object); michael@0: michael@0: if (!lowerCallArguments(call)) michael@0: return false; michael@0: michael@0: // Height of the current argument vector. michael@0: JSFunction *target = call->getSingleTarget(); michael@0: michael@0: // Call DOM functions. michael@0: if (call->isCallDOMNative()) { michael@0: JS_ASSERT(target && target->isNative()); michael@0: Register cxReg, objReg, privReg, argsReg; michael@0: GetTempRegForIntArg(0, 0, &cxReg); michael@0: GetTempRegForIntArg(1, 0, &objReg); michael@0: GetTempRegForIntArg(2, 0, &privReg); michael@0: mozilla::DebugOnly ok = GetTempRegForIntArg(3, 0, &argsReg); michael@0: MOZ_ASSERT(ok, "How can we not have four temp registers?"); michael@0: LCallDOMNative *lir = new(alloc()) LCallDOMNative(tempFixed(cxReg), tempFixed(objReg), michael@0: tempFixed(privReg), tempFixed(argsReg)); michael@0: return defineReturn(lir, call) && assignSafepoint(lir, call); michael@0: } michael@0: michael@0: // Call known functions. michael@0: if (target) { michael@0: if (target->isNative()) { michael@0: Register cxReg, numReg, vpReg, tmpReg; michael@0: GetTempRegForIntArg(0, 0, &cxReg); michael@0: GetTempRegForIntArg(1, 0, &numReg); michael@0: GetTempRegForIntArg(2, 0, &vpReg); michael@0: michael@0: // Even though this is just a temp reg, use the same API to avoid michael@0: // register collisions. michael@0: mozilla::DebugOnly ok = GetTempRegForIntArg(3, 0, &tmpReg); michael@0: MOZ_ASSERT(ok, "How can we not have four temp registers?"); michael@0: michael@0: LCallNative *lir = new(alloc()) LCallNative(tempFixed(cxReg), tempFixed(numReg), michael@0: tempFixed(vpReg), tempFixed(tmpReg)); michael@0: return defineReturn(lir, call) && assignSafepoint(lir, call); michael@0: } michael@0: michael@0: LCallKnown *lir = new(alloc()) LCallKnown(useFixed(call->getFunction(), CallTempReg0), michael@0: tempFixed(CallTempReg2)); michael@0: return defineReturn(lir, call) && assignSafepoint(lir, call); michael@0: } michael@0: michael@0: // Call anything, using the most generic code. michael@0: LCallGeneric *lir = new(alloc()) LCallGeneric(useFixed(call->getFunction(), CallTempReg0), michael@0: tempFixed(ArgumentsRectifierReg), michael@0: tempFixed(CallTempReg2)); michael@0: return defineReturn(lir, call) && assignSafepoint(lir, call); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitApplyArgs(MApplyArgs *apply) michael@0: { michael@0: JS_ASSERT(apply->getFunction()->type() == MIRType_Object); michael@0: michael@0: // Assert if we cannot build a rectifier frame. michael@0: JS_ASSERT(CallTempReg0 != ArgumentsRectifierReg); michael@0: JS_ASSERT(CallTempReg1 != ArgumentsRectifierReg); michael@0: michael@0: // Assert if the return value is already erased. michael@0: JS_ASSERT(CallTempReg2 != JSReturnReg_Type); michael@0: JS_ASSERT(CallTempReg2 != JSReturnReg_Data); michael@0: michael@0: LApplyArgsGeneric *lir = new(alloc()) LApplyArgsGeneric( michael@0: useFixed(apply->getFunction(), CallTempReg3), michael@0: useFixed(apply->getArgc(), CallTempReg0), michael@0: tempFixed(CallTempReg1), // object register michael@0: tempFixed(CallTempReg2)); // copy register michael@0: michael@0: MDefinition *self = apply->getThis(); michael@0: if (!useBoxFixed(lir, LApplyArgsGeneric::ThisIndex, self, CallTempReg4, CallTempReg5)) michael@0: return false; michael@0: michael@0: // Bailout is only needed in the case of possible non-JSFunction callee. michael@0: if (!apply->getSingleTarget() && !assignSnapshot(lir)) michael@0: return false; michael@0: michael@0: if (!defineReturn(lir, apply)) michael@0: return false; michael@0: if (!assignSafepoint(lir, apply)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitBail(MBail *bail) michael@0: { michael@0: LBail *lir = new(alloc()) LBail(); michael@0: return assignSnapshot(lir) && add(lir, bail); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAssertFloat32(MAssertFloat32 *assertion) michael@0: { michael@0: MIRType type = assertion->input()->type(); michael@0: DebugOnly checkIsFloat32 = assertion->mustBeFloat32(); michael@0: michael@0: if (!allowFloat32Optimizations()) michael@0: return true; michael@0: michael@0: if (type != MIRType_Value && !js_JitOptions.eagerCompilation) { michael@0: JS_ASSERT_IF(checkIsFloat32, type == MIRType_Float32); michael@0: JS_ASSERT_IF(!checkIsFloat32, type != MIRType_Float32); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitArraySplice(MArraySplice *ins) michael@0: { michael@0: LArraySplice *lir = new(alloc()) LArraySplice(useRegisterAtStart(ins->object()), michael@0: useRegisterAtStart(ins->start()), michael@0: useRegisterAtStart(ins->deleteCount())); michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGetDynamicName(MGetDynamicName *ins) michael@0: { michael@0: MDefinition *scopeChain = ins->getScopeChain(); michael@0: JS_ASSERT(scopeChain->type() == MIRType_Object); michael@0: michael@0: MDefinition *name = ins->getName(); michael@0: JS_ASSERT(name->type() == MIRType_String); michael@0: michael@0: LGetDynamicName *lir = new(alloc()) LGetDynamicName(useFixed(scopeChain, CallTempReg0), michael@0: useFixed(name, CallTempReg1), michael@0: tempFixed(CallTempReg2), michael@0: tempFixed(CallTempReg3), michael@0: tempFixed(CallTempReg4)); michael@0: michael@0: return assignSnapshot(lir) && defineReturn(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitFilterArgumentsOrEval(MFilterArgumentsOrEval *ins) michael@0: { michael@0: MDefinition *string = ins->getString(); michael@0: MOZ_ASSERT(string->type() == MIRType_String || string->type() == MIRType_Value); michael@0: michael@0: LInstruction *lir; michael@0: if (string->type() == MIRType_String) { michael@0: lir = new(alloc()) LFilterArgumentsOrEvalS(useFixed(string, CallTempReg0), michael@0: tempFixed(CallTempReg1), michael@0: tempFixed(CallTempReg2)); michael@0: } else { michael@0: lir = new(alloc()) LFilterArgumentsOrEvalV(tempFixed(CallTempReg0), michael@0: tempFixed(CallTempReg1), michael@0: tempFixed(CallTempReg2)); michael@0: if (!useBoxFixed(lir, LFilterArgumentsOrEvalV::Input, string, michael@0: CallTempReg3, CallTempReg4)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return assignSnapshot(lir) && add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallDirectEval(MCallDirectEval *ins) michael@0: { michael@0: MDefinition *scopeChain = ins->getScopeChain(); michael@0: JS_ASSERT(scopeChain->type() == MIRType_Object); michael@0: michael@0: MDefinition *string = ins->getString(); michael@0: JS_ASSERT(string->type() == MIRType_String || string->type() == MIRType_Value); michael@0: michael@0: MDefinition *thisValue = ins->getThisValue(); michael@0: michael@0: michael@0: LInstruction *lir; michael@0: if (string->type() == MIRType_String) { michael@0: lir = new(alloc()) LCallDirectEvalS(useRegisterAtStart(scopeChain), michael@0: useRegisterAtStart(string)); michael@0: } else { michael@0: lir = new(alloc()) LCallDirectEvalV(useRegisterAtStart(scopeChain)); michael@0: if (!useBoxAtStart(lir, LCallDirectEvalV::Argument, string)) michael@0: return false; michael@0: } michael@0: michael@0: if (string->type() == MIRType_String) { michael@0: if (!useBoxAtStart(lir, LCallDirectEvalS::ThisValue, thisValue)) michael@0: return false; michael@0: } else { michael@0: if (!useBoxAtStart(lir, LCallDirectEvalV::ThisValue, thisValue)) michael@0: return false; michael@0: } michael@0: michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: static JSOp michael@0: ReorderComparison(JSOp op, MDefinition **lhsp, MDefinition **rhsp) michael@0: { michael@0: MDefinition *lhs = *lhsp; michael@0: MDefinition *rhs = *rhsp; michael@0: michael@0: if (lhs->isConstant()) { michael@0: *rhsp = lhs; michael@0: *lhsp = rhs; michael@0: return ReverseCompareOp(op); michael@0: } michael@0: return op; michael@0: } michael@0: michael@0: static void michael@0: ReorderCommutative(MDefinition **lhsp, MDefinition **rhsp) michael@0: { michael@0: MDefinition *lhs = *lhsp; michael@0: MDefinition *rhs = *rhsp; michael@0: michael@0: // Ensure that if there is a constant, then it is in rhs. michael@0: // In addition, since clobbering binary operations clobber the left michael@0: // operand, prefer a non-constant lhs operand with no further uses. michael@0: michael@0: if (rhs->isConstant()) michael@0: return; michael@0: michael@0: // lhs and rhs are used by the commutative operator. If they have any michael@0: // *other* uses besides, try to reorder to avoid clobbering them. To michael@0: // be fully precise, we should check whether this is the *last* use, michael@0: // but checking hasOneDefUse() is a decent approximation which doesn't michael@0: // require any extra analysis. michael@0: JS_ASSERT(lhs->defUseCount() > 0); michael@0: JS_ASSERT(rhs->defUseCount() > 0); michael@0: if (lhs->isConstant() || (rhs->hasOneDefUse() && !lhs->hasOneDefUse())) { michael@0: *rhsp = lhs; michael@0: *lhsp = rhs; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitTest(MTest *test) michael@0: { michael@0: MDefinition *opd = test->getOperand(0); michael@0: MBasicBlock *ifTrue = test->ifTrue(); michael@0: MBasicBlock *ifFalse = test->ifFalse(); michael@0: michael@0: // String is converted to length of string in the type analysis phase (see michael@0: // TestPolicy). michael@0: JS_ASSERT(opd->type() != MIRType_String); michael@0: michael@0: if (opd->type() == MIRType_Value) { michael@0: LDefinition temp0, temp1; michael@0: if (test->operandMightEmulateUndefined()) { michael@0: temp0 = temp(); michael@0: temp1 = temp(); michael@0: } else { michael@0: temp0 = LDefinition::BogusTemp(); michael@0: temp1 = LDefinition::BogusTemp(); michael@0: } michael@0: LTestVAndBranch *lir = new(alloc()) LTestVAndBranch(ifTrue, ifFalse, tempDouble(), temp0, temp1); michael@0: if (!useBox(lir, LTestVAndBranch::Input, opd)) michael@0: return false; michael@0: return add(lir, test); michael@0: } michael@0: michael@0: if (opd->type() == MIRType_Object) { michael@0: // If the object might emulate undefined, we have to test for that. michael@0: if (test->operandMightEmulateUndefined()) michael@0: return add(new(alloc()) LTestOAndBranch(useRegister(opd), ifTrue, ifFalse, temp()), test); michael@0: michael@0: // Otherwise we know it's truthy. michael@0: return add(new(alloc()) LGoto(ifTrue)); michael@0: } michael@0: michael@0: // These must be explicitly sniffed out since they are constants and have michael@0: // no payload. michael@0: if (opd->type() == MIRType_Undefined || opd->type() == MIRType_Null) michael@0: return add(new(alloc()) LGoto(ifFalse)); michael@0: michael@0: // Constant Double operand. michael@0: if (opd->type() == MIRType_Double && opd->isConstant()) { michael@0: bool result = opd->toConstant()->valueToBoolean(); michael@0: return add(new(alloc()) LGoto(result ? ifTrue : ifFalse)); michael@0: } michael@0: michael@0: // Constant Float32 operand. michael@0: if (opd->type() == MIRType_Float32 && opd->isConstant()) { michael@0: bool result = opd->toConstant()->valueToBoolean(); michael@0: return add(new(alloc()) LGoto(result ? ifTrue : ifFalse)); michael@0: } michael@0: michael@0: // Constant Int32 operand. michael@0: if (opd->type() == MIRType_Int32 && opd->isConstant()) { michael@0: int32_t num = opd->toConstant()->value().toInt32(); michael@0: return add(new(alloc()) LGoto(num ? ifTrue : ifFalse)); michael@0: } michael@0: michael@0: // Constant Boolean operand. michael@0: if (opd->type() == MIRType_Boolean && opd->isConstant()) { michael@0: bool result = opd->toConstant()->value().toBoolean(); michael@0: return add(new(alloc()) LGoto(result ? ifTrue : ifFalse)); michael@0: } michael@0: michael@0: // Check if the operand for this test is a compare operation. If it is, we want michael@0: // to emit an LCompare*AndBranch rather than an LTest*AndBranch, to fuse the michael@0: // compare and jump instructions. michael@0: if (opd->isCompare() && opd->isEmittedAtUses()) { michael@0: MCompare *comp = opd->toCompare(); michael@0: MDefinition *left = comp->lhs(); michael@0: MDefinition *right = comp->rhs(); michael@0: michael@0: // Try to fold the comparison so that we don't have to handle all cases. michael@0: bool result; michael@0: if (comp->tryFold(&result)) michael@0: return add(new(alloc()) LGoto(result ? ifTrue : ifFalse)); michael@0: michael@0: // Emit LCompare*AndBranch. michael@0: michael@0: // Compare and branch null/undefined. michael@0: // The second operand has known null/undefined type, michael@0: // so just test the first operand. michael@0: if (comp->compareType() == MCompare::Compare_Null || michael@0: comp->compareType() == MCompare::Compare_Undefined) michael@0: { michael@0: if (left->type() == MIRType_Object) { michael@0: MOZ_ASSERT(comp->operandMightEmulateUndefined(), michael@0: "MCompare::tryFold should handle the never-emulates-undefined case"); michael@0: michael@0: LEmulatesUndefinedAndBranch *lir = michael@0: new(alloc()) LEmulatesUndefinedAndBranch(comp, useRegister(left), michael@0: ifTrue, ifFalse, temp()); michael@0: return add(lir, test); michael@0: } michael@0: michael@0: LDefinition tmp, tmpToUnbox; michael@0: if (comp->operandMightEmulateUndefined()) { michael@0: tmp = temp(); michael@0: tmpToUnbox = tempToUnbox(); michael@0: } else { michael@0: tmp = LDefinition::BogusTemp(); michael@0: tmpToUnbox = LDefinition::BogusTemp(); michael@0: } michael@0: michael@0: LIsNullOrLikeUndefinedAndBranch *lir = michael@0: new(alloc()) LIsNullOrLikeUndefinedAndBranch(comp, ifTrue, ifFalse, michael@0: tmp, tmpToUnbox); michael@0: if (!useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left)) michael@0: return false; michael@0: return add(lir, test); michael@0: } michael@0: michael@0: // Compare and branch booleans. michael@0: if (comp->compareType() == MCompare::Compare_Boolean) { michael@0: JS_ASSERT(left->type() == MIRType_Value); michael@0: JS_ASSERT(right->type() == MIRType_Boolean); michael@0: michael@0: LAllocation rhs = useRegisterOrConstant(right); michael@0: LCompareBAndBranch *lir = new(alloc()) LCompareBAndBranch(comp, rhs, ifTrue, ifFalse); michael@0: if (!useBox(lir, LCompareBAndBranch::Lhs, left)) michael@0: return false; michael@0: return add(lir, test); michael@0: } michael@0: michael@0: // Compare and branch Int32 or Object pointers. michael@0: if (comp->isInt32Comparison() || michael@0: comp->compareType() == MCompare::Compare_UInt32 || michael@0: comp->compareType() == MCompare::Compare_Object) michael@0: { michael@0: JSOp op = ReorderComparison(comp->jsop(), &left, &right); michael@0: LAllocation lhs = useRegister(left); michael@0: LAllocation rhs; michael@0: if (comp->isInt32Comparison() || comp->compareType() == MCompare::Compare_UInt32) michael@0: rhs = useAnyOrConstant(right); michael@0: else michael@0: rhs = useRegister(right); michael@0: LCompareAndBranch *lir = new(alloc()) LCompareAndBranch(comp, op, lhs, rhs, michael@0: ifTrue, ifFalse); michael@0: return add(lir, test); michael@0: } michael@0: michael@0: // Compare and branch doubles. michael@0: if (comp->isDoubleComparison()) { michael@0: LAllocation lhs = useRegister(left); michael@0: LAllocation rhs = useRegister(right); michael@0: LCompareDAndBranch *lir = new(alloc()) LCompareDAndBranch(comp, lhs, rhs, michael@0: ifTrue, ifFalse); michael@0: return add(lir, test); michael@0: } michael@0: michael@0: // Compare and branch floats. michael@0: if (comp->isFloat32Comparison()) { michael@0: LAllocation lhs = useRegister(left); michael@0: LAllocation rhs = useRegister(right); michael@0: LCompareFAndBranch *lir = new(alloc()) LCompareFAndBranch(comp, lhs, rhs, michael@0: ifTrue, ifFalse); michael@0: return add(lir, test); michael@0: } michael@0: michael@0: // Compare values. michael@0: if (comp->compareType() == MCompare::Compare_Value) { michael@0: LCompareVAndBranch *lir = new(alloc()) LCompareVAndBranch(comp, ifTrue, ifFalse); michael@0: if (!useBoxAtStart(lir, LCompareVAndBranch::LhsInput, left)) michael@0: return false; michael@0: if (!useBoxAtStart(lir, LCompareVAndBranch::RhsInput, right)) michael@0: return false; michael@0: return add(lir, test); michael@0: } michael@0: } michael@0: michael@0: // Check if the operand for this test is a bitand operation. If it is, we want michael@0: // to emit an LBitAndAndBranch rather than an LTest*AndBranch. michael@0: if (opd->isBitAnd() && opd->isEmittedAtUses()) { michael@0: MDefinition *lhs = opd->getOperand(0); michael@0: MDefinition *rhs = opd->getOperand(1); michael@0: if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) { michael@0: ReorderCommutative(&lhs, &rhs); michael@0: return lowerForBitAndAndBranch(new(alloc()) LBitAndAndBranch(ifTrue, ifFalse), test, lhs, rhs); michael@0: } michael@0: } michael@0: michael@0: if (opd->type() == MIRType_Double) michael@0: return add(new(alloc()) LTestDAndBranch(useRegister(opd), ifTrue, ifFalse)); michael@0: michael@0: if (opd->type() == MIRType_Float32) michael@0: return add(new(alloc()) LTestFAndBranch(useRegister(opd), ifTrue, ifFalse)); michael@0: michael@0: JS_ASSERT(opd->type() == MIRType_Int32 || opd->type() == MIRType_Boolean); michael@0: return add(new(alloc()) LTestIAndBranch(useRegister(opd), ifTrue, ifFalse)); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitFunctionDispatch(MFunctionDispatch *ins) michael@0: { michael@0: LFunctionDispatch *lir = new(alloc()) LFunctionDispatch(useRegister(ins->input())); michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitTypeObjectDispatch(MTypeObjectDispatch *ins) michael@0: { michael@0: LTypeObjectDispatch *lir = new(alloc()) LTypeObjectDispatch(useRegister(ins->input()), temp()); michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: static inline bool michael@0: CanEmitCompareAtUses(MInstruction *ins) michael@0: { michael@0: if (!ins->canEmitAtUses()) michael@0: return false; michael@0: michael@0: bool foundTest = false; michael@0: for (MUseIterator iter(ins->usesBegin()); iter != ins->usesEnd(); iter++) { michael@0: MNode *node = iter->consumer(); michael@0: if (!node->isDefinition()) michael@0: return false; michael@0: if (!node->toDefinition()->isTest()) michael@0: return false; michael@0: if (foundTest) michael@0: return false; michael@0: foundTest = true; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCompare(MCompare *comp) michael@0: { michael@0: MDefinition *left = comp->lhs(); michael@0: MDefinition *right = comp->rhs(); michael@0: michael@0: // Try to fold the comparison so that we don't have to handle all cases. michael@0: bool result; michael@0: if (comp->tryFold(&result)) michael@0: return define(new(alloc()) LInteger(result), comp); michael@0: michael@0: // Move below the emitAtUses call if we ever implement michael@0: // LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't michael@0: // make sense and avoids confusion. michael@0: if (comp->compareType() == MCompare::Compare_String) { michael@0: LCompareS *lir = new(alloc()) LCompareS(useRegister(left), useRegister(right), temp()); michael@0: if (!define(lir, comp)) michael@0: return false; michael@0: return assignSafepoint(lir, comp); michael@0: } michael@0: michael@0: // Strict compare between value and string michael@0: if (comp->compareType() == MCompare::Compare_StrictString) { michael@0: JS_ASSERT(left->type() == MIRType_Value); michael@0: JS_ASSERT(right->type() == MIRType_String); michael@0: michael@0: LCompareStrictS *lir = new(alloc()) LCompareStrictS(useRegister(right), temp(), tempToUnbox()); michael@0: if (!useBox(lir, LCompareStrictS::Lhs, left)) michael@0: return false; michael@0: if (!define(lir, comp)) michael@0: return false; michael@0: return assignSafepoint(lir, comp); michael@0: } michael@0: michael@0: // Unknown/unspecialized compare use a VM call. michael@0: if (comp->compareType() == MCompare::Compare_Unknown) { michael@0: LCompareVM *lir = new(alloc()) LCompareVM(); michael@0: if (!useBoxAtStart(lir, LCompareVM::LhsInput, left)) michael@0: return false; michael@0: if (!useBoxAtStart(lir, LCompareVM::RhsInput, right)) michael@0: return false; michael@0: return defineReturn(lir, comp) && assignSafepoint(lir, comp); michael@0: } michael@0: michael@0: // Sniff out if the output of this compare is used only for a branching. michael@0: // If it is, then we will emit an LCompare*AndBranch instruction in place michael@0: // of this compare and any test that uses this compare. Thus, we can michael@0: // ignore this Compare. michael@0: if (CanEmitCompareAtUses(comp)) michael@0: return emitAtUses(comp); michael@0: michael@0: // Compare Null and Undefined. michael@0: if (comp->compareType() == MCompare::Compare_Null || michael@0: comp->compareType() == MCompare::Compare_Undefined) michael@0: { michael@0: if (left->type() == MIRType_Object) { michael@0: MOZ_ASSERT(comp->operandMightEmulateUndefined(), michael@0: "MCompare::tryFold should have folded this away"); michael@0: michael@0: return define(new(alloc()) LEmulatesUndefined(useRegister(left)), comp); michael@0: } michael@0: michael@0: LDefinition tmp, tmpToUnbox; michael@0: if (comp->operandMightEmulateUndefined()) { michael@0: tmp = temp(); michael@0: tmpToUnbox = tempToUnbox(); michael@0: } else { michael@0: tmp = LDefinition::BogusTemp(); michael@0: tmpToUnbox = LDefinition::BogusTemp(); michael@0: } michael@0: michael@0: LIsNullOrLikeUndefined *lir = new(alloc()) LIsNullOrLikeUndefined(tmp, tmpToUnbox); michael@0: if (!useBox(lir, LIsNullOrLikeUndefined::Value, left)) michael@0: return false; michael@0: return define(lir, comp); michael@0: } michael@0: michael@0: // Compare booleans. michael@0: if (comp->compareType() == MCompare::Compare_Boolean) { michael@0: JS_ASSERT(left->type() == MIRType_Value); michael@0: JS_ASSERT(right->type() == MIRType_Boolean); michael@0: michael@0: LCompareB *lir = new(alloc()) LCompareB(useRegisterOrConstant(right)); michael@0: if (!useBox(lir, LCompareB::Lhs, left)) michael@0: return false; michael@0: return define(lir, comp); michael@0: } michael@0: michael@0: // Compare Int32 or Object pointers. michael@0: if (comp->isInt32Comparison() || michael@0: comp->compareType() == MCompare::Compare_UInt32 || michael@0: comp->compareType() == MCompare::Compare_Object) michael@0: { michael@0: JSOp op = ReorderComparison(comp->jsop(), &left, &right); michael@0: LAllocation lhs = useRegister(left); michael@0: LAllocation rhs; michael@0: if (comp->isInt32Comparison() || michael@0: comp->compareType() == MCompare::Compare_UInt32) michael@0: { michael@0: rhs = useAnyOrConstant(right); michael@0: } else { michael@0: rhs = useRegister(right); michael@0: } michael@0: return define(new(alloc()) LCompare(op, lhs, rhs), comp); michael@0: } michael@0: michael@0: // Compare doubles. michael@0: if (comp->isDoubleComparison()) michael@0: return define(new(alloc()) LCompareD(useRegister(left), useRegister(right)), comp); michael@0: michael@0: // Compare float32. michael@0: if (comp->isFloat32Comparison()) michael@0: return define(new(alloc()) LCompareF(useRegister(left), useRegister(right)), comp); michael@0: michael@0: // Compare values. michael@0: if (comp->compareType() == MCompare::Compare_Value) { michael@0: LCompareV *lir = new(alloc()) LCompareV(); michael@0: if (!useBoxAtStart(lir, LCompareV::LhsInput, left)) michael@0: return false; michael@0: if (!useBoxAtStart(lir, LCompareV::RhsInput, right)) michael@0: return false; michael@0: return define(lir, comp); michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Unrecognized compare type."); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::lowerBitOp(JSOp op, MInstruction *ins) michael@0: { michael@0: MDefinition *lhs = ins->getOperand(0); michael@0: MDefinition *rhs = ins->getOperand(1); michael@0: michael@0: if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) { michael@0: ReorderCommutative(&lhs, &rhs); michael@0: return lowerForALU(new(alloc()) LBitOpI(op), ins, lhs, rhs); michael@0: } michael@0: michael@0: LBitOpV *lir = new(alloc()) LBitOpV(op); michael@0: if (!useBoxAtStart(lir, LBitOpV::LhsInput, lhs)) michael@0: return false; michael@0: if (!useBoxAtStart(lir, LBitOpV::RhsInput, rhs)) michael@0: return false; michael@0: michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitTypeOf(MTypeOf *ins) michael@0: { michael@0: MDefinition *opd = ins->input(); michael@0: JS_ASSERT(opd->type() == MIRType_Value); michael@0: michael@0: LTypeOfV *lir = new(alloc()) LTypeOfV(tempToUnbox()); michael@0: if (!useBox(lir, LTypeOfV::Input, opd)) michael@0: return false; michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitToId(MToId *ins) michael@0: { michael@0: LToIdV *lir = new(alloc()) LToIdV(tempDouble()); michael@0: if (!useBox(lir, LToIdV::Object, ins->lhs())) michael@0: return false; michael@0: if (!useBox(lir, LToIdV::Index, ins->rhs())) michael@0: return false; michael@0: return defineBox(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitBitNot(MBitNot *ins) michael@0: { michael@0: MDefinition *input = ins->getOperand(0); michael@0: michael@0: if (input->type() == MIRType_Int32) michael@0: return lowerForALU(new(alloc()) LBitNotI(), ins, input); michael@0: michael@0: LBitNotV *lir = new(alloc()) LBitNotV; michael@0: if (!useBoxAtStart(lir, LBitNotV::Input, input)) michael@0: return false; michael@0: if (!defineReturn(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: static bool michael@0: CanEmitBitAndAtUses(MInstruction *ins) michael@0: { michael@0: if (!ins->canEmitAtUses()) michael@0: return false; michael@0: michael@0: if (ins->getOperand(0)->type() != MIRType_Int32 || ins->getOperand(1)->type() != MIRType_Int32) michael@0: return false; michael@0: michael@0: MUseIterator iter(ins->usesBegin()); michael@0: if (iter == ins->usesEnd()) michael@0: return false; michael@0: michael@0: MNode *node = iter->consumer(); michael@0: if (!node->isDefinition()) michael@0: return false; michael@0: michael@0: if (!node->toDefinition()->isTest()) michael@0: return false; michael@0: michael@0: iter++; michael@0: return iter == ins->usesEnd(); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitBitAnd(MBitAnd *ins) michael@0: { michael@0: // Sniff out if the output of this bitand is used only for a branching. michael@0: // If it is, then we will emit an LBitAndAndBranch instruction in place michael@0: // of this bitand and any test that uses this bitand. Thus, we can michael@0: // ignore this BitAnd. michael@0: if (CanEmitBitAndAtUses(ins)) michael@0: return emitAtUses(ins); michael@0: michael@0: return lowerBitOp(JSOP_BITAND, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitBitOr(MBitOr *ins) michael@0: { michael@0: return lowerBitOp(JSOP_BITOR, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitBitXor(MBitXor *ins) michael@0: { michael@0: return lowerBitOp(JSOP_BITXOR, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::lowerShiftOp(JSOp op, MShiftInstruction *ins) michael@0: { michael@0: MDefinition *lhs = ins->getOperand(0); michael@0: MDefinition *rhs = ins->getOperand(1); michael@0: michael@0: if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) { michael@0: if (ins->type() == MIRType_Double) { michael@0: JS_ASSERT(op == JSOP_URSH); michael@0: return lowerUrshD(ins->toUrsh()); michael@0: } michael@0: michael@0: LShiftI *lir = new(alloc()) LShiftI(op); michael@0: if (op == JSOP_URSH) { michael@0: if (ins->toUrsh()->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) michael@0: return false; michael@0: } michael@0: return lowerForShift(lir, ins, lhs, rhs); michael@0: } michael@0: michael@0: JS_ASSERT(ins->specialization() == MIRType_None); michael@0: michael@0: if (op == JSOP_URSH) { michael@0: // Result is either int32 or double so we have to use BinaryV. michael@0: return lowerBinaryV(JSOP_URSH, ins); michael@0: } michael@0: michael@0: LBitOpV *lir = new(alloc()) LBitOpV(op); michael@0: if (!useBoxAtStart(lir, LBitOpV::LhsInput, lhs)) michael@0: return false; michael@0: if (!useBoxAtStart(lir, LBitOpV::RhsInput, rhs)) michael@0: return false; michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLsh(MLsh *ins) michael@0: { michael@0: return lowerShiftOp(JSOP_LSH, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRsh(MRsh *ins) michael@0: { michael@0: return lowerShiftOp(JSOP_RSH, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitUrsh(MUrsh *ins) michael@0: { michael@0: return lowerShiftOp(JSOP_URSH, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitFloor(MFloor *ins) michael@0: { michael@0: MIRType type = ins->num()->type(); michael@0: JS_ASSERT(IsFloatingPointType(type)); michael@0: michael@0: if (type == MIRType_Double) { michael@0: LFloor *lir = new(alloc()) LFloor(useRegister(ins->num())); michael@0: if (!assignSnapshot(lir)) michael@0: return false; michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: LFloorF *lir = new(alloc()) LFloorF(useRegister(ins->num())); michael@0: if (!assignSnapshot(lir)) michael@0: return false; michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRound(MRound *ins) michael@0: { michael@0: MIRType type = ins->num()->type(); michael@0: JS_ASSERT(IsFloatingPointType(type)); michael@0: michael@0: if (type == MIRType_Double) { michael@0: LRound *lir = new (alloc()) LRound(useRegister(ins->num()), tempDouble()); michael@0: if (!assignSnapshot(lir)) michael@0: return false; michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: LRoundF *lir = new (alloc()) LRoundF(useRegister(ins->num()), tempDouble()); michael@0: if (!assignSnapshot(lir)) michael@0: return false; michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitMinMax(MMinMax *ins) michael@0: { michael@0: MDefinition *first = ins->getOperand(0); michael@0: MDefinition *second = ins->getOperand(1); michael@0: michael@0: ReorderCommutative(&first, &second); michael@0: michael@0: if (ins->specialization() == MIRType_Int32) { michael@0: LMinMaxI *lir = new(alloc()) LMinMaxI(useRegisterAtStart(first), useRegisterOrConstant(second)); michael@0: return defineReuseInput(lir, ins, 0); michael@0: } michael@0: michael@0: LMinMaxD *lir = new(alloc()) LMinMaxD(useRegisterAtStart(first), useRegister(second)); michael@0: return defineReuseInput(lir, ins, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAbs(MAbs *ins) michael@0: { michael@0: MDefinition *num = ins->num(); michael@0: JS_ASSERT(IsNumberType(num->type())); michael@0: michael@0: if (num->type() == MIRType_Int32) { michael@0: LAbsI *lir = new(alloc()) LAbsI(useRegisterAtStart(num)); michael@0: // needed to handle abs(INT32_MIN) michael@0: if (ins->fallible() && !assignSnapshot(lir)) michael@0: return false; michael@0: return defineReuseInput(lir, ins, 0); michael@0: } michael@0: if (num->type() == MIRType_Float32) { michael@0: LAbsF *lir = new(alloc()) LAbsF(useRegisterAtStart(num)); michael@0: return defineReuseInput(lir, ins, 0); michael@0: } michael@0: michael@0: LAbsD *lir = new(alloc()) LAbsD(useRegisterAtStart(num)); michael@0: return defineReuseInput(lir, ins, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSqrt(MSqrt *ins) michael@0: { michael@0: MDefinition *num = ins->num(); michael@0: JS_ASSERT(IsFloatingPointType(num->type())); michael@0: if (num->type() == MIRType_Double) { michael@0: LSqrtD *lir = new(alloc()) LSqrtD(useRegisterAtStart(num)); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: LSqrtF *lir = new(alloc()) LSqrtF(useRegisterAtStart(num)); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAtan2(MAtan2 *ins) michael@0: { michael@0: MDefinition *y = ins->y(); michael@0: JS_ASSERT(y->type() == MIRType_Double); michael@0: michael@0: MDefinition *x = ins->x(); michael@0: JS_ASSERT(x->type() == MIRType_Double); michael@0: michael@0: LAtan2D *lir = new(alloc()) LAtan2D(useRegisterAtStart(y), useRegisterAtStart(x), tempFixed(CallTempReg0)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitHypot(MHypot *ins) michael@0: { michael@0: MDefinition *x = ins->x(); michael@0: JS_ASSERT(x->type() == MIRType_Double); michael@0: michael@0: MDefinition *y = ins->y(); michael@0: JS_ASSERT(y->type() == MIRType_Double); michael@0: michael@0: LHypot *lir = new(alloc()) LHypot(useRegisterAtStart(x), useRegisterAtStart(y), tempFixed(CallTempReg0)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitPow(MPow *ins) michael@0: { michael@0: MDefinition *input = ins->input(); michael@0: JS_ASSERT(input->type() == MIRType_Double); michael@0: michael@0: MDefinition *power = ins->power(); michael@0: JS_ASSERT(power->type() == MIRType_Int32 || power->type() == MIRType_Double); michael@0: michael@0: if (power->type() == MIRType_Int32) { michael@0: // Note: useRegisterAtStart here is safe, the temp is a GP register so michael@0: // it will never get the same register. michael@0: LPowI *lir = new(alloc()) LPowI(useRegisterAtStart(input), useFixed(power, CallTempReg1), michael@0: tempFixed(CallTempReg0)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: LPowD *lir = new(alloc()) LPowD(useRegisterAtStart(input), useRegisterAtStart(power), michael@0: tempFixed(CallTempReg0)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRandom(MRandom *ins) michael@0: { michael@0: LRandom *lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitMathFunction(MMathFunction *ins) michael@0: { michael@0: JS_ASSERT(IsFloatingPointType(ins->type())); michael@0: JS_ASSERT_IF(ins->type() == MIRType_Double, ins->input()->type() == MIRType_Double); michael@0: JS_ASSERT_IF(ins->type() == MIRType_Float32, ins->input()->type() == MIRType_Float32); michael@0: michael@0: if (ins->type() == MIRType_Double) { michael@0: // Note: useRegisterAtStart is safe here, the temp is not a FP register. michael@0: LMathFunctionD *lir = new(alloc()) LMathFunctionD(useRegisterAtStart(ins->input()), michael@0: tempFixed(CallTempReg0)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: LMathFunctionF *lir = new(alloc()) LMathFunctionF(useRegisterAtStart(ins->input()), michael@0: tempFixed(CallTempReg0)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: // Try to mark an add or sub instruction as able to recover its input when michael@0: // bailing out. michael@0: template michael@0: static void michael@0: MaybeSetRecoversInput(S *mir, T *lir) michael@0: { michael@0: JS_ASSERT(lir->mirRaw() == mir); michael@0: if (!mir->fallible()) michael@0: return; michael@0: michael@0: if (lir->output()->policy() != LDefinition::MUST_REUSE_INPUT) michael@0: return; michael@0: michael@0: // The original operands to an add or sub can't be recovered if they both michael@0: // use the same register. michael@0: if (lir->lhs()->isUse() && lir->rhs()->isUse() && michael@0: lir->lhs()->toUse()->virtualRegister() == lir->rhs()->toUse()->virtualRegister()) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: // Add instructions that are on two different values can recover michael@0: // the input they clobbered via MUST_REUSE_INPUT. Thus, a copy michael@0: // of that input does not need to be kept alive in the snapshot michael@0: // for the instruction. michael@0: michael@0: lir->setRecoversInput(); michael@0: michael@0: const LUse *input = lir->getOperand(lir->output()->getReusedInput())->toUse(); michael@0: lir->snapshot()->rewriteRecoveredInput(*input); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAdd(MAdd *ins) michael@0: { michael@0: MDefinition *lhs = ins->getOperand(0); michael@0: MDefinition *rhs = ins->getOperand(1); michael@0: michael@0: JS_ASSERT(lhs->type() == rhs->type()); michael@0: michael@0: if (ins->specialization() == MIRType_Int32) { michael@0: JS_ASSERT(lhs->type() == MIRType_Int32); michael@0: ReorderCommutative(&lhs, &rhs); michael@0: LAddI *lir = new(alloc()) LAddI; michael@0: michael@0: if (ins->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) michael@0: return false; michael@0: michael@0: if (!lowerForALU(lir, ins, lhs, rhs)) michael@0: return false; michael@0: michael@0: MaybeSetRecoversInput(ins, lir); michael@0: return true; michael@0: } michael@0: michael@0: if (ins->specialization() == MIRType_Double) { michael@0: JS_ASSERT(lhs->type() == MIRType_Double); michael@0: ReorderCommutative(&lhs, &rhs); michael@0: return lowerForFPU(new(alloc()) LMathD(JSOP_ADD), ins, lhs, rhs); michael@0: } michael@0: michael@0: if (ins->specialization() == MIRType_Float32) { michael@0: JS_ASSERT(lhs->type() == MIRType_Float32); michael@0: ReorderCommutative(&lhs, &rhs); michael@0: return lowerForFPU(new(alloc()) LMathF(JSOP_ADD), ins, lhs, rhs); michael@0: } michael@0: michael@0: return lowerBinaryV(JSOP_ADD, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSub(MSub *ins) michael@0: { michael@0: MDefinition *lhs = ins->lhs(); michael@0: MDefinition *rhs = ins->rhs(); michael@0: michael@0: JS_ASSERT(lhs->type() == rhs->type()); michael@0: michael@0: if (ins->specialization() == MIRType_Int32) { michael@0: JS_ASSERT(lhs->type() == MIRType_Int32); michael@0: michael@0: LSubI *lir = new(alloc()) LSubI; michael@0: if (ins->fallible() && !assignSnapshot(lir)) michael@0: return false; michael@0: michael@0: if (!lowerForALU(lir, ins, lhs, rhs)) michael@0: return false; michael@0: michael@0: MaybeSetRecoversInput(ins, lir); michael@0: return true; michael@0: } michael@0: if (ins->specialization() == MIRType_Double) { michael@0: JS_ASSERT(lhs->type() == MIRType_Double); michael@0: return lowerForFPU(new(alloc()) LMathD(JSOP_SUB), ins, lhs, rhs); michael@0: } michael@0: if (ins->specialization() == MIRType_Float32) { michael@0: JS_ASSERT(lhs->type() == MIRType_Float32); michael@0: return lowerForFPU(new(alloc()) LMathF(JSOP_SUB), ins, lhs, rhs); michael@0: } michael@0: michael@0: return lowerBinaryV(JSOP_SUB, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitMul(MMul *ins) michael@0: { michael@0: MDefinition *lhs = ins->lhs(); michael@0: MDefinition *rhs = ins->rhs(); michael@0: JS_ASSERT(lhs->type() == rhs->type()); michael@0: michael@0: if (ins->specialization() == MIRType_Int32) { michael@0: JS_ASSERT(lhs->type() == MIRType_Int32); michael@0: ReorderCommutative(&lhs, &rhs); michael@0: michael@0: // If our RHS is a constant -1 and we don't have to worry about michael@0: // overflow, we can optimize to an LNegI. michael@0: if (!ins->fallible() && rhs->isConstant() && rhs->toConstant()->value() == Int32Value(-1)) michael@0: return defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(lhs)), ins, 0); michael@0: michael@0: return lowerMulI(ins, lhs, rhs); michael@0: } michael@0: if (ins->specialization() == MIRType_Double) { michael@0: JS_ASSERT(lhs->type() == MIRType_Double); michael@0: ReorderCommutative(&lhs, &rhs); michael@0: michael@0: // If our RHS is a constant -1.0, we can optimize to an LNegD. michael@0: if (rhs->isConstant() && rhs->toConstant()->value() == DoubleValue(-1.0)) michael@0: return defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(lhs)), ins, 0); michael@0: michael@0: return lowerForFPU(new(alloc()) LMathD(JSOP_MUL), ins, lhs, rhs); michael@0: } michael@0: if (ins->specialization() == MIRType_Float32) { michael@0: JS_ASSERT(lhs->type() == MIRType_Float32); michael@0: ReorderCommutative(&lhs, &rhs); michael@0: michael@0: // We apply the same optimizations as for doubles michael@0: if (rhs->isConstant() && rhs->toConstant()->value() == Float32Value(-1.0f)) michael@0: return defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(lhs)), ins, 0); michael@0: michael@0: return lowerForFPU(new(alloc()) LMathF(JSOP_MUL), ins, lhs, rhs); michael@0: } michael@0: michael@0: return lowerBinaryV(JSOP_MUL, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitDiv(MDiv *ins) michael@0: { michael@0: MDefinition *lhs = ins->lhs(); michael@0: MDefinition *rhs = ins->rhs(); michael@0: JS_ASSERT(lhs->type() == rhs->type()); michael@0: michael@0: if (ins->specialization() == MIRType_Int32) { michael@0: JS_ASSERT(lhs->type() == MIRType_Int32); michael@0: return lowerDivI(ins); michael@0: } michael@0: if (ins->specialization() == MIRType_Double) { michael@0: JS_ASSERT(lhs->type() == MIRType_Double); michael@0: return lowerForFPU(new(alloc()) LMathD(JSOP_DIV), ins, lhs, rhs); michael@0: } michael@0: if (ins->specialization() == MIRType_Float32) { michael@0: JS_ASSERT(lhs->type() == MIRType_Float32); michael@0: return lowerForFPU(new(alloc()) LMathF(JSOP_DIV), ins, lhs, rhs); michael@0: } michael@0: michael@0: return lowerBinaryV(JSOP_DIV, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitMod(MMod *ins) michael@0: { michael@0: JS_ASSERT(ins->lhs()->type() == ins->rhs()->type()); michael@0: michael@0: if (ins->specialization() == MIRType_Int32) { michael@0: JS_ASSERT(ins->type() == MIRType_Int32); michael@0: JS_ASSERT(ins->lhs()->type() == MIRType_Int32); michael@0: return lowerModI(ins); michael@0: } michael@0: michael@0: if (ins->specialization() == MIRType_Double) { michael@0: JS_ASSERT(ins->type() == MIRType_Double); michael@0: JS_ASSERT(ins->lhs()->type() == MIRType_Double); michael@0: JS_ASSERT(ins->rhs()->type() == MIRType_Double); michael@0: michael@0: // Note: useRegisterAtStart is safe here, the temp is not a FP register. michael@0: LModD *lir = new(alloc()) LModD(useRegisterAtStart(ins->lhs()), useRegisterAtStart(ins->rhs()), michael@0: tempFixed(CallTempReg0)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: return lowerBinaryV(JSOP_MOD, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::lowerBinaryV(JSOp op, MBinaryInstruction *ins) michael@0: { michael@0: MDefinition *lhs = ins->getOperand(0); michael@0: MDefinition *rhs = ins->getOperand(1); michael@0: michael@0: JS_ASSERT(lhs->type() == MIRType_Value); michael@0: JS_ASSERT(rhs->type() == MIRType_Value); michael@0: michael@0: LBinaryV *lir = new(alloc()) LBinaryV(op); michael@0: if (!useBoxAtStart(lir, LBinaryV::LhsInput, lhs)) michael@0: return false; michael@0: if (!useBoxAtStart(lir, LBinaryV::RhsInput, rhs)) michael@0: return false; michael@0: if (!defineReturn(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitConcat(MConcat *ins) michael@0: { michael@0: MDefinition *lhs = ins->getOperand(0); michael@0: MDefinition *rhs = ins->getOperand(1); michael@0: michael@0: JS_ASSERT(lhs->type() == MIRType_String); michael@0: JS_ASSERT(rhs->type() == MIRType_String); michael@0: JS_ASSERT(ins->type() == MIRType_String); michael@0: michael@0: LConcat *lir = new(alloc()) LConcat(useFixedAtStart(lhs, CallTempReg0), michael@0: useFixedAtStart(rhs, CallTempReg1), michael@0: tempFixed(CallTempReg0), michael@0: tempFixed(CallTempReg1), michael@0: tempFixed(CallTempReg2), michael@0: tempFixed(CallTempReg3), michael@0: tempFixed(CallTempReg4)); michael@0: if (!defineFixed(lir, ins, LAllocation(AnyRegister(CallTempReg5)))) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitConcatPar(MConcatPar *ins) michael@0: { michael@0: MDefinition *cx = ins->forkJoinContext(); michael@0: MDefinition *lhs = ins->lhs(); michael@0: MDefinition *rhs = ins->rhs(); michael@0: michael@0: JS_ASSERT(lhs->type() == MIRType_String); michael@0: JS_ASSERT(rhs->type() == MIRType_String); michael@0: JS_ASSERT(ins->type() == MIRType_String); michael@0: michael@0: LConcatPar *lir = new(alloc()) LConcatPar(useFixed(cx, CallTempReg4), michael@0: useFixedAtStart(lhs, CallTempReg0), michael@0: useFixedAtStart(rhs, CallTempReg1), michael@0: tempFixed(CallTempReg0), michael@0: tempFixed(CallTempReg1), michael@0: tempFixed(CallTempReg2), michael@0: tempFixed(CallTempReg3)); michael@0: if (!defineFixed(lir, ins, LAllocation(AnyRegister(CallTempReg5)))) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCharCodeAt(MCharCodeAt *ins) michael@0: { michael@0: MDefinition *str = ins->getOperand(0); michael@0: MDefinition *idx = ins->getOperand(1); michael@0: michael@0: JS_ASSERT(str->type() == MIRType_String); michael@0: JS_ASSERT(idx->type() == MIRType_Int32); michael@0: michael@0: LCharCodeAt *lir = new(alloc()) LCharCodeAt(useRegister(str), useRegister(idx)); michael@0: if (!define(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitFromCharCode(MFromCharCode *ins) michael@0: { michael@0: MDefinition *code = ins->getOperand(0); michael@0: michael@0: JS_ASSERT(code->type() == MIRType_Int32); michael@0: michael@0: LFromCharCode *lir = new(alloc()) LFromCharCode(useRegister(code)); michael@0: if (!define(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStart(MStart *start) michael@0: { michael@0: // Create a snapshot that captures the initial state of the function. michael@0: LStart *lir = new(alloc()) LStart; michael@0: if (!assignSnapshot(lir)) michael@0: return false; michael@0: michael@0: if (start->startType() == MStart::StartType_Default) michael@0: lirGraph_.setEntrySnapshot(lir->snapshot()); michael@0: return add(lir); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNop(MNop *nop) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitOsrEntry(MOsrEntry *entry) michael@0: { michael@0: LOsrEntry *lir = new(alloc()) LOsrEntry; michael@0: return defineFixed(lir, entry, LAllocation(AnyRegister(OsrFrameReg))); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitOsrValue(MOsrValue *value) michael@0: { michael@0: LOsrValue *lir = new(alloc()) LOsrValue(useRegister(value->entry())); michael@0: return defineBox(lir, value); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitOsrReturnValue(MOsrReturnValue *value) michael@0: { michael@0: LOsrReturnValue *lir = new(alloc()) LOsrReturnValue(useRegister(value->entry())); michael@0: return defineBox(lir, value); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitOsrScopeChain(MOsrScopeChain *object) michael@0: { michael@0: LOsrScopeChain *lir = new(alloc()) LOsrScopeChain(useRegister(object->entry())); michael@0: return define(lir, object); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitOsrArgumentsObject(MOsrArgumentsObject *object) michael@0: { michael@0: LOsrArgumentsObject *lir = new(alloc()) LOsrArgumentsObject(useRegister(object->entry())); michael@0: return define(lir, object); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitToDouble(MToDouble *convert) michael@0: { michael@0: MDefinition *opd = convert->input(); michael@0: mozilla::DebugOnly conversion = convert->conversion(); michael@0: michael@0: switch (opd->type()) { michael@0: case MIRType_Value: michael@0: { michael@0: LValueToDouble *lir = new(alloc()) LValueToDouble(); michael@0: if (!useBox(lir, LValueToDouble::Input, opd)) michael@0: return false; michael@0: return assignSnapshot(lir) && define(lir, convert); michael@0: } michael@0: michael@0: case MIRType_Null: michael@0: JS_ASSERT(conversion != MToDouble::NumbersOnly && conversion != MToDouble::NonNullNonStringPrimitives); michael@0: return lowerConstantDouble(0, convert); michael@0: michael@0: case MIRType_Undefined: michael@0: JS_ASSERT(conversion != MToDouble::NumbersOnly); michael@0: return lowerConstantDouble(GenericNaN(), convert); michael@0: michael@0: case MIRType_Boolean: michael@0: JS_ASSERT(conversion != MToDouble::NumbersOnly); michael@0: /* FALLTHROUGH */ michael@0: michael@0: case MIRType_Int32: michael@0: { michael@0: LInt32ToDouble *lir = new(alloc()) LInt32ToDouble(useRegister(opd)); michael@0: return define(lir, convert); michael@0: } michael@0: michael@0: case MIRType_Float32: michael@0: { michael@0: LFloat32ToDouble *lir = new(alloc()) LFloat32ToDouble(useRegisterAtStart(opd)); michael@0: return define(lir, convert); michael@0: } michael@0: michael@0: case MIRType_Double: michael@0: return redefine(convert, opd); michael@0: michael@0: default: michael@0: // Objects might be effectful. michael@0: // Strings are complicated - we don't handle them yet. michael@0: MOZ_ASSUME_UNREACHABLE("unexpected type"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitToFloat32(MToFloat32 *convert) michael@0: { michael@0: MDefinition *opd = convert->input(); michael@0: mozilla::DebugOnly conversion = convert->conversion(); michael@0: michael@0: switch (opd->type()) { michael@0: case MIRType_Value: michael@0: { michael@0: LValueToFloat32 *lir = new(alloc()) LValueToFloat32(); michael@0: if (!useBox(lir, LValueToFloat32::Input, opd)) michael@0: return false; michael@0: return assignSnapshot(lir) && define(lir, convert); michael@0: } michael@0: michael@0: case MIRType_Null: michael@0: JS_ASSERT(conversion != MToFloat32::NonStringPrimitives); michael@0: return lowerConstantFloat32(0, convert); michael@0: michael@0: case MIRType_Undefined: michael@0: JS_ASSERT(conversion != MToFloat32::NumbersOnly); michael@0: return lowerConstantFloat32(GenericNaN(), convert); michael@0: michael@0: case MIRType_Boolean: michael@0: JS_ASSERT(conversion != MToFloat32::NumbersOnly); michael@0: /* FALLTHROUGH */ michael@0: michael@0: case MIRType_Int32: michael@0: { michael@0: LInt32ToFloat32 *lir = new(alloc()) LInt32ToFloat32(useRegister(opd)); michael@0: return define(lir, convert); michael@0: } michael@0: michael@0: case MIRType_Double: michael@0: { michael@0: LDoubleToFloat32 *lir = new(alloc()) LDoubleToFloat32(useRegister(opd)); michael@0: return define(lir, convert); michael@0: } michael@0: michael@0: case MIRType_Float32: michael@0: return redefine(convert, opd); michael@0: michael@0: default: michael@0: // Objects might be effectful. michael@0: // Strings are complicated - we don't handle them yet. michael@0: MOZ_ASSUME_UNREACHABLE("unexpected type"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitToInt32(MToInt32 *convert) michael@0: { michael@0: MDefinition *opd = convert->input(); michael@0: michael@0: switch (opd->type()) { michael@0: case MIRType_Value: michael@0: { michael@0: LValueToInt32 *lir = new(alloc()) LValueToInt32(tempDouble(), temp(), LValueToInt32::NORMAL); michael@0: if (!useBox(lir, LValueToInt32::Input, opd)) michael@0: return false; michael@0: return assignSnapshot(lir) && define(lir, convert) && assignSafepoint(lir, convert); michael@0: } michael@0: michael@0: case MIRType_Null: michael@0: return define(new(alloc()) LInteger(0), convert); michael@0: michael@0: case MIRType_Int32: michael@0: case MIRType_Boolean: michael@0: return redefine(convert, opd); michael@0: michael@0: case MIRType_Float32: michael@0: { michael@0: LFloat32ToInt32 *lir = new(alloc()) LFloat32ToInt32(useRegister(opd)); michael@0: return assignSnapshot(lir) && define(lir, convert); michael@0: } michael@0: michael@0: case MIRType_Double: michael@0: { michael@0: LDoubleToInt32 *lir = new(alloc()) LDoubleToInt32(useRegister(opd)); michael@0: return assignSnapshot(lir) && define(lir, convert); michael@0: } michael@0: michael@0: case MIRType_String: michael@0: case MIRType_Object: michael@0: case MIRType_Undefined: michael@0: // Objects might be effectful. Undefined coerces to NaN, not int32. michael@0: MOZ_ASSUME_UNREACHABLE("ToInt32 invalid input type"); michael@0: return false; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected type"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitTruncateToInt32(MTruncateToInt32 *truncate) michael@0: { michael@0: MDefinition *opd = truncate->input(); michael@0: michael@0: switch (opd->type()) { michael@0: case MIRType_Value: michael@0: { michael@0: LValueToInt32 *lir = new(alloc()) LValueToInt32(tempDouble(), temp(), LValueToInt32::TRUNCATE); michael@0: if (!useBox(lir, LValueToInt32::Input, opd)) michael@0: return false; michael@0: return assignSnapshot(lir) && define(lir, truncate) && assignSafepoint(lir, truncate); michael@0: } michael@0: michael@0: case MIRType_Null: michael@0: case MIRType_Undefined: michael@0: return define(new(alloc()) LInteger(0), truncate); michael@0: michael@0: case MIRType_Int32: michael@0: case MIRType_Boolean: michael@0: return redefine(truncate, opd); michael@0: michael@0: case MIRType_Double: michael@0: return lowerTruncateDToInt32(truncate); michael@0: michael@0: case MIRType_Float32: michael@0: return lowerTruncateFToInt32(truncate); michael@0: michael@0: default: michael@0: // Objects might be effectful. michael@0: // Strings are complicated - we don't handle them yet. michael@0: MOZ_ASSUME_UNREACHABLE("unexpected type"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitToString(MToString *ins) michael@0: { michael@0: MDefinition *opd = ins->input(); michael@0: michael@0: switch (opd->type()) { michael@0: case MIRType_Null: { michael@0: const JSAtomState &names = GetIonContext()->runtime->names(); michael@0: LPointer *lir = new(alloc()) LPointer(names.null); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: case MIRType_Undefined: { michael@0: const JSAtomState &names = GetIonContext()->runtime->names(); michael@0: LPointer *lir = new(alloc()) LPointer(names.undefined); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: case MIRType_Boolean: { michael@0: LBooleanToString *lir = new(alloc()) LBooleanToString(useRegister(opd)); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: case MIRType_Double: { michael@0: LDoubleToString *lir = new(alloc()) LDoubleToString(useRegister(opd), temp()); michael@0: michael@0: if (!define(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: case MIRType_Int32: { michael@0: LIntToString *lir = new(alloc()) LIntToString(useRegister(opd)); michael@0: michael@0: if (!define(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: case MIRType_Value: { michael@0: JS_ASSERT(!opd->mightBeType(MIRType_Object)); michael@0: LPrimitiveToString *lir = new(alloc()) LPrimitiveToString(tempToUnbox()); michael@0: if (!useBox(lir, LPrimitiveToString::Input, opd)) michael@0: return false; michael@0: if (!define(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: default: michael@0: // Objects might be effectful. (see ToPrimitive) michael@0: MOZ_ASSUME_UNREACHABLE("unexpected type"); michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: MustCloneRegExpForCall(MCall *call, uint32_t useIndex) michael@0: { michael@0: // We have a regex literal flowing into a call. Return |false| iff michael@0: // this is a native call that does not let the regex escape. michael@0: michael@0: JSFunction *target = call->getSingleTarget(); michael@0: if (!target || !target->isNative()) michael@0: return true; michael@0: michael@0: if (useIndex == MCall::IndexOfThis() && michael@0: (target->native() == regexp_exec || target->native() == regexp_test)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: if (useIndex == MCall::IndexOfArgument(0) && michael@0: (target->native() == str_split || michael@0: target->native() == str_replace || michael@0: target->native() == str_match || michael@0: target->native() == str_search)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: michael@0: static bool michael@0: MustCloneRegExp(MRegExp *regexp) michael@0: { michael@0: if (regexp->mustClone()) michael@0: return true; michael@0: michael@0: // If this regex literal only flows into known natives that don't let michael@0: // it escape, we don't have to clone it. michael@0: michael@0: for (MUseIterator iter(regexp->usesBegin()); iter != regexp->usesEnd(); iter++) { michael@0: MNode *node = iter->consumer(); michael@0: if (!node->isDefinition()) michael@0: return true; michael@0: michael@0: MDefinition *def = node->toDefinition(); michael@0: if (def->isRegExpTest() && iter->index() == 1) { michael@0: // Optimized RegExp.prototype.test. michael@0: JS_ASSERT(def->toRegExpTest()->regexp() == regexp); michael@0: continue; michael@0: } michael@0: michael@0: if (def->isCall() && !MustCloneRegExpForCall(def->toCall(), iter->index())) michael@0: continue; michael@0: michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRegExp(MRegExp *ins) michael@0: { michael@0: if (!MustCloneRegExp(ins)) { michael@0: RegExpObject *source = ins->source(); michael@0: return define(new(alloc()) LPointer(source), ins); michael@0: } michael@0: michael@0: LRegExp *lir = new(alloc()) LRegExp(); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRegExpExec(MRegExpExec *ins) michael@0: { michael@0: JS_ASSERT(ins->regexp()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->string()->type() == MIRType_String); michael@0: michael@0: LRegExpExec *lir = new(alloc()) LRegExpExec(useRegisterAtStart(ins->regexp()), michael@0: useRegisterAtStart(ins->string())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRegExpTest(MRegExpTest *ins) michael@0: { michael@0: JS_ASSERT(ins->regexp()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->string()->type() == MIRType_String); michael@0: michael@0: LRegExpTest *lir = new(alloc()) LRegExpTest(useRegisterAtStart(ins->regexp()), michael@0: useRegisterAtStart(ins->string())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRegExpReplace(MRegExpReplace *ins) michael@0: { michael@0: JS_ASSERT(ins->pattern()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->string()->type() == MIRType_String); michael@0: JS_ASSERT(ins->replacement()->type() == MIRType_String); michael@0: michael@0: LRegExpReplace *lir = new(alloc()) LRegExpReplace(useRegisterOrConstantAtStart(ins->string()), michael@0: useRegisterAtStart(ins->pattern()), michael@0: useRegisterOrConstantAtStart(ins->replacement())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStringReplace(MStringReplace *ins) michael@0: { michael@0: JS_ASSERT(ins->pattern()->type() == MIRType_String); michael@0: JS_ASSERT(ins->string()->type() == MIRType_String); michael@0: JS_ASSERT(ins->replacement()->type() == MIRType_String); michael@0: michael@0: LStringReplace *lir = new(alloc()) LStringReplace(useRegisterOrConstantAtStart(ins->string()), michael@0: useRegisterAtStart(ins->pattern()), michael@0: useRegisterOrConstantAtStart(ins->replacement())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLambda(MLambda *ins) michael@0: { michael@0: if (ins->info().singletonType || ins->info().useNewTypeForClone) { michael@0: // If the function has a singleton type, this instruction will only be michael@0: // executed once so we don't bother inlining it. michael@0: // michael@0: // If UseNewTypeForClone is true, we will assign a singleton type to michael@0: // the clone and we have to clone the script, we can't do that inline. michael@0: LLambdaForSingleton *lir = new(alloc()) LLambdaForSingleton(useRegisterAtStart(ins->scopeChain())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: LLambda *lir = new(alloc()) LLambda(useRegister(ins->scopeChain()), temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLambdaArrow(MLambdaArrow *ins) michael@0: { michael@0: MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object); michael@0: MOZ_ASSERT(ins->thisDef()->type() == MIRType_Value); michael@0: michael@0: LLambdaArrow *lir = new(alloc()) LLambdaArrow(useRegister(ins->scopeChain()), temp()); michael@0: if (!useBox(lir, LLambdaArrow::ThisValue, ins->thisDef())) michael@0: return false; michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLambdaPar(MLambdaPar *ins) michael@0: { michael@0: JS_ASSERT(!ins->info().singletonType); michael@0: JS_ASSERT(!ins->info().useNewTypeForClone); michael@0: LLambdaPar *lir = new(alloc()) LLambdaPar(useRegister(ins->forkJoinContext()), michael@0: useRegister(ins->scopeChain()), michael@0: temp(), temp()); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitImplicitThis(MImplicitThis *ins) michael@0: { michael@0: JS_ASSERT(ins->callee()->type() == MIRType_Object); michael@0: michael@0: LImplicitThis *lir = new(alloc()) LImplicitThis(useRegister(ins->callee())); michael@0: return assignSnapshot(lir) && defineBox(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSlots(MSlots *ins) michael@0: { michael@0: return define(new(alloc()) LSlots(useRegisterAtStart(ins->object())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitElements(MElements *ins) michael@0: { michael@0: return define(new(alloc()) LElements(useRegisterAtStart(ins->object())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitConstantElements(MConstantElements *ins) michael@0: { michael@0: return define(new(alloc()) LPointer(ins->value(), LPointer::NON_GC_THING), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitConvertElementsToDoubles(MConvertElementsToDoubles *ins) michael@0: { michael@0: LInstruction *check = new(alloc()) LConvertElementsToDoubles(useRegister(ins->elements())); michael@0: return add(check, ins) && assignSafepoint(check, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitMaybeToDoubleElement(MMaybeToDoubleElement *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->value()->type() == MIRType_Int32); michael@0: michael@0: LMaybeToDoubleElement *lir = new(alloc()) LMaybeToDoubleElement(useRegisterAtStart(ins->elements()), michael@0: useRegisterAtStart(ins->value()), michael@0: tempDouble()); michael@0: return defineBox(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLoadSlot(MLoadSlot *ins) michael@0: { michael@0: switch (ins->type()) { michael@0: case MIRType_Value: michael@0: return defineBox(new(alloc()) LLoadSlotV(useRegister(ins->slots())), ins); michael@0: michael@0: case MIRType_Undefined: michael@0: case MIRType_Null: michael@0: MOZ_ASSUME_UNREACHABLE("typed load must have a payload"); michael@0: michael@0: default: michael@0: return define(new(alloc()) LLoadSlotT(useRegister(ins->slots())), ins); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitFunctionEnvironment(MFunctionEnvironment *ins) michael@0: { michael@0: return define(new(alloc()) LFunctionEnvironment(useRegisterAtStart(ins->function())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitForkJoinContext(MForkJoinContext *ins) michael@0: { michael@0: LForkJoinContext *lir = new(alloc()) LForkJoinContext(tempFixed(CallTempReg0)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGuardThreadExclusive(MGuardThreadExclusive *ins) michael@0: { michael@0: // FIXME (Bug 956281) -- For now, we always generate the most michael@0: // general form of write guard check. we could employ TI feedback michael@0: // to optimize this if we know that the object being tested is a michael@0: // typed object or know that it is definitely NOT a typed object. michael@0: LGuardThreadExclusive *lir = michael@0: new(alloc()) LGuardThreadExclusive(useFixed(ins->forkJoinContext(), CallTempReg0), michael@0: useFixed(ins->object(), CallTempReg1), michael@0: tempFixed(CallTempReg2)); michael@0: lir->setMir(ins); michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInterruptCheck(MInterruptCheck *ins) michael@0: { michael@0: // Implicit interrupt checks require asm.js signal handlers to be michael@0: // installed. ARM does not yet use implicit interrupt checks, see michael@0: // bug 864220. michael@0: #ifndef JS_CODEGEN_ARM michael@0: if (GetIonContext()->runtime->signalHandlersInstalled()) { michael@0: LInterruptCheckImplicit *lir = new(alloc()) LInterruptCheckImplicit(); michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: #endif michael@0: michael@0: LInterruptCheck *lir = new(alloc()) LInterruptCheck(); michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInterruptCheckPar(MInterruptCheckPar *ins) michael@0: { michael@0: LInterruptCheckPar *lir = michael@0: new(alloc()) LInterruptCheckPar(useRegister(ins->forkJoinContext()), temp()); michael@0: if (!add(lir, ins)) michael@0: return false; michael@0: if (!assignSafepoint(lir, ins)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewPar(MNewPar *ins) michael@0: { michael@0: LNewPar *lir = new(alloc()) LNewPar(useRegister(ins->forkJoinContext()), temp(), temp()); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNewDenseArrayPar(MNewDenseArrayPar *ins) michael@0: { michael@0: LNewDenseArrayPar *lir = michael@0: new(alloc()) LNewDenseArrayPar(useFixed(ins->forkJoinContext(), CallTempReg0), michael@0: useFixed(ins->length(), CallTempReg1), michael@0: tempFixed(CallTempReg2), michael@0: tempFixed(CallTempReg3), michael@0: tempFixed(CallTempReg4)); michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStoreSlot(MStoreSlot *ins) michael@0: { michael@0: LInstruction *lir; michael@0: michael@0: switch (ins->value()->type()) { michael@0: case MIRType_Value: michael@0: lir = new(alloc()) LStoreSlotV(useRegister(ins->slots())); michael@0: if (!useBox(lir, LStoreSlotV::Value, ins->value())) michael@0: return false; michael@0: return add(lir, ins); michael@0: michael@0: case MIRType_Double: michael@0: return add(new(alloc()) LStoreSlotT(useRegister(ins->slots()), useRegister(ins->value())), ins); michael@0: michael@0: case MIRType_Float32: michael@0: MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be stored in a slot."); michael@0: michael@0: default: michael@0: return add(new(alloc()) LStoreSlotT(useRegister(ins->slots()), useRegisterOrConstant(ins->value())), michael@0: ins); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitFilterTypeSet(MFilterTypeSet *ins) michael@0: { michael@0: return redefine(ins, ins->input()); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitTypeBarrier(MTypeBarrier *ins) michael@0: { michael@0: // Requesting a non-GC pointer is safe here since we never re-enter C++ michael@0: // from inside a type barrier test. michael@0: michael@0: const types::TemporaryTypeSet *types = ins->resultTypeSet(); michael@0: bool needTemp = !types->unknownObject() && types->getObjectCount() > 0; michael@0: michael@0: MIRType inputType = ins->getOperand(0)->type(); michael@0: DebugOnly outputType = ins->type(); michael@0: michael@0: JS_ASSERT(inputType == outputType); michael@0: michael@0: // Handle typebarrier that will always bail. michael@0: // (Emit LBail for visibility). michael@0: if (ins->alwaysBails()) { michael@0: LBail *bail = new(alloc()) LBail(); michael@0: if (!assignSnapshot(bail)) michael@0: return false; michael@0: return redefine(ins, ins->input()) && add(bail, ins); michael@0: } michael@0: michael@0: // Handle typebarrier with Value as input. michael@0: if (inputType == MIRType_Value) { michael@0: LDefinition tmp = needTemp ? temp() : tempToUnbox(); michael@0: LTypeBarrierV *barrier = new(alloc()) LTypeBarrierV(tmp); michael@0: if (!useBox(barrier, LTypeBarrierV::Input, ins->input())) michael@0: return false; michael@0: if (!assignSnapshot(barrier)) michael@0: return false; michael@0: return redefine(ins, ins->input()) && add(barrier, ins); michael@0: } michael@0: michael@0: // Handle typebarrier with specific TypeObject/SingleObjects. michael@0: if (inputType == MIRType_Object && !types->hasType(types::Type::AnyObjectType())) michael@0: { michael@0: LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp(); michael@0: LTypeBarrierO *barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp); michael@0: if (!assignSnapshot(barrier)) michael@0: return false; michael@0: return redefine(ins, ins->getOperand(0)) && add(barrier, ins); michael@0: } michael@0: michael@0: // Handle remaining cases: No-op, unbox did everything. michael@0: return redefine(ins, ins->getOperand(0)); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitMonitorTypes(MMonitorTypes *ins) michael@0: { michael@0: // Requesting a non-GC pointer is safe here since we never re-enter C++ michael@0: // from inside a type check. michael@0: michael@0: const types::TemporaryTypeSet *types = ins->typeSet(); michael@0: bool needTemp = !types->unknownObject() && types->getObjectCount() > 0; michael@0: LDefinition tmp = needTemp ? temp() : tempToUnbox(); michael@0: michael@0: LMonitorTypes *lir = new(alloc()) LMonitorTypes(tmp); michael@0: if (!useBox(lir, LMonitorTypes::Input, ins->input())) michael@0: return false; michael@0: return assignSnapshot(lir, Bailout_Normal) && add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins) michael@0: { michael@0: #ifdef JSGC_GENERATIONAL michael@0: switch (ins->value()->type()) { michael@0: case MIRType_Object: { michael@0: LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); michael@0: LPostWriteBarrierO *lir = michael@0: new(alloc()) LPostWriteBarrierO(useRegisterOrConstant(ins->object()), michael@0: useRegister(ins->value()), tmp); michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: case MIRType_Value: { michael@0: LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); michael@0: LPostWriteBarrierV *lir = michael@0: new(alloc()) LPostWriteBarrierV(useRegisterOrConstant(ins->object()), tmp); michael@0: if (!useBox(lir, LPostWriteBarrierV::Input, ins->value())) michael@0: return false; michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: default: michael@0: // Currently, only objects can be in the nursery. Other instruction michael@0: // types cannot hold nursery pointers. michael@0: return true; michael@0: } michael@0: #endif // JSGC_GENERATIONAL michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitArrayLength(MArrayLength *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: return define(new(alloc()) LArrayLength(useRegisterAtStart(ins->elements())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSetArrayLength(MSetArrayLength *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: michael@0: JS_ASSERT(ins->index()->isConstant()); michael@0: return add(new(alloc()) LSetArrayLength(useRegister(ins->elements()), michael@0: useRegisterOrConstant(ins->index())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitTypedArrayLength(MTypedArrayLength *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: return define(new(alloc()) LTypedArrayLength(useRegisterAtStart(ins->object())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitTypedArrayElements(MTypedArrayElements *ins) michael@0: { michael@0: JS_ASSERT(ins->type() == MIRType_Elements); michael@0: return define(new(alloc()) LTypedArrayElements(useRegisterAtStart(ins->object())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitTypedObjectElements(MTypedObjectElements *ins) michael@0: { michael@0: JS_ASSERT(ins->type() == MIRType_Elements); michael@0: return define(new(alloc()) LTypedObjectElements(useRegisterAtStart(ins->object())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSetTypedObjectOffset(MSetTypedObjectOffset *ins) michael@0: { michael@0: return add(new(alloc()) LSetTypedObjectOffset( michael@0: useRegister(ins->object()), michael@0: useRegister(ins->offset()), michael@0: temp()), michael@0: ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInitializedLength(MInitializedLength *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: return define(new(alloc()) LInitializedLength(useRegisterAtStart(ins->elements())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSetInitializedLength(MSetInitializedLength *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: michael@0: JS_ASSERT(ins->index()->isConstant()); michael@0: return add(new(alloc()) LSetInitializedLength(useRegister(ins->elements()), michael@0: useRegisterOrConstant(ins->index())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNot(MNot *ins) michael@0: { michael@0: MDefinition *op = ins->operand(); michael@0: michael@0: // String is converted to length of string in the type analysis phase (see michael@0: // TestPolicy). michael@0: JS_ASSERT(op->type() != MIRType_String); michael@0: michael@0: // - boolean: x xor 1 michael@0: // - int32: LCompare(x, 0) michael@0: // - double: LCompare(x, 0) michael@0: // - null or undefined: true michael@0: // - object: false if it never emulates undefined, else LNotO(x) michael@0: switch (op->type()) { michael@0: case MIRType_Boolean: { michael@0: MConstant *cons = MConstant::New(alloc(), Int32Value(1)); michael@0: ins->block()->insertBefore(ins, cons); michael@0: return lowerForALU(new(alloc()) LBitOpI(JSOP_BITXOR), ins, op, cons); michael@0: } michael@0: case MIRType_Int32: { michael@0: return define(new(alloc()) LNotI(useRegisterAtStart(op)), ins); michael@0: } michael@0: case MIRType_Double: michael@0: return define(new(alloc()) LNotD(useRegister(op)), ins); michael@0: case MIRType_Float32: michael@0: return define(new(alloc()) LNotF(useRegister(op)), ins); michael@0: case MIRType_Undefined: michael@0: case MIRType_Null: michael@0: return define(new(alloc()) LInteger(1), ins); michael@0: case MIRType_Object: { michael@0: // Objects that don't emulate undefined can be constant-folded. michael@0: if (!ins->operandMightEmulateUndefined()) michael@0: return define(new(alloc()) LInteger(0), ins); michael@0: // All others require further work. michael@0: return define(new(alloc()) LNotO(useRegister(op)), ins); michael@0: } michael@0: case MIRType_Value: { michael@0: LDefinition temp0, temp1; michael@0: if (ins->operandMightEmulateUndefined()) { michael@0: temp0 = temp(); michael@0: temp1 = temp(); michael@0: } else { michael@0: temp0 = LDefinition::BogusTemp(); michael@0: temp1 = LDefinition::BogusTemp(); michael@0: } michael@0: michael@0: LNotV *lir = new(alloc()) LNotV(tempDouble(), temp0, temp1); michael@0: if (!useBox(lir, LNotV::Input, op)) michael@0: return false; michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected MIRType."); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitNeuterCheck(MNeuterCheck *ins) michael@0: { michael@0: LNeuterCheck *chk = new(alloc()) LNeuterCheck(useRegister(ins->object()), michael@0: temp()); michael@0: if (!assignSnapshot(chk, Bailout_BoundsCheck)) michael@0: return false; michael@0: return redefine(ins, ins->input()) && add(chk, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitBoundsCheck(MBoundsCheck *ins) michael@0: { michael@0: LInstruction *check; michael@0: if (ins->minimum() || ins->maximum()) { michael@0: check = new(alloc()) LBoundsCheckRange(useRegisterOrConstant(ins->index()), michael@0: useAny(ins->length()), michael@0: temp()); michael@0: } else { michael@0: check = new(alloc()) LBoundsCheck(useRegisterOrConstant(ins->index()), michael@0: useAnyOrConstant(ins->length())); michael@0: } michael@0: return assignSnapshot(check, Bailout_BoundsCheck) && add(check, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitBoundsCheckLower(MBoundsCheckLower *ins) michael@0: { michael@0: if (!ins->fallible()) michael@0: return true; michael@0: michael@0: LInstruction *check = new(alloc()) LBoundsCheckLower(useRegister(ins->index())); michael@0: return assignSnapshot(check, Bailout_BoundsCheck) && add(check, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInArray(MInArray *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: JS_ASSERT(ins->initLength()->type() == MIRType_Int32); michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->type() == MIRType_Boolean); michael@0: michael@0: LAllocation object; michael@0: if (ins->needsNegativeIntCheck()) michael@0: object = useRegister(ins->object()); michael@0: else michael@0: object = LConstantIndex::Bogus(); michael@0: michael@0: LInArray *lir = new(alloc()) LInArray(useRegister(ins->elements()), michael@0: useRegisterOrConstant(ins->index()), michael@0: useRegister(ins->initLength()), michael@0: object); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLoadElement(MLoadElement *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: michael@0: switch (ins->type()) { michael@0: case MIRType_Value: michael@0: { michael@0: LLoadElementV *lir = new(alloc()) LLoadElementV(useRegister(ins->elements()), michael@0: useRegisterOrConstant(ins->index())); michael@0: if (ins->fallible() && !assignSnapshot(lir)) michael@0: return false; michael@0: return defineBox(lir, ins); michael@0: } michael@0: case MIRType_Undefined: michael@0: case MIRType_Null: michael@0: MOZ_ASSUME_UNREACHABLE("typed load must have a payload"); michael@0: michael@0: default: michael@0: { michael@0: LLoadElementT *lir = new(alloc()) LLoadElementT(useRegister(ins->elements()), michael@0: useRegisterOrConstant(ins->index())); michael@0: if (ins->fallible() && !assignSnapshot(lir)) michael@0: return false; michael@0: return define(lir, ins); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLoadElementHole(MLoadElementHole *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: JS_ASSERT(ins->initLength()->type() == MIRType_Int32); michael@0: JS_ASSERT(ins->type() == MIRType_Value); michael@0: michael@0: LLoadElementHole *lir = new(alloc()) LLoadElementHole(useRegister(ins->elements()), michael@0: useRegisterOrConstant(ins->index()), michael@0: useRegister(ins->initLength())); michael@0: if (ins->needsNegativeIntCheck() && !assignSnapshot(lir)) michael@0: return false; michael@0: return defineBox(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStoreElement(MStoreElement *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: michael@0: const LUse elements = useRegister(ins->elements()); michael@0: const LAllocation index = useRegisterOrConstant(ins->index()); michael@0: michael@0: switch (ins->value()->type()) { michael@0: case MIRType_Value: michael@0: { michael@0: LInstruction *lir = new(alloc()) LStoreElementV(elements, index); michael@0: if (ins->fallible() && !assignSnapshot(lir)) michael@0: return false; michael@0: if (!useBox(lir, LStoreElementV::Value, ins->value())) michael@0: return false; michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: default: michael@0: { michael@0: const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); michael@0: LInstruction *lir = new(alloc()) LStoreElementT(elements, index, value); michael@0: if (ins->fallible() && !assignSnapshot(lir)) michael@0: return false; michael@0: return add(lir, ins); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStoreElementHole(MStoreElementHole *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: michael@0: const LUse object = useRegister(ins->object()); michael@0: const LUse elements = useRegister(ins->elements()); michael@0: const LAllocation index = useRegisterOrConstant(ins->index()); michael@0: michael@0: LInstruction *lir; michael@0: switch (ins->value()->type()) { michael@0: case MIRType_Value: michael@0: lir = new(alloc()) LStoreElementHoleV(object, elements, index); michael@0: if (!useBox(lir, LStoreElementHoleV::Value, ins->value())) michael@0: return false; michael@0: break; michael@0: michael@0: default: michael@0: { michael@0: const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); michael@0: lir = new(alloc()) LStoreElementHoleT(object, elements, index, value); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitEffectiveAddress(MEffectiveAddress *ins) michael@0: { michael@0: return define(new(alloc()) LEffectiveAddress(useRegister(ins->base()), useRegister(ins->index())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitArrayPopShift(MArrayPopShift *ins) michael@0: { michael@0: LUse object = useRegister(ins->object()); michael@0: michael@0: switch (ins->type()) { michael@0: case MIRType_Value: michael@0: { michael@0: LArrayPopShiftV *lir = new(alloc()) LArrayPopShiftV(object, temp(), temp()); michael@0: return defineBox(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: case MIRType_Undefined: michael@0: case MIRType_Null: michael@0: MOZ_ASSUME_UNREACHABLE("typed load must have a payload"); michael@0: michael@0: default: michael@0: { michael@0: LArrayPopShiftT *lir = new(alloc()) LArrayPopShiftT(object, temp(), temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitArrayPush(MArrayPush *ins) michael@0: { michael@0: JS_ASSERT(ins->type() == MIRType_Int32); michael@0: michael@0: LUse object = useRegister(ins->object()); michael@0: michael@0: switch (ins->value()->type()) { michael@0: case MIRType_Value: michael@0: { michael@0: LArrayPushV *lir = new(alloc()) LArrayPushV(object, temp()); michael@0: if (!useBox(lir, LArrayPushV::Value, ins->value())) michael@0: return false; michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: default: michael@0: { michael@0: const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); michael@0: LArrayPushT *lir = new(alloc()) LArrayPushT(object, value, temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitArrayConcat(MArrayConcat *ins) michael@0: { michael@0: JS_ASSERT(ins->type() == MIRType_Object); michael@0: JS_ASSERT(ins->lhs()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->rhs()->type() == MIRType_Object); michael@0: michael@0: LArrayConcat *lir = new(alloc()) LArrayConcat(useFixed(ins->lhs(), CallTempReg1), michael@0: useFixed(ins->rhs(), CallTempReg2), michael@0: tempFixed(CallTempReg3), michael@0: tempFixed(CallTempReg4)); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStringSplit(MStringSplit *ins) michael@0: { michael@0: JS_ASSERT(ins->type() == MIRType_Object); michael@0: JS_ASSERT(ins->string()->type() == MIRType_String); michael@0: JS_ASSERT(ins->separator()->type() == MIRType_String); michael@0: michael@0: LStringSplit *lir = new(alloc()) LStringSplit(useRegisterAtStart(ins->string()), michael@0: useRegisterAtStart(ins->separator())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLoadTypedArrayElement(MLoadTypedArrayElement *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: michael@0: const LUse elements = useRegister(ins->elements()); michael@0: const LAllocation index = useRegisterOrConstant(ins->index()); michael@0: michael@0: JS_ASSERT(IsNumberType(ins->type())); michael@0: michael@0: // We need a temp register for Uint32Array with known double result. michael@0: LDefinition tempDef = LDefinition::BogusTemp(); michael@0: if (ins->arrayType() == ScalarTypeDescr::TYPE_UINT32 && IsFloatingPointType(ins->type())) michael@0: tempDef = temp(); michael@0: michael@0: LLoadTypedArrayElement *lir = new(alloc()) LLoadTypedArrayElement(elements, index, tempDef); michael@0: if (ins->fallible() && !assignSnapshot(lir)) michael@0: return false; michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitClampToUint8(MClampToUint8 *ins) michael@0: { michael@0: MDefinition *in = ins->input(); michael@0: michael@0: switch (in->type()) { michael@0: case MIRType_Boolean: michael@0: return redefine(ins, in); michael@0: michael@0: case MIRType_Int32: michael@0: return defineReuseInput(new(alloc()) LClampIToUint8(useRegisterAtStart(in)), ins, 0); michael@0: michael@0: case MIRType_Double: michael@0: return define(new(alloc()) LClampDToUint8(useRegisterAtStart(in), tempCopy(in, 0)), ins); michael@0: michael@0: case MIRType_Value: michael@0: { michael@0: LClampVToUint8 *lir = new(alloc()) LClampVToUint8(tempDouble()); michael@0: if (!useBox(lir, LClampVToUint8::Input, in)) michael@0: return false; michael@0: return assignSnapshot(lir) && define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected type"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: michael@0: JS_ASSERT(ins->type() == MIRType_Value); michael@0: michael@0: const LUse object = useRegister(ins->object()); michael@0: const LAllocation index = useRegisterOrConstant(ins->index()); michael@0: michael@0: LLoadTypedArrayElementHole *lir = new(alloc()) LLoadTypedArrayElementHole(object, index); michael@0: if (ins->fallible() && !assignSnapshot(lir)) michael@0: return false; michael@0: return defineBox(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic *ins) michael@0: { michael@0: LLoadTypedArrayElementStatic *lir = michael@0: new(alloc()) LLoadTypedArrayElementStatic(useRegisterAtStart(ins->ptr())); michael@0: michael@0: if (ins->fallible() && !assignSnapshot(lir)) michael@0: return false; michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: michael@0: if (ins->isFloatArray()) { michael@0: DebugOnly optimizeFloat32 = allowFloat32Optimizations(); michael@0: JS_ASSERT_IF(optimizeFloat32 && ins->arrayType() == ScalarTypeDescr::TYPE_FLOAT32, michael@0: ins->value()->type() == MIRType_Float32); michael@0: JS_ASSERT_IF(!optimizeFloat32 || ins->arrayType() == ScalarTypeDescr::TYPE_FLOAT64, michael@0: ins->value()->type() == MIRType_Double); michael@0: } else { michael@0: JS_ASSERT(ins->value()->type() == MIRType_Int32); michael@0: } michael@0: michael@0: LUse elements = useRegister(ins->elements()); michael@0: LAllocation index = useRegisterOrConstant(ins->index()); michael@0: LAllocation value; michael@0: michael@0: // For byte arrays, the value has to be in a byte register on x86. michael@0: if (ins->isByteArray()) michael@0: value = useByteOpRegisterOrNonDoubleConstant(ins->value()); michael@0: else michael@0: value = useRegisterOrNonDoubleConstant(ins->value()); michael@0: return add(new(alloc()) LStoreTypedArrayElement(elements, index, value), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins) michael@0: { michael@0: JS_ASSERT(ins->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: JS_ASSERT(ins->length()->type() == MIRType_Int32); michael@0: michael@0: if (ins->isFloatArray()) { michael@0: DebugOnly optimizeFloat32 = allowFloat32Optimizations(); michael@0: JS_ASSERT_IF(optimizeFloat32 && ins->arrayType() == ScalarTypeDescr::TYPE_FLOAT32, michael@0: ins->value()->type() == MIRType_Float32); michael@0: JS_ASSERT_IF(!optimizeFloat32 || ins->arrayType() == ScalarTypeDescr::TYPE_FLOAT64, michael@0: ins->value()->type() == MIRType_Double); michael@0: } else { michael@0: JS_ASSERT(ins->value()->type() == MIRType_Int32); michael@0: } michael@0: michael@0: LUse elements = useRegister(ins->elements()); michael@0: LAllocation length = useAnyOrConstant(ins->length()); michael@0: LAllocation index = useRegisterOrConstant(ins->index()); michael@0: LAllocation value; michael@0: michael@0: // For byte arrays, the value has to be in a byte register on x86. michael@0: if (ins->isByteArray()) michael@0: value = useByteOpRegisterOrNonDoubleConstant(ins->value()); michael@0: else michael@0: value = useRegisterOrNonDoubleConstant(ins->value()); michael@0: return add(new(alloc()) LStoreTypedArrayElementHole(elements, length, index, value), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: michael@0: if (ins->type() == MIRType_Value) { michael@0: LLoadFixedSlotV *lir = new(alloc()) LLoadFixedSlotV(useRegister(ins->object())); michael@0: return defineBox(lir, ins); michael@0: } michael@0: michael@0: LLoadFixedSlotT *lir = new(alloc()) LLoadFixedSlotT(useRegister(ins->object())); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStoreFixedSlot(MStoreFixedSlot *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: michael@0: if (ins->value()->type() == MIRType_Value) { michael@0: LStoreFixedSlotV *lir = new(alloc()) LStoreFixedSlotV(useRegister(ins->object())); michael@0: michael@0: if (!useBox(lir, LStoreFixedSlotV::Value, ins->value())) michael@0: return false; michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: LStoreFixedSlotT *lir = new(alloc()) LStoreFixedSlotT(useRegister(ins->object()), michael@0: useRegisterOrConstant(ins->value())); michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGetNameCache(MGetNameCache *ins) michael@0: { michael@0: JS_ASSERT(ins->scopeObj()->type() == MIRType_Object); michael@0: michael@0: LGetNameCache *lir = new(alloc()) LGetNameCache(useRegister(ins->scopeObj())); michael@0: if (!defineBox(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallGetIntrinsicValue(MCallGetIntrinsicValue *ins) michael@0: { michael@0: LCallGetIntrinsicValue *lir = new(alloc()) LCallGetIntrinsicValue(); michael@0: if (!defineReturn(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallsiteCloneCache(MCallsiteCloneCache *ins) michael@0: { michael@0: JS_ASSERT(ins->callee()->type() == MIRType_Object); michael@0: michael@0: LCallsiteCloneCache *lir = new(alloc()) LCallsiteCloneCache(useRegister(ins->callee())); michael@0: if (!define(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGetPropertyCache(MGetPropertyCache *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: if (ins->type() == MIRType_Value) { michael@0: LGetPropertyCacheV *lir = new(alloc()) LGetPropertyCacheV(useRegister(ins->object())); michael@0: if (!defineBox(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: LGetPropertyCacheT *lir = new(alloc()) LGetPropertyCacheT(useRegister(ins->object()), michael@0: tempForDispatchCache(ins->type())); michael@0: if (!define(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGetPropertyPolymorphic(MGetPropertyPolymorphic *ins) michael@0: { michael@0: JS_ASSERT(ins->obj()->type() == MIRType_Object); michael@0: michael@0: if (ins->type() == MIRType_Value) { michael@0: LGetPropertyPolymorphicV *lir = new(alloc()) LGetPropertyPolymorphicV(useRegister(ins->obj())); michael@0: return assignSnapshot(lir, Bailout_ShapeGuard) && defineBox(lir, ins); michael@0: } michael@0: michael@0: LDefinition maybeTemp = (ins->type() == MIRType_Double) ? temp() : LDefinition::BogusTemp(); michael@0: LGetPropertyPolymorphicT *lir = new(alloc()) LGetPropertyPolymorphicT(useRegister(ins->obj()), maybeTemp); michael@0: return assignSnapshot(lir, Bailout_ShapeGuard) && define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSetPropertyPolymorphic(MSetPropertyPolymorphic *ins) michael@0: { michael@0: JS_ASSERT(ins->obj()->type() == MIRType_Object); michael@0: michael@0: if (ins->value()->type() == MIRType_Value) { michael@0: LSetPropertyPolymorphicV *lir = new(alloc()) LSetPropertyPolymorphicV(useRegister(ins->obj()), temp()); michael@0: if (!useBox(lir, LSetPropertyPolymorphicV::Value, ins->value())) michael@0: return false; michael@0: return assignSnapshot(lir, Bailout_ShapeGuard) && add(lir, ins); michael@0: } michael@0: michael@0: LAllocation value = useRegisterOrConstant(ins->value()); michael@0: LSetPropertyPolymorphicT *lir = michael@0: new(alloc()) LSetPropertyPolymorphicT(useRegister(ins->obj()), value, ins->value()->type(), temp()); michael@0: return assignSnapshot(lir, Bailout_ShapeGuard) && add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGetElementCache(MGetElementCache *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: michael@0: if (ins->type() == MIRType_Value) { michael@0: JS_ASSERT(ins->index()->type() == MIRType_Value); michael@0: LGetElementCacheV *lir = new(alloc()) LGetElementCacheV(useRegister(ins->object())); michael@0: if (!useBox(lir, LGetElementCacheV::Index, ins->index())) michael@0: return false; michael@0: return defineBox(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: JS_ASSERT(ins->index()->type() == MIRType_Int32); michael@0: LGetElementCacheT *lir = new(alloc()) LGetElementCacheT(useRegister(ins->object()), michael@0: useRegister(ins->index()), michael@0: tempForDispatchCache(ins->type())); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitBindNameCache(MBindNameCache *ins) michael@0: { michael@0: JS_ASSERT(ins->scopeChain()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->type() == MIRType_Object); michael@0: michael@0: LBindNameCache *lir = new(alloc()) LBindNameCache(useRegister(ins->scopeChain())); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGuardObjectIdentity(MGuardObjectIdentity *ins) michael@0: { michael@0: LGuardObjectIdentity *guard = new(alloc()) LGuardObjectIdentity(useRegister(ins->obj())); michael@0: return assignSnapshot(guard) && add(guard, ins) && redefine(ins, ins->obj()); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGuardClass(MGuardClass *ins) michael@0: { michael@0: LDefinition t = temp(); michael@0: LGuardClass *guard = new(alloc()) LGuardClass(useRegister(ins->obj()), t); michael@0: return assignSnapshot(guard) && add(guard, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGuardObject(MGuardObject *ins) michael@0: { michael@0: // The type policy does all the work, so at this point the input michael@0: // is guaranteed to be an object. michael@0: JS_ASSERT(ins->input()->type() == MIRType_Object); michael@0: return redefine(ins, ins->input()); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGuardString(MGuardString *ins) michael@0: { michael@0: // The type policy does all the work, so at this point the input michael@0: // is guaranteed to be a string. michael@0: JS_ASSERT(ins->input()->type() == MIRType_String); michael@0: return redefine(ins, ins->input()); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAssertRange(MAssertRange *ins) michael@0: { michael@0: MDefinition *input = ins->input(); michael@0: LInstruction *lir = nullptr; michael@0: michael@0: switch (input->type()) { michael@0: case MIRType_Boolean: michael@0: case MIRType_Int32: michael@0: lir = new(alloc()) LAssertRangeI(useRegisterAtStart(input)); michael@0: break; michael@0: michael@0: case MIRType_Double: michael@0: lir = new(alloc()) LAssertRangeD(useRegister(input), tempDouble()); michael@0: break; michael@0: michael@0: case MIRType_Float32: michael@0: lir = new(alloc()) LAssertRangeF(useRegister(input), tempFloat32()); michael@0: break; michael@0: michael@0: case MIRType_Value: michael@0: lir = new(alloc()) LAssertRangeV(tempToUnbox(), tempDouble(), tempDouble()); michael@0: if (!useBox(lir, LAssertRangeV::Input, input)) michael@0: return false; michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected Range for MIRType"); michael@0: break; michael@0: } michael@0: michael@0: lir->setMir(ins); michael@0: return add(lir); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallGetProperty(MCallGetProperty *ins) michael@0: { michael@0: LCallGetProperty *lir = new(alloc()) LCallGetProperty(); michael@0: if (!useBoxAtStart(lir, LCallGetProperty::Value, ins->value())) michael@0: return false; michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallGetElement(MCallGetElement *ins) michael@0: { michael@0: JS_ASSERT(ins->lhs()->type() == MIRType_Value); michael@0: JS_ASSERT(ins->rhs()->type() == MIRType_Value); michael@0: michael@0: LCallGetElement *lir = new(alloc()) LCallGetElement(); michael@0: if (!useBoxAtStart(lir, LCallGetElement::LhsInput, ins->lhs())) michael@0: return false; michael@0: if (!useBoxAtStart(lir, LCallGetElement::RhsInput, ins->rhs())) michael@0: return false; michael@0: if (!defineReturn(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallSetProperty(MCallSetProperty *ins) michael@0: { michael@0: LInstruction *lir = new(alloc()) LCallSetProperty(useRegisterAtStart(ins->object())); michael@0: if (!useBoxAtStart(lir, LCallSetProperty::Value, ins->value())) michael@0: return false; michael@0: if (!add(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitDeleteProperty(MDeleteProperty *ins) michael@0: { michael@0: LCallDeleteProperty *lir = new(alloc()) LCallDeleteProperty(); michael@0: if(!useBoxAtStart(lir, LCallDeleteProperty::Value, ins->value())) michael@0: return false; michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitDeleteElement(MDeleteElement *ins) michael@0: { michael@0: LCallDeleteElement *lir = new(alloc()) LCallDeleteElement(); michael@0: if(!useBoxAtStart(lir, LCallDeleteElement::Value, ins->value())) michael@0: return false; michael@0: if(!useBoxAtStart(lir, LCallDeleteElement::Index, ins->index())) michael@0: return false; michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSetPropertyCache(MSetPropertyCache *ins) michael@0: { michael@0: LUse obj = useRegisterAtStart(ins->object()); michael@0: LDefinition slots = tempCopy(ins->object(), 0); michael@0: LDefinition dispatchTemp = tempForDispatchCache(); michael@0: michael@0: LInstruction *lir; michael@0: if (ins->value()->type() == MIRType_Value) { michael@0: lir = new(alloc()) LSetPropertyCacheV(obj, slots, dispatchTemp); michael@0: if (!useBox(lir, LSetPropertyCacheV::Value, ins->value())) michael@0: return false; michael@0: } else { michael@0: LAllocation value = useRegisterOrConstant(ins->value()); michael@0: lir = new(alloc()) LSetPropertyCacheT(obj, slots, value, dispatchTemp, ins->value()->type()); michael@0: } michael@0: michael@0: if (!add(lir, ins)) michael@0: return false; michael@0: michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSetElementCache(MSetElementCache *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Value); michael@0: michael@0: // Due to lack of registers on x86, we reuse the object register as a michael@0: // temporary. This register may be used in a 1-byte store, which on x86 michael@0: // again has constraints; thus the use of |useByteOpRegister| over michael@0: // |useRegister| below. michael@0: LInstruction *lir; michael@0: if (ins->value()->type() == MIRType_Value) { michael@0: lir = new(alloc()) LSetElementCacheV(useByteOpRegister(ins->object()), tempToUnbox(), michael@0: temp(), tempDouble()); michael@0: michael@0: if (!useBox(lir, LSetElementCacheV::Index, ins->index())) michael@0: return false; michael@0: if (!useBox(lir, LSetElementCacheV::Value, ins->value())) michael@0: return false; michael@0: } else { michael@0: lir = new(alloc()) LSetElementCacheT(useByteOpRegister(ins->object()), michael@0: useRegisterOrConstant(ins->value()), michael@0: tempToUnbox(), temp(), tempDouble()); michael@0: michael@0: if (!useBox(lir, LSetElementCacheT::Index, ins->index())) michael@0: return false; michael@0: } michael@0: michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallSetElement(MCallSetElement *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->index()->type() == MIRType_Value); michael@0: JS_ASSERT(ins->value()->type() == MIRType_Value); michael@0: michael@0: LCallSetElement *lir = new(alloc()) LCallSetElement(); michael@0: lir->setOperand(0, useRegisterAtStart(ins->object())); michael@0: if (!useBoxAtStart(lir, LCallSetElement::Index, ins->index())) michael@0: return false; michael@0: if (!useBoxAtStart(lir, LCallSetElement::Value, ins->value())) michael@0: return false; michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallInitElementArray(MCallInitElementArray *ins) michael@0: { michael@0: LCallInitElementArray *lir = new(alloc()) LCallInitElementArray(); michael@0: lir->setOperand(0, useRegisterAtStart(ins->object())); michael@0: if (!useBoxAtStart(lir, LCallInitElementArray::Value, ins->value())) michael@0: return false; michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitIteratorStart(MIteratorStart *ins) michael@0: { michael@0: // Call a stub if this is not a simple for-in loop. michael@0: if (ins->flags() != JSITER_ENUMERATE) { michael@0: LCallIteratorStart *lir = new(alloc()) LCallIteratorStart(useRegisterAtStart(ins->object())); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: LIteratorStart *lir = new(alloc()) LIteratorStart(useRegister(ins->object()), temp(), temp(), temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitIteratorNext(MIteratorNext *ins) michael@0: { michael@0: LIteratorNext *lir = new(alloc()) LIteratorNext(useRegister(ins->iterator()), temp()); michael@0: return defineBox(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitIteratorMore(MIteratorMore *ins) michael@0: { michael@0: LIteratorMore *lir = new(alloc()) LIteratorMore(useRegister(ins->iterator()), temp()); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitIteratorEnd(MIteratorEnd *ins) michael@0: { michael@0: LIteratorEnd *lir = new(alloc()) LIteratorEnd(useRegister(ins->iterator()), temp(), temp(), temp()); michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitStringLength(MStringLength *ins) michael@0: { michael@0: JS_ASSERT(ins->string()->type() == MIRType_String); michael@0: return define(new(alloc()) LStringLength(useRegisterAtStart(ins->string())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitArgumentsLength(MArgumentsLength *ins) michael@0: { michael@0: return define(new(alloc()) LArgumentsLength(), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGetFrameArgument(MGetFrameArgument *ins) michael@0: { michael@0: LGetFrameArgument *lir = new(alloc()) LGetFrameArgument(useRegisterOrConstant(ins->index())); michael@0: return defineBox(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSetFrameArgument(MSetFrameArgument *ins) michael@0: { michael@0: MDefinition *input = ins->input(); michael@0: michael@0: if (input->type() == MIRType_Value) { michael@0: LSetFrameArgumentV *lir = new(alloc()) LSetFrameArgumentV(); michael@0: if (!useBox(lir, LSetFrameArgumentV::Input, input)) michael@0: return false; michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: if (input->type() == MIRType_Undefined || input->type() == MIRType_Null) { michael@0: Value val = input->type() == MIRType_Undefined ? UndefinedValue() : NullValue(); michael@0: LSetFrameArgumentC *lir = new(alloc()) LSetFrameArgumentC(val); michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: LSetFrameArgumentT *lir = new(alloc()) LSetFrameArgumentT(useRegister(input)); michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRunOncePrologue(MRunOncePrologue *ins) michael@0: { michael@0: LRunOncePrologue *lir = new(alloc()) LRunOncePrologue; michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRest(MRest *ins) michael@0: { michael@0: JS_ASSERT(ins->numActuals()->type() == MIRType_Int32); michael@0: michael@0: LRest *lir = new(alloc()) LRest(useFixed(ins->numActuals(), CallTempReg0), michael@0: tempFixed(CallTempReg1), michael@0: tempFixed(CallTempReg2), michael@0: tempFixed(CallTempReg3)); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRestPar(MRestPar *ins) michael@0: { michael@0: JS_ASSERT(ins->numActuals()->type() == MIRType_Int32); michael@0: michael@0: LRestPar *lir = new(alloc()) LRestPar(useFixed(ins->forkJoinContext(), CallTempReg0), michael@0: useFixed(ins->numActuals(), CallTempReg1), michael@0: tempFixed(CallTempReg2), michael@0: tempFixed(CallTempReg3), michael@0: tempFixed(CallTempReg4)); michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitThrow(MThrow *ins) michael@0: { michael@0: MDefinition *value = ins->getOperand(0); michael@0: JS_ASSERT(value->type() == MIRType_Value); michael@0: michael@0: LThrow *lir = new(alloc()) LThrow; michael@0: if (!useBoxAtStart(lir, LThrow::Value, value)) michael@0: return false; michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitIn(MIn *ins) michael@0: { michael@0: MDefinition *lhs = ins->lhs(); michael@0: MDefinition *rhs = ins->rhs(); michael@0: michael@0: JS_ASSERT(lhs->type() == MIRType_Value); michael@0: JS_ASSERT(rhs->type() == MIRType_Object); michael@0: michael@0: LIn *lir = new(alloc()) LIn(useRegisterAtStart(rhs)); michael@0: if (!useBoxAtStart(lir, LIn::LHS, lhs)) michael@0: return false; michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInstanceOf(MInstanceOf *ins) michael@0: { michael@0: MDefinition *lhs = ins->getOperand(0); michael@0: michael@0: JS_ASSERT(lhs->type() == MIRType_Value || lhs->type() == MIRType_Object); michael@0: michael@0: if (lhs->type() == MIRType_Object) { michael@0: LInstanceOfO *lir = new(alloc()) LInstanceOfO(useRegister(lhs)); michael@0: return define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: LInstanceOfV *lir = new(alloc()) LInstanceOfV(); michael@0: return useBox(lir, LInstanceOfV::LHS, lhs) && define(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitCallInstanceOf(MCallInstanceOf *ins) michael@0: { michael@0: MDefinition *lhs = ins->lhs(); michael@0: MDefinition *rhs = ins->rhs(); michael@0: michael@0: JS_ASSERT(lhs->type() == MIRType_Value); michael@0: JS_ASSERT(rhs->type() == MIRType_Object); michael@0: michael@0: LCallInstanceOf *lir = new(alloc()) LCallInstanceOf(useRegisterAtStart(rhs)); michael@0: if (!useBoxAtStart(lir, LCallInstanceOf::LHS, lhs)) michael@0: return false; michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitProfilerStackOp(MProfilerStackOp *ins) michael@0: { michael@0: LProfilerStackOp *lir = new(alloc()) LProfilerStackOp(temp()); michael@0: if (!add(lir, ins)) michael@0: return false; michael@0: // If slow assertions are enabled, then this node will result in a callVM michael@0: // out to a C++ function for the assertions, so we will need a safepoint. michael@0: return !gen->options.spsSlowAssertionsEnabled() || assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitIsCallable(MIsCallable *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->type() == MIRType_Boolean); michael@0: return define(new(alloc()) LIsCallable(useRegister(ins->object())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitHaveSameClass(MHaveSameClass *ins) michael@0: { michael@0: MDefinition *lhs = ins->lhs(); michael@0: MDefinition *rhs = ins->rhs(); michael@0: michael@0: JS_ASSERT(lhs->type() == MIRType_Object); michael@0: JS_ASSERT(rhs->type() == MIRType_Object); michael@0: michael@0: return define(new(alloc()) LHaveSameClass(useRegister(lhs), useRegister(rhs), temp()), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitHasClass(MHasClass *ins) michael@0: { michael@0: JS_ASSERT(ins->object()->type() == MIRType_Object); michael@0: JS_ASSERT(ins->type() == MIRType_Boolean); michael@0: return define(new(alloc()) LHasClass(useRegister(ins->object())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins) michael@0: { michael@0: return define(new(alloc()) LAsmJSLoadGlobalVar, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins) michael@0: { michael@0: return add(new(alloc()) LAsmJSStoreGlobalVar(useRegisterAtStart(ins->value())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAsmJSLoadFFIFunc(MAsmJSLoadFFIFunc *ins) michael@0: { michael@0: return define(new(alloc()) LAsmJSLoadFFIFunc, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAsmJSParameter(MAsmJSParameter *ins) michael@0: { michael@0: ABIArg abi = ins->abi(); michael@0: if (abi.argInRegister()) michael@0: return defineFixed(new(alloc()) LAsmJSParameter, ins, LAllocation(abi.reg())); michael@0: michael@0: JS_ASSERT(IsNumberType(ins->type())); michael@0: return defineFixed(new(alloc()) LAsmJSParameter, ins, LArgument(abi.offsetFromArgBase())); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAsmJSReturn(MAsmJSReturn *ins) michael@0: { michael@0: MDefinition *rval = ins->getOperand(0); michael@0: LAsmJSReturn *lir = new(alloc()) LAsmJSReturn; michael@0: if (IsFloatingPointType(rval->type())) michael@0: lir->setOperand(0, useFixed(rval, ReturnFloatReg)); michael@0: else if (rval->type() == MIRType_Int32) michael@0: lir->setOperand(0, useFixed(rval, ReturnReg)); michael@0: else michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected asm.js return type"); michael@0: return add(lir); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAsmJSVoidReturn(MAsmJSVoidReturn *ins) michael@0: { michael@0: return add(new(alloc()) LAsmJSVoidReturn); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAsmJSPassStackArg(MAsmJSPassStackArg *ins) michael@0: { michael@0: if (IsFloatingPointType(ins->arg()->type())) { michael@0: JS_ASSERT(!ins->arg()->isEmittedAtUses()); michael@0: return add(new(alloc()) LAsmJSPassStackArg(useRegisterAtStart(ins->arg())), ins); michael@0: } michael@0: michael@0: return add(new(alloc()) LAsmJSPassStackArg(useRegisterOrConstantAtStart(ins->arg())), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitAsmJSCall(MAsmJSCall *ins) michael@0: { michael@0: gen->setPerformsAsmJSCall(); michael@0: michael@0: LAllocation *args = gen->allocate(ins->numOperands()); michael@0: if (!args) michael@0: return false; michael@0: michael@0: for (unsigned i = 0; i < ins->numArgs(); i++) michael@0: args[i] = useFixed(ins->getOperand(i), ins->registerForArg(i)); michael@0: michael@0: if (ins->callee().which() == MAsmJSCall::Callee::Dynamic) michael@0: args[ins->dynamicCalleeOperandIndex()] = useFixed(ins->callee().dynamic(), CallTempReg0); michael@0: michael@0: LInstruction *lir = new(alloc()) LAsmJSCall(args, ins->numOperands()); michael@0: if (ins->type() == MIRType_None) { michael@0: return add(lir, ins); michael@0: } michael@0: return defineReturn(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitSetDOMProperty(MSetDOMProperty *ins) michael@0: { michael@0: MDefinition *val = ins->value(); michael@0: michael@0: Register cxReg, objReg, privReg, valueReg; michael@0: GetTempRegForIntArg(0, 0, &cxReg); michael@0: GetTempRegForIntArg(1, 0, &objReg); michael@0: GetTempRegForIntArg(2, 0, &privReg); michael@0: GetTempRegForIntArg(3, 0, &valueReg); michael@0: LSetDOMProperty *lir = new(alloc()) LSetDOMProperty(tempFixed(cxReg), michael@0: useFixed(ins->object(), objReg), michael@0: tempFixed(privReg), michael@0: tempFixed(valueReg)); michael@0: michael@0: // Keep using GetTempRegForIntArg, since we want to make sure we michael@0: // don't clobber registers we're already using. michael@0: Register tempReg1, tempReg2; michael@0: GetTempRegForIntArg(4, 0, &tempReg1); michael@0: mozilla::DebugOnly ok = GetTempRegForIntArg(5, 0, &tempReg2); michael@0: MOZ_ASSERT(ok, "How can we not have six temp registers?"); michael@0: if (!useBoxFixed(lir, LSetDOMProperty::Value, val, tempReg1, tempReg2)) michael@0: return false; michael@0: michael@0: return add(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGetDOMProperty(MGetDOMProperty *ins) michael@0: { michael@0: Register cxReg, objReg, privReg, valueReg; michael@0: GetTempRegForIntArg(0, 0, &cxReg); michael@0: GetTempRegForIntArg(1, 0, &objReg); michael@0: GetTempRegForIntArg(2, 0, &privReg); michael@0: mozilla::DebugOnly ok = GetTempRegForIntArg(3, 0, &valueReg); michael@0: MOZ_ASSERT(ok, "How can we not have four temp registers?"); michael@0: LGetDOMProperty *lir = new(alloc()) LGetDOMProperty(tempFixed(cxReg), michael@0: useFixed(ins->object(), objReg), michael@0: tempFixed(privReg), michael@0: tempFixed(valueReg)); michael@0: michael@0: return defineReturn(lir, ins) && assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitGetDOMMember(MGetDOMMember *ins) michael@0: { michael@0: MOZ_ASSERT(ins->isDomMovable(), "Members had better be movable"); michael@0: // We wish we could assert that ins->domAliasSet() == JSJitInfo::AliasNone, michael@0: // but some MGetDOMMembers are for [Pure], not [Constant] properties, whose michael@0: // value can in fact change as a result of DOM setters and method calls. michael@0: MOZ_ASSERT(ins->domAliasSet() != JSJitInfo::AliasEverything, michael@0: "Member gets had better not alias the world"); michael@0: LGetDOMMember *lir = michael@0: new(alloc()) LGetDOMMember(useRegister(ins->object())); michael@0: return defineBox(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitRecompileCheck(MRecompileCheck *ins) michael@0: { michael@0: LRecompileCheck *lir = new(alloc()) LRecompileCheck(temp()); michael@0: if (!add(lir, ins)) michael@0: return false; michael@0: return assignSafepoint(lir, ins); michael@0: } michael@0: michael@0: static void michael@0: SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint) michael@0: { michael@0: fprintf(IonSpewFile, "Current resume point %p details:\n", (void *)resumePoint); michael@0: fprintf(IonSpewFile, " frame count: %u\n", resumePoint->frameCount()); michael@0: michael@0: if (ins) { michael@0: fprintf(IonSpewFile, " taken after: "); michael@0: ins->printName(IonSpewFile); michael@0: } else { michael@0: fprintf(IonSpewFile, " taken at block %d entry", block->id()); michael@0: } michael@0: fprintf(IonSpewFile, "\n"); michael@0: michael@0: fprintf(IonSpewFile, " pc: %p (script: %p, offset: %d)\n", michael@0: (void *)resumePoint->pc(), michael@0: (void *)resumePoint->block()->info().script(), michael@0: int(resumePoint->block()->info().script()->pcToOffset(resumePoint->pc()))); michael@0: michael@0: for (size_t i = 0, e = resumePoint->numOperands(); i < e; i++) { michael@0: MDefinition *in = resumePoint->getOperand(i); michael@0: fprintf(IonSpewFile, " slot%u: ", (unsigned)i); michael@0: in->printName(IonSpewFile); michael@0: fprintf(IonSpewFile, "\n"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitInstruction(MInstruction *ins) michael@0: { michael@0: if (!gen->ensureBallast()) michael@0: return false; michael@0: if (!ins->accept(this)) michael@0: return false; michael@0: michael@0: if (ins->possiblyCalls()) michael@0: gen->setPerformsCall(); michael@0: michael@0: if (ins->resumePoint()) michael@0: updateResumeState(ins); michael@0: michael@0: if (gen->errored()) michael@0: return false; michael@0: #ifdef DEBUG michael@0: ins->setInWorklistUnchecked(); michael@0: #endif michael@0: michael@0: // If no safepoint was created, there's no need for an OSI point. michael@0: if (LOsiPoint *osiPoint = popOsiPoint()) { michael@0: if (!add(osiPoint)) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::definePhis() michael@0: { michael@0: size_t lirIndex = 0; michael@0: MBasicBlock *block = current->mir(); michael@0: for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) { michael@0: if (phi->type() == MIRType_Value) { michael@0: if (!defineUntypedPhi(*phi, lirIndex)) michael@0: return false; michael@0: lirIndex += BOX_PIECES; michael@0: } else { michael@0: if (!defineTypedPhi(*phi, lirIndex)) michael@0: return false; michael@0: lirIndex += 1; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: LIRGenerator::updateResumeState(MInstruction *ins) michael@0: { michael@0: lastResumePoint_ = ins->resumePoint(); michael@0: if (IonSpewEnabled(IonSpew_Snapshots) && lastResumePoint_) michael@0: SpewResumePoint(nullptr, ins, lastResumePoint_); michael@0: } michael@0: michael@0: void michael@0: LIRGenerator::updateResumeState(MBasicBlock *block) michael@0: { michael@0: lastResumePoint_ = block->entryResumePoint(); michael@0: if (IonSpewEnabled(IonSpew_Snapshots) && lastResumePoint_) michael@0: SpewResumePoint(block, nullptr, lastResumePoint_); michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::visitBlock(MBasicBlock *block) michael@0: { michael@0: current = block->lir(); michael@0: updateResumeState(block); michael@0: michael@0: if (!definePhis()) michael@0: return false; michael@0: michael@0: if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) { michael@0: if (!add(new(alloc()) LLabel())) michael@0: return false; michael@0: } michael@0: michael@0: for (MInstructionIterator iter = block->begin(); *iter != block->lastIns(); iter++) { michael@0: if (!visitInstruction(*iter)) michael@0: return false; michael@0: } michael@0: michael@0: if (block->successorWithPhis()) { michael@0: // If we have a successor with phis, lower the phi input now that we michael@0: // are approaching the join point. michael@0: MBasicBlock *successor = block->successorWithPhis(); michael@0: uint32_t position = block->positionInPhiSuccessor(); michael@0: size_t lirIndex = 0; michael@0: for (MPhiIterator phi(successor->phisBegin()); phi != successor->phisEnd(); phi++) { michael@0: MDefinition *opd = phi->getOperand(position); michael@0: if (!ensureDefined(opd)) michael@0: return false; michael@0: michael@0: JS_ASSERT(opd->type() == phi->type()); michael@0: michael@0: if (phi->type() == MIRType_Value) { michael@0: lowerUntypedPhiInput(*phi, position, successor->lir(), lirIndex); michael@0: lirIndex += BOX_PIECES; michael@0: } else { michael@0: lowerTypedPhiInput(*phi, position, successor->lir(), lirIndex); michael@0: lirIndex += 1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Now emit the last instruction, which is some form of branch. michael@0: if (!visitInstruction(block->lastIns())) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::precreatePhi(LBlock *block, MPhi *phi) michael@0: { michael@0: LPhi *lir = LPhi::New(gen, phi); michael@0: if (!lir) michael@0: return false; michael@0: if (!block->addPhi(lir)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGenerator::generate() michael@0: { michael@0: // Create all blocks and prep all phis beforehand. michael@0: for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) { michael@0: if (gen->shouldCancel("Lowering (preparation loop)")) michael@0: return false; michael@0: michael@0: current = LBlock::New(alloc(), *block); michael@0: if (!current) michael@0: return false; michael@0: if (!lirGraph_.addBlock(current)) michael@0: return false; michael@0: block->assignLir(current); michael@0: michael@0: // For each MIR phi, add LIR phis as appropriate. We'll fill in their michael@0: // operands on each incoming edge, and set their definitions at the michael@0: // start of their defining block. michael@0: for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) { michael@0: int numPhis = (phi->type() == MIRType_Value) ? BOX_PIECES : 1; michael@0: for (int i = 0; i < numPhis; i++) { michael@0: if (!precreatePhi(block->lir(), *phi)) michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) { michael@0: if (gen->shouldCancel("Lowering (main loop)")) michael@0: return false; michael@0: michael@0: if (!visitBlock(*block)) michael@0: return false; michael@0: } michael@0: michael@0: if (graph.osrBlock()) michael@0: lirGraph_.setOsrBlock(graph.osrBlock()->lir()); michael@0: michael@0: lirGraph_.setArgumentSlotCount(maxargslots_); michael@0: return true; michael@0: }