author | Brian Hackett <bhackett1024@gmail.com> |
Sat, 14 Feb 2015 14:55:48 -0700 | |
changeset 229168 | ac19a93de1789c743e59a36fbcacf1c54a9dc431 |
parent 229167 | 77537f14b7369b8dbbf62d1d993bdb416a8d1660 |
child 229169 | b160bea2b0304674189bf3c305babdad47bf0f4e |
push id | 55622 |
push user | bhackett@mozilla.com |
push date | Sat, 14 Feb 2015 21:56:03 +0000 |
treeherder | mozilla-inbound@ac19a93de178 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 1131403 |
milestone | 38.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/ion/bailout-with-object-or-null.js @@ -0,0 +1,17 @@ + +function foo(p) { + this.f = p; +} +function use(v, a, b) { + var f = v.f; + g = f; + g = a + b; + if (f != null) + return; +} + +with({}){} + +for (var i = 0; i < 2000; i++) + use(new foo(i % 2 ? {} : null), 1, 2); +use(new foo(null), 2147483548, 1000);
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2204,23 +2204,27 @@ CodeGenerator::visitStoreSlotT(LStoreSlo Register base = ToRegister(lir->slots()); int32_t offset = lir->mir()->slot() * sizeof(js::Value); Address dest(base, offset); if (lir->mir()->needsBarrier()) emitPreBarrier(dest); MIRType valueType = lir->mir()->value()->type(); - ConstantOrRegister value; - if (lir->value()->isConstant()) - value = ConstantOrRegister(*lir->value()->toConstant()); - else - value = TypedOrValueRegister(valueType, ToAnyRegister(lir->value())); - - masm.storeUnboxedValue(value, valueType, dest, lir->mir()->slotType()); + + if (valueType == MIRType_ObjectOrNull) { + masm.storeObjectOrNull(ToRegister(lir->value()), dest); + } else { + ConstantOrRegister value; + if (lir->value()->isConstant()) + value = ConstantOrRegister(*lir->value()->toConstant()); + else + value = TypedOrValueRegister(valueType, ToAnyRegister(lir->value())); + masm.storeUnboxedValue(value, valueType, dest, lir->mir()->slotType()); + } } void CodeGenerator::visitStoreSlotV(LStoreSlotV *lir) { Register base = ToRegister(lir->slots()); int32_t offset = lir->mir()->slot() * sizeof(Value); @@ -3726,17 +3730,17 @@ CodeGenerator::emitObjectOrStringResultC if ((mir->type() == MIRType_Object || mir->type() == MIRType_ObjectOrNull) && mir->resultTypeSet() && !mir->resultTypeSet()->unknownObject()) { // We have a result TypeSet, assert this object is in it. Label miss, ok; if (mir->type() == MIRType_ObjectOrNull) - masm.branchPtr(Assembler::NotEqual, output, ImmWord(0), &ok); + masm.branchPtr(Assembler::Equal, output, ImmWord(0), &ok); if (mir->resultTypeSet()->getObjectCount() > 0) masm.guardObjectType(output, mir->resultTypeSet(), temp, &miss); else masm.jump(&miss); masm.jump(&ok); masm.bind(&miss); @@ -5330,24 +5334,24 @@ CodeGenerator::visitCompareVM(LCompareVM break; default: MOZ_CRASH("Unexpected compare op"); } } void -CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir) +CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV *lir) { JSOp op = lir->mir()->jsop(); MCompare::CompareType compareType = lir->mir()->compareType(); MOZ_ASSERT(compareType == MCompare::Compare_Undefined || compareType == MCompare::Compare_Null); - const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefined::Value); + const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedV::Value); Register output = ToRegister(lir->output()); if (op == JSOP_EQ || op == JSOP_NE) { MOZ_ASSERT(lir->mir()->lhs()->type() != MIRType_Object || lir->mir()->operandMightEmulateUndefined(), "Operands which can't emulate undefined should have been folded"); OutOfLineTestObjectWithLabels *ool = nullptr; @@ -5404,24 +5408,24 @@ CodeGenerator::visitIsNullOrLikeUndefine Assembler::Condition cond = JSOpToCondition(compareType, op); if (compareType == MCompare::Compare_Null) masm.testNullSet(cond, value, output); else masm.testUndefinedSet(cond, value, output); } void -CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir) +CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV(LIsNullOrLikeUndefinedAndBranchV *lir) { JSOp op = lir->cmpMir()->jsop(); MCompare::CompareType compareType = lir->cmpMir()->compareType(); MOZ_ASSERT(compareType == MCompare::Compare_Undefined || compareType == MCompare::Compare_Null); - const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranch::Value); + const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranchV::Value); if (op == JSOP_EQ || op == JSOP_NE) { MBasicBlock *ifTrue; MBasicBlock *ifFalse; if (op == JSOP_EQ) { ifTrue = lir->ifTrue(); ifFalse = lir->ifFalse(); @@ -5471,86 +5475,120 @@ CodeGenerator::visitIsNullOrLikeUndefine Assembler::Condition cond = JSOpToCondition(compareType, op); if (compareType == MCompare::Compare_Null) testNullEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse()); else testUndefinedEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse()); } void -CodeGenerator::visitEmulatesUndefined(LEmulatesUndefined *lir) +CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT * lir) { MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null); - MOZ_ASSERT(lir->mir()->lhs()->type() == MIRType_Object); - MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(), - "If the object couldn't emulate undefined, this should have been folded."); + + MIRType lhsType = lir->mir()->lhs()->type(); + MOZ_ASSERT(lhsType == MIRType_Object || lhsType == MIRType_ObjectOrNull); JSOp op = lir->mir()->jsop(); - MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded"); - - OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels(); - addOutOfLineCode(ool, lir->mir()); - - Label *emulatesUndefined = ool->label1(); - Label *doesntEmulateUndefined = ool->label2(); + MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || op == JSOP_EQ || op == JSOP_NE, + "Strict equality should have been folded"); + + MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || lir->mir()->operandMightEmulateUndefined(), + "If the object couldn't emulate undefined, this should have been folded."); Register objreg = ToRegister(lir->input()); Register output = ToRegister(lir->output()); - branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, doesntEmulateUndefined, - output, ool); - - Label done; - - masm.move32(Imm32(op == JSOP_NE), output); - masm.jump(&done); - - masm.bind(emulatesUndefined); - masm.move32(Imm32(op == JSOP_EQ), output); - masm.bind(&done); -} - -void -CodeGenerator::visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir) -{ - MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || - lir->cmpMir()->compareType() == MCompare::Compare_Null); - MOZ_ASSERT(lir->cmpMir()->operandMightEmulateUndefined(), - "Operands which can't emulate undefined should have been folded"); + + if ((op == JSOP_EQ || op == JSOP_NE) && lir->mir()->operandMightEmulateUndefined()) { + OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels(); + addOutOfLineCode(ool, lir->mir()); + + Label *emulatesUndefined = ool->label1(); + Label *doesntEmulateUndefined = ool->label2(); + + if (lhsType == MIRType_ObjectOrNull) + masm.branchTestPtr(Assembler::Zero, objreg, objreg, emulatesUndefined); + + branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, doesntEmulateUndefined, + output, ool); + + Label done; + + masm.move32(Imm32(op == JSOP_NE), output); + masm.jump(&done); + + masm.bind(emulatesUndefined); + masm.move32(Imm32(op == JSOP_EQ), output); + masm.bind(&done); + } else { + MOZ_ASSERT(lhsType == MIRType_ObjectOrNull); + + Label isNull, done; + + masm.branchTestPtr(Assembler::Zero, objreg, objreg, &isNull); + + masm.move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), output); + masm.jump(&done); + + masm.bind(&isNull); + masm.move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), output); + + masm.bind(&done); + } +} + +void +CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT(LIsNullOrLikeUndefinedAndBranchT *lir) +{ + DebugOnly<MCompare::CompareType> compareType = lir->cmpMir()->compareType(); + MOZ_ASSERT(compareType == MCompare::Compare_Undefined || + compareType == MCompare::Compare_Null); + + MIRType lhsType = lir->cmpMir()->lhs()->type(); + MOZ_ASSERT(lhsType == MIRType_Object || lhsType == MIRType_ObjectOrNull); JSOp op = lir->cmpMir()->jsop(); - MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded"); - - OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject(); - addOutOfLineCode(ool, lir->cmpMir()); - - Label *equal; - Label *unequal; - - { - MBasicBlock *ifTrue; - MBasicBlock *ifFalse; - - if (op == JSOP_EQ) { - ifTrue = lir->ifTrue(); - ifFalse = lir->ifFalse(); - } else { - // Swap branches. - ifTrue = lir->ifFalse(); - ifFalse = lir->ifTrue(); - op = JSOP_EQ; - } - - equal = getJumpLabelForBranch(ifTrue); - unequal = getJumpLabelForBranch(ifFalse); - } - - Register objreg = ToRegister(lir->input()); - - testObjectEmulatesUndefined(objreg, equal, unequal, ToRegister(lir->temp()), ool); + MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || op == JSOP_EQ || op == JSOP_NE, + "Strict equality should have been folded"); + + MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || lir->cmpMir()->operandMightEmulateUndefined(), + "If the object couldn't emulate undefined, this should have been folded."); + + MBasicBlock *ifTrue; + MBasicBlock *ifFalse; + + if (op == JSOP_EQ || op == JSOP_STRICTEQ) { + ifTrue = lir->ifTrue(); + ifFalse = lir->ifFalse(); + } else { + // Swap branches. + ifTrue = lir->ifFalse(); + ifFalse = lir->ifTrue(); + } + + Register input = ToRegister(lir->getOperand(0)); + + if ((op == JSOP_EQ || op == JSOP_NE) && lir->cmpMir()->operandMightEmulateUndefined()) { + OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject(); + addOutOfLineCode(ool, lir->cmpMir()); + + Label *ifTrueLabel = getJumpLabelForBranch(ifTrue); + Label *ifFalseLabel = getJumpLabelForBranch(ifFalse); + + if (lhsType == MIRType_ObjectOrNull) + masm.branchTestPtr(Assembler::Zero, input, input, ifTrueLabel); + + // Objects that emulate undefined are loosely equal to null/undefined. + Register scratch = ToRegister(lir->temp()); + testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool); + } else { + MOZ_ASSERT(lhsType == MIRType_ObjectOrNull); + testZeroEmitBranch(Assembler::Equal, input, ifTrue, ifFalse); + } } typedef JSString *(*ConcatStringsFn)(ExclusiveContext *, HandleString, HandleString); static const VMFunction ConcatStringsInfo = FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>); void CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output) { @@ -7700,25 +7738,29 @@ void CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT *ins) { const Register obj = ToRegister(ins->getOperand(0)); size_t slot = ins->mir()->slot(); const LAllocation *value = ins->value(); MIRType valueType = ins->mir()->value()->type(); - ConstantOrRegister nvalue = value->isConstant() - ? ConstantOrRegister(*value->toConstant()) - : TypedOrValueRegister(valueType, ToAnyRegister(value)); - Address address(obj, NativeObject::getFixedSlotOffset(slot)); if (ins->mir()->needsBarrier()) emitPreBarrier(address); - masm.storeConstantOrRegister(nvalue, address); + if (valueType == MIRType_ObjectOrNull) { + Register nvalue = ToRegister(value); + masm.storeObjectOrNull(nvalue, address); + } else { + ConstantOrRegister nvalue = value->isConstant() + ? ConstantOrRegister(*value->toConstant()) + : TypedOrValueRegister(valueType, ToAnyRegister(value)); + masm.storeConstantOrRegister(nvalue, address); + } } void CodeGenerator::visitGetNameCache(LGetNameCache *ins) { RegisterSet liveRegs = ins->safepoint()->liveRegs(); Register scopeChain = ToRegister(ins->scopeObj()); TypedOrValueRegister output(GetValueOutput(ins)); @@ -8466,18 +8508,18 @@ CodeGenerator::visitLoadUnboxedPointerT( { Register elements = ToRegister(lir->elements()); const LAllocation *index = lir->index(); Register out = ToRegister(lir->output()); bool bailOnNull; int32_t offsetAdjustment; if (lir->mir()->isLoadUnboxedObjectOrNull()) { - bailOnNull = lir->mir()->toLoadUnboxedObjectOrNull()->nullBehavior() != - MLoadUnboxedObjectOrNull::NullNotPossible; + bailOnNull = lir->mir()->toLoadUnboxedObjectOrNull()->nullBehavior() == + MLoadUnboxedObjectOrNull::BailOnNull; offsetAdjustment = lir->mir()->toLoadUnboxedObjectOrNull()->offsetAdjustment(); } else if (lir->mir()->isLoadUnboxedString()) { bailOnNull = false; offsetAdjustment = lir->mir()->toLoadUnboxedString()->offsetAdjustment(); } else { MOZ_CRASH(); } @@ -8492,16 +8534,28 @@ CodeGenerator::visitLoadUnboxedPointerT( if (bailOnNull) { Label bail; masm.branchTestPtr(Assembler::Zero, out, out, &bail); bailoutFrom(&bail, lir->snapshot()); } } void +CodeGenerator::visitUnboxObjectOrNull(LUnboxObjectOrNull *lir) +{ + Register obj = ToRegister(lir->input()); + + if (lir->mir()->fallible()) { + Label bail; + masm.branchTestPtr(Assembler::Zero, obj, obj, &bail); + bailoutFrom(&bail, lir->snapshot()); + } +} + +void CodeGenerator::visitLoadTypedArrayElement(LLoadTypedArrayElement *lir) { Register elements = ToRegister(lir->elements()); Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp()); AnyRegister out = ToAnyRegister(lir->output()); Scalar::Type arrayType = lir->mir()->arrayType(); int width = Scalar::byteSize(arrayType);
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -216,20 +216,20 @@ class CodeGenerator : public CodeGenerat void visitMathFunctionF(LMathFunctionF *ins); void visitModD(LModD *ins); void visitMinMaxI(LMinMaxI *lir); void visitBinaryV(LBinaryV *lir); void emitCompareS(LInstruction *lir, JSOp op, Register left, Register right, Register output); void visitCompareS(LCompareS *lir); void visitCompareStrictS(LCompareStrictS *lir); void visitCompareVM(LCompareVM *lir); - void visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir); - void visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir); - void visitEmulatesUndefined(LEmulatesUndefined *lir); - void visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir); + void visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV *lir); + void visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT *lir); + void visitIsNullOrLikeUndefinedAndBranchV(LIsNullOrLikeUndefinedAndBranchV *lir); + void visitIsNullOrLikeUndefinedAndBranchT(LIsNullOrLikeUndefinedAndBranchT *lir); void emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output); void visitConcat(LConcat *lir); void visitCharCodeAt(LCharCodeAt *lir); void visitFromCharCode(LFromCharCode *lir); void visitStringSplit(LStringSplit *lir); void visitFunctionEnvironment(LFunctionEnvironment *lir); void visitCallGetProperty(LCallGetProperty *lir); void visitCallGetElement(LCallGetElement *lir); @@ -240,16 +240,17 @@ class CodeGenerator : public CodeGenerat void visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool); void visitToIdV(LToIdV *lir); template<typename T> void emitLoadElementT(LLoadElementT *lir, const T &source); void visitLoadElementT(LLoadElementT *lir); void visitLoadElementV(LLoadElementV *load); void visitLoadElementHole(LLoadElementHole *lir); void visitLoadUnboxedPointerV(LLoadUnboxedPointerV *lir); void visitLoadUnboxedPointerT(LLoadUnboxedPointerT *lir); + void visitUnboxObjectOrNull(LUnboxObjectOrNull *lir); void visitStoreElementT(LStoreElementT *lir); void visitStoreElementV(LStoreElementV *lir); void visitStoreElementHoleT(LStoreElementHoleT *lir); void visitStoreElementHoleV(LStoreElementHoleV *lir); void visitStoreUnboxedPointer(LStoreUnboxedPointer *lir); void emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj, Register elementsTemp, Register lengthTemp, TypedOrValueRegister out); void visitArrayPopShiftV(LArrayPopShiftV *lir);
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2513,16 +2513,61 @@ TryEliminateTypeBarrier(MTypeBarrier *ba if (previous == block) break; block = previous; } return true; } +static bool +TryOptimizeLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *def, MDefinitionVector *peliminateList) +{ + if (def->type() != MIRType_Value) + return true; + + MDefinitionVector eliminateList(def->block()->graph().alloc()); + + for (MUseDefIterator iter(def); iter; ++iter) { + MDefinition *ndef = iter.def(); + switch (ndef->op()) { + case MDefinition::Op_Compare: + if (ndef->toCompare()->compareType() != MCompare::Compare_Null) + return true; + break; + case MDefinition::Op_PostWriteBarrier: + break; + case MDefinition::Op_StoreFixedSlot: + break; + case MDefinition::Op_StoreSlot: + break; + case MDefinition::Op_ToObjectOrNull: + if (!eliminateList.append(ndef->toToObjectOrNull())) + return false; + break; + case MDefinition::Op_Unbox: + MOZ_ASSERT(ndef->type() == MIRType_Object); + break; + default: + return true; + } + } + + def->setResultType(MIRType_ObjectOrNull); + + for (size_t i = 0; i < eliminateList.length(); i++) { + MDefinition *ndef = eliminateList[i]; + ndef->replaceAllUsesWith(def); + if (!peliminateList->append(ndef)) + return false; + } + + return true; +} + static inline MDefinition * PassthroughOperand(MDefinition *def) { if (def->isConvertElementsToDoubles()) return def->toConvertElementsToDoubles()->elements(); if (def->isMaybeCopyElementsForWrite()) return def->toMaybeCopyElementsForWrite()->object(); return nullptr; @@ -2560,16 +2605,18 @@ jit::EliminateRedundantChecks(MIRGraph & for (MBasicBlockIterator i(graph.begin()); i != graph.end(); i++) { MBasicBlock *block = *i; if (block->immediateDominator() == block) { if (!worklist.append(block)) return false; } } + MDefinitionVector eliminateList(graph.alloc()); + // Starting from each self-dominating block, traverse the CFG in pre-order. while (!worklist.empty()) { MBasicBlock *block = worklist.popCopy(); // Add all immediate dominators to the front of the worklist. if (!worklist.append(block->immediatelyDominatedBlocksBegin(), block->immediatelyDominatedBlocksEnd())) { return false; @@ -2581,31 +2628,40 @@ jit::EliminateRedundantChecks(MIRGraph & bool eliminated = false; if (def->isBoundsCheck()) { if (!TryEliminateBoundsCheck(checks, index, def->toBoundsCheck(), &eliminated)) return false; } else if (def->isTypeBarrier()) { if (!TryEliminateTypeBarrier(def->toTypeBarrier(), &eliminated)) return false; + } else if (def->isLoadUnboxedObjectOrNull()) { + if (!TryOptimizeLoadUnboxedObjectOrNull(def->toLoadUnboxedObjectOrNull(), &eliminateList)) + return false; } else { // Now that code motion passes have finished, replace // instructions which pass through one of their operands // (and perform additional checks) with that operand. if (MDefinition *passthrough = PassthroughOperand(def)) def->replaceAllUsesWith(passthrough); } if (eliminated) block->discardDef(def); } index++; } MOZ_ASSERT(index == graph.numBlocks()); + + for (size_t i = 0; i < eliminateList.length(); i++) { + MDefinition *def = eliminateList[i]; + def->block()->discardDef(def); + } + return true; } bool LinearSum::multiply(int32_t scale) { for (size_t i = 0; i < terms_.length(); i++) { if (!SafeMul(scale, terms_[i].scale, &terms_[i].scale))
--- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -1723,17 +1723,19 @@ uintptr_t SnapshotIterator::fromStack(int32_t offset) const { return ReadFrameSlot(fp_, offset); } static Value FromObjectPayload(uintptr_t payload) { - return ObjectValue(*reinterpret_cast<JSObject *>(payload)); + // Note: Both MIRType_Object and MIRType_ObjectOrNull are encoded in + // snapshots using JSVAL_TYPE_OBJECT. + return ObjectOrNullValue(reinterpret_cast<JSObject *>(payload)); } static Value FromStringPayload(uintptr_t payload) { return StringValue(reinterpret_cast<JSString *>(payload)); }
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -2493,22 +2493,25 @@ class LBitAndAndBranch : public LControl const LAllocation *left() { return getOperand(0); } const LAllocation *right() { return getOperand(1); } }; -class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2> -{ - public: - LIR_HEADER(IsNullOrLikeUndefined) - - LIsNullOrLikeUndefined(const LDefinition &temp, const LDefinition &tempToUnbox) +// Takes a value and tests whether it is null, undefined, or is an object that +// emulates |undefined|, as determined by the JSCLASS_EMULATES_UNDEFINED class +// flag on unwrapped objects. See also js::EmulatesUndefined. +class LIsNullOrLikeUndefinedV : public LInstructionHelper<1, BOX_PIECES, 2> +{ + public: + LIR_HEADER(IsNullOrLikeUndefinedV) + + LIsNullOrLikeUndefinedV(const LDefinition &temp, const LDefinition &tempToUnbox) { setTemp(0, temp); setTemp(1, tempToUnbox); } static const size_t Value = 0; MCompare *mir() { @@ -2519,25 +2522,42 @@ class LIsNullOrLikeUndefined : public LI return getTemp(0); } const LDefinition *tempToUnbox() { return getTemp(1); } }; -class LIsNullOrLikeUndefinedAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 2> +// Takes an object or object-or-null pointer and tests whether it is null or is +// an object that emulates |undefined|, as above. +class LIsNullOrLikeUndefinedT : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(IsNullOrLikeUndefinedT) + + explicit LIsNullOrLikeUndefinedT(const LAllocation &input) + { + setOperand(0, input); + } + + MCompare *mir() { + return mir_->toCompare(); + } +}; + +class LIsNullOrLikeUndefinedAndBranchV : public LControlInstructionHelper<2, BOX_PIECES, 2> { MCompare *cmpMir_; public: - LIR_HEADER(IsNullOrLikeUndefinedAndBranch) - - LIsNullOrLikeUndefinedAndBranch(MCompare *cmpMir, MBasicBlock *ifTrue, MBasicBlock *ifFalse, - const LDefinition &temp, const LDefinition &tempToUnbox) + LIR_HEADER(IsNullOrLikeUndefinedAndBranchV) + + LIsNullOrLikeUndefinedAndBranchV(MCompare *cmpMir, MBasicBlock *ifTrue, MBasicBlock *ifFalse, + const LDefinition &temp, const LDefinition &tempToUnbox) : cmpMir_(cmpMir) { setSuccessor(0, ifTrue); setSuccessor(1, ifFalse); setTemp(0, temp); setTemp(1, tempToUnbox); } @@ -2558,44 +2578,26 @@ class LIsNullOrLikeUndefinedAndBranch : const LDefinition *temp() { return getTemp(0); } const LDefinition *tempToUnbox() { return getTemp(1); } }; -// Takes an object and tests whether it emulates |undefined|, as determined by -// the JSCLASS_EMULATES_UNDEFINED class flag on unwrapped objects. See also -// js::EmulatesUndefined. -class LEmulatesUndefined : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(EmulatesUndefined) - - explicit LEmulatesUndefined(const LAllocation &input) - { - setOperand(0, input); - } - - MCompare *mir() { - return mir_->toCompare(); - } -}; - -class LEmulatesUndefinedAndBranch : public LControlInstructionHelper<2, 1, 1> +class LIsNullOrLikeUndefinedAndBranchT : public LControlInstructionHelper<2, 1, 1> { MCompare *cmpMir_; public: - LIR_HEADER(EmulatesUndefinedAndBranch) - - LEmulatesUndefinedAndBranch(MCompare *cmpMir, const LAllocation &input, - MBasicBlock *ifTrue, MBasicBlock *ifFalse, - const LDefinition &temp) + LIR_HEADER(IsNullOrLikeUndefinedAndBranchT) + + LIsNullOrLikeUndefinedAndBranchT(MCompare *cmpMir, const LAllocation &input, + MBasicBlock *ifTrue, MBasicBlock *ifFalse, + const LDefinition &temp) : cmpMir_(cmpMir) { setOperand(0, input); setSuccessor(0, ifTrue); setSuccessor(1, ifFalse); setTemp(0, temp); } @@ -4479,16 +4481,34 @@ class LLoadUnboxedPointerT : public LIns const LAllocation *elements() { return getOperand(0); } const LAllocation *index() { return getOperand(1); } }; +class LUnboxObjectOrNull : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(UnboxObjectOrNull); + + explicit LUnboxObjectOrNull(const LAllocation &input) + { + setOperand(0, input); + } + + MUnbox *mir() const { + return mir_->toUnbox(); + } + const LAllocation *input() { + return getOperand(0); + } +}; + // Store a boxed value to a dense array's element vector. class LStoreElementV : public LInstructionHelper<0, 2 + BOX_PIECES, 0> { public: LIR_HEADER(StoreElementV) LStoreElementV(const LAllocation &elements, const LAllocation &index) { setOperand(0, elements);
--- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -113,20 +113,20 @@ _(CompareS) \ _(CompareStrictS) \ _(CompareB) \ _(CompareBAndBranch) \ _(CompareV) \ _(CompareVAndBranch) \ _(CompareVM) \ _(BitAndAndBranch) \ - _(IsNullOrLikeUndefined) \ - _(IsNullOrLikeUndefinedAndBranch)\ - _(EmulatesUndefined) \ - _(EmulatesUndefinedAndBranch) \ + _(IsNullOrLikeUndefinedV) \ + _(IsNullOrLikeUndefinedT) \ + _(IsNullOrLikeUndefinedAndBranchV)\ + _(IsNullOrLikeUndefinedAndBranchT)\ _(MinMaxI) \ _(MinMaxD) \ _(MinMaxF) \ _(NegI) \ _(NegD) \ _(NegF) \ _(AbsI) \ _(AbsD) \ @@ -214,16 +214,17 @@ _(BoundsCheck) \ _(BoundsCheckRange) \ _(BoundsCheckLower) \ _(LoadElementV) \ _(LoadElementT) \ _(LoadElementHole) \ _(LoadUnboxedPointerV) \ _(LoadUnboxedPointerT) \ + _(UnboxObjectOrNull) \ _(StoreElementV) \ _(StoreElementT) \ _(StoreUnboxedPointer) \ _(ArrayPopShiftV) \ _(ArrayPopShiftT) \ _(ArrayPushV) \ _(ArrayPushT) \ _(ArrayConcat) \
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -698,40 +698,43 @@ LIRGenerator::visitTest(MTest *test) // Emit LCompare*AndBranch. // Compare and branch null/undefined. // The second operand has known null/undefined type, // so just test the first operand. if (comp->compareType() == MCompare::Compare_Null || comp->compareType() == MCompare::Compare_Undefined) { - if (left->type() == MIRType_Object) { - MOZ_ASSERT(comp->operandMightEmulateUndefined(), + if (left->type() == MIRType_Object || left->type() == MIRType_ObjectOrNull) { + MOZ_ASSERT(left->type() == MIRType_ObjectOrNull || + comp->operandMightEmulateUndefined(), "MCompare::tryFold should handle the never-emulates-undefined case"); - LEmulatesUndefinedAndBranch *lir = - new(alloc()) LEmulatesUndefinedAndBranch(comp, useRegister(left), - ifTrue, ifFalse, temp()); + LDefinition tmp = + comp->operandMightEmulateUndefined() ? temp() : LDefinition::BogusTemp(); + LIsNullOrLikeUndefinedAndBranchT *lir = + new(alloc()) LIsNullOrLikeUndefinedAndBranchT(comp, useRegister(left), + ifTrue, ifFalse, tmp); add(lir, test); return; } LDefinition tmp, tmpToUnbox; if (comp->operandMightEmulateUndefined()) { tmp = temp(); tmpToUnbox = tempToUnbox(); } else { tmp = LDefinition::BogusTemp(); tmpToUnbox = LDefinition::BogusTemp(); } - LIsNullOrLikeUndefinedAndBranch *lir = - new(alloc()) LIsNullOrLikeUndefinedAndBranch(comp, ifTrue, ifFalse, - tmp, tmpToUnbox); - useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left); + LIsNullOrLikeUndefinedAndBranchV *lir = + new(alloc()) LIsNullOrLikeUndefinedAndBranchV(comp, ifTrue, ifFalse, + tmp, tmpToUnbox); + useBox(lir, LIsNullOrLikeUndefinedAndBranchV::Value, left); add(lir, test); return; } // Compare and branch booleans. if (comp->compareType() == MCompare::Compare_Boolean) { MOZ_ASSERT(left->type() == MIRType_Value); MOZ_ASSERT(right->type() == MIRType_Boolean); @@ -934,35 +937,36 @@ LIRGenerator::visitCompare(MCompare *com emitAtUses(comp); return; } // Compare Null and Undefined. if (comp->compareType() == MCompare::Compare_Null || comp->compareType() == MCompare::Compare_Undefined) { - if (left->type() == MIRType_Object) { - MOZ_ASSERT(comp->operandMightEmulateUndefined(), + if (left->type() == MIRType_Object || left->type() == MIRType_ObjectOrNull) { + MOZ_ASSERT(left->type() == MIRType_ObjectOrNull || + comp->operandMightEmulateUndefined(), "MCompare::tryFold should have folded this away"); - define(new(alloc()) LEmulatesUndefined(useRegister(left)), comp); + define(new(alloc()) LIsNullOrLikeUndefinedT(useRegister(left)), comp); return; } LDefinition tmp, tmpToUnbox; if (comp->operandMightEmulateUndefined()) { tmp = temp(); tmpToUnbox = tempToUnbox(); } else { tmp = LDefinition::BogusTemp(); tmpToUnbox = LDefinition::BogusTemp(); } - LIsNullOrLikeUndefined *lir = new(alloc()) LIsNullOrLikeUndefined(tmp, tmpToUnbox); - useBox(lir, LIsNullOrLikeUndefined::Value, left); + LIsNullOrLikeUndefinedV *lir = new(alloc()) LIsNullOrLikeUndefinedV(tmp, tmpToUnbox); + useBox(lir, LIsNullOrLikeUndefinedV::Value, left); define(lir, comp); return; } // Compare booleans. if (comp->compareType() == MCompare::Compare_Boolean) { MOZ_ASSERT(left->type() == MIRType_Value); MOZ_ASSERT(right->type() == MIRType_Boolean); @@ -2622,17 +2626,17 @@ LIRGenerator::visitLoadElementHole(MLoad } void LIRGenerator::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *ins) { MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); MOZ_ASSERT(ins->index()->type() == MIRType_Int32); - if (ins->type() == MIRType_Object) { + if (ins->type() == MIRType_Object || ins->type() == MIRType_ObjectOrNull) { LLoadUnboxedPointerT *lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()), useRegisterOrConstant(ins->index())); if (ins->nullBehavior() == MLoadUnboxedObjectOrNull::BailOnNull) assignSnapshot(lir, Bailout_TypeBarrierO); define(lir, ins); } else { MOZ_ASSERT(ins->type() == MIRType_Value); MOZ_ASSERT(ins->nullBehavior() != MLoadUnboxedObjectOrNull::BailOnNull);
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -611,24 +611,28 @@ class MDefinition : public MNode TemporaryTypeSet *resultTypeSet() const { return resultTypeSet_; } bool emptyResultTypeSet() const; bool mightBeType(MIRType type) const { MOZ_ASSERT(type != MIRType_Value); + MOZ_ASSERT(type != MIRType_ObjectOrNull); if (type == this->type()) return true; - if (MIRType_Value != this->type()) - return false; - - return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type); + if (this->type() == MIRType_ObjectOrNull) + return type == MIRType_Object || type == MIRType_Null; + + if (this->type() == MIRType_Value) + return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type); + + return false; } bool mightBeMagicType() const; // Float32 specialization operations (see big comment in IonAnalysis before the Float32 // specialization algorithm). virtual bool isFloat32Commutative() const { return false; } virtual bool canProduceFloat32() const { return false; }
--- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -420,16 +420,27 @@ class MacroAssembler : public MacroAssem } storeDouble(reg, dest); } else { storeValue(ValueTypeFromMIRType(src.type()), src.typedReg().gpr(), dest); } } template <typename T> + void storeObjectOrNull(Register src, const T &dest) { + Label notNull, done; + branchTestPtr(Assembler::NonZero, src, src, ¬Null); + storeValue(NullValue(), dest); + jump(&done); + bind(¬Null); + storeValue(JSVAL_TYPE_OBJECT, src, dest); + bind(&done); + } + + template <typename T> void storeConstantOrRegister(ConstantOrRegister src, const T &dest) { if (src.constant()) storeValue(src.value(), dest); else storeTypedOrValue(src.reg(), dest); } void storeCallResult(Register reg) {
--- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -97,16 +97,23 @@ class CodeGeneratorARM : public CodeGene emitBranch(cond, ifTrue, ifFalse); } void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand &value, MBasicBlock *ifTrue, MBasicBlock *ifFalse) { cond = masm.testObject(cond, value); emitBranch(cond, ifTrue, ifFalse); } + void testZeroEmitBranch(Assembler::Condition cond, Register reg, + MBasicBlock *ifTrue, MBasicBlock *ifFalse) + { + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + masm.cmpPtr(reg, ImmWord(0)); + emitBranch(cond, ifTrue, ifFalse); + } void emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base); public: // Instruction visitors. virtual void visitMinMaxD(LMinMaxD *ins); virtual void visitMinMaxF(LMinMaxF *ins); virtual void visitAbsD(LAbsD *ins);
--- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -120,20 +120,29 @@ LIRGeneratorARM::visitBox(MBox *box) lir->setDef(1, LDefinition::BogusTemp()); box->setVirtualRegister(vreg); add(lir); } void LIRGeneratorARM::visitUnbox(MUnbox *unbox) { + MDefinition *inner = unbox->getOperand(0); + + if (inner->type() == MIRType_ObjectOrNull) { + LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(inner)); + if (unbox->fallible()) + assignSnapshot(lir, unbox->bailoutKind()); + defineReuseInput(lir, unbox, 0); + return; + } + // An unbox on arm reads in a type tag (either in memory or a register) and - // a payload. Unlike most instructions conusming a box, we ask for the type + // a payload. Unlike most instructions consuming a box, we ask for the type // second, so that the result can re-use the first input. - MDefinition *inner = unbox->getOperand(0); MOZ_ASSERT(inner->type() == MIRType_Value); ensureDefined(inner); if (IsFloatingPointType(unbox->type())) { LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type()); if (unbox->fallible()) assignSnapshot(lir, unbox->bailoutKind());
--- a/js/src/jit/mips/CodeGenerator-mips.h +++ b/js/src/jit/mips/CodeGenerator-mips.h @@ -155,16 +155,21 @@ class CodeGeneratorMIPS : public CodeGen { emitBranch(value.typeReg(), (Imm32)ImmType(JSVAL_TYPE_UNDEFINED), cond, ifTrue, ifFalse); } void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand &value, MBasicBlock *ifTrue, MBasicBlock *ifFalse) { emitBranch(value.typeReg(), (Imm32)ImmType(JSVAL_TYPE_OBJECT), cond, ifTrue, ifFalse); } + void testZeroEmitBranch(Assembler::Condition cond, Register reg, + MBasicBlock *ifTrue, MBasicBlock *ifFalse) + { + emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse); + } void emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base); public: // Instruction visitors. virtual void visitMinMaxD(LMinMaxD *ins); virtual void visitMinMaxF(LMinMaxF *ins); virtual void visitAbsD(LAbsD *ins);
--- a/js/src/jit/mips/Lowering-mips.cpp +++ b/js/src/jit/mips/Lowering-mips.cpp @@ -122,20 +122,29 @@ LIRGeneratorMIPS::visitBox(MBox *box) lir->setDef(1, LDefinition::BogusTemp()); box->setVirtualRegister(vreg); add(lir); } void LIRGeneratorMIPS::visitUnbox(MUnbox *unbox) { + MDefinition *inner = unbox->getOperand(0); + + if (inner->type() == MIRType_ObjectOrNull) { + LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(inner)); + if (unbox->fallible()) + assignSnapshot(lir, unbox->bailoutKind()); + defineReuseInput(lir, unbox, 0); + return; + } + // An unbox on mips reads in a type tag (either in memory or a register) and // a payload. Unlike most instructions consuming a box, we ask for the type // second, so that the result can re-use the first input. - MDefinition *inner = unbox->getOperand(0); MOZ_ASSERT(inner->type() == MIRType_Value); ensureDefined(inner); if (IsFloatingPointType(unbox->type())) { LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type()); if (unbox->fallible()) assignSnapshot(lir, unbox->bailoutKind());
--- a/js/src/jit/none/CodeGenerator-none.h +++ b/js/src/jit/none/CodeGenerator-none.h @@ -43,16 +43,19 @@ class CodeGeneratorNone : public CodeGen MOZ_CRASH(); } void testUndefinedEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock *, MBasicBlock *) { MOZ_CRASH(); } void testObjectEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock *, MBasicBlock *) { MOZ_CRASH(); } + void testZeroEmitBranch(Assembler::Condition, Register, MBasicBlock *, MBasicBlock *) { + MOZ_CRASH(); + } void emitTableSwitchDispatch(MTableSwitch *, Register, Register) { MOZ_CRASH(); } ValueOperand ToValue(LInstruction *, size_t) { MOZ_CRASH(); } ValueOperand ToOutValue(LInstruction *) { MOZ_CRASH(); } ValueOperand ToTempValue(LInstruction *, size_t) { MOZ_CRASH(); } void generateInvalidateEpilogue() { MOZ_CRASH(); } }; typedef CodeGeneratorNone CodeGeneratorSpecific;
--- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -373,22 +373,24 @@ CodeGeneratorShared::encodeAllocation(LS break; case MIRType_Null: alloc = RValueAllocation::Null(); break; case MIRType_Int32: case MIRType_String: case MIRType_Symbol: case MIRType_Object: + case MIRType_ObjectOrNull: case MIRType_Boolean: case MIRType_Double: case MIRType_Float32: { LAllocation *payload = snapshot->payloadOfSlot(*allocIndex); - JSValueType valueType = ValueTypeFromMIRType(type); + JSValueType valueType = + (type == MIRType_ObjectOrNull) ? JSVAL_TYPE_OBJECT : ValueTypeFromMIRType(type); if (payload->isMemory()) { if (type == MIRType_Float32) alloc = RValueAllocation::Float32(ToStackIndex(payload)); else alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload)); } else if (payload->isGeneralReg()) { alloc = RValueAllocation::Typed(valueType, ToRegister(payload)); } else if (payload->isFloatReg()) {
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/shared/CodeGenerator-x86-shared.h @@ -142,16 +142,24 @@ class CodeGeneratorX86Shared : public Co } void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand &value, MBasicBlock *ifTrue, MBasicBlock *ifFalse) { cond = masm.testObject(cond, value); emitBranch(cond, ifTrue, ifFalse); } + void testZeroEmitBranch(Assembler::Condition cond, Register reg, + MBasicBlock *ifTrue, MBasicBlock *ifFalse) + { + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + masm.cmpPtr(reg, ImmWord(0)); + emitBranch(cond, ifTrue, ifFalse); + } + void emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base); public: CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); public: // Instruction visitors. virtual void visitDouble(LDouble *ins);
--- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -75,16 +75,25 @@ LIRGeneratorX64::visitBox(MBox *box) define(ins, box, LDefinition(LDefinition::BOX)); } } void LIRGeneratorX64::visitUnbox(MUnbox *unbox) { MDefinition *box = unbox->getOperand(0); + + if (box->type() == MIRType_ObjectOrNull) { + LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(box)); + if (unbox->fallible()) + assignSnapshot(lir, unbox->bailoutKind()); + defineReuseInput(lir, unbox, 0); + return; + } + MOZ_ASSERT(box->type() == MIRType_Value); LUnboxBase *lir; if (IsFloatingPointType(unbox->type())) { lir = new(alloc()) LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type()); } else if (unbox->fallible()) { // If the unbox is fallible, load the Value in a register first to // avoid multiple loads.
--- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -93,20 +93,29 @@ LIRGeneratorX86::visitBox(MBox *box) lir->setDef(1, LDefinition::BogusTemp()); box->setVirtualRegister(vreg); add(lir); } void LIRGeneratorX86::visitUnbox(MUnbox *unbox) { + MDefinition *inner = unbox->getOperand(0); + + if (inner->type() == MIRType_ObjectOrNull) { + LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(inner)); + if (unbox->fallible()) + assignSnapshot(lir, unbox->bailoutKind()); + defineReuseInput(lir, unbox, 0); + return; + } + // An unbox on x86 reads in a type tag (either in memory or a register) and - // a payload. Unlike most instructions conusming a box, we ask for the type + // a payload. Unlike most instructions consuming a box, we ask for the type // second, so that the result can re-use the first input. - MDefinition *inner = unbox->getOperand(0); MOZ_ASSERT(inner->type() == MIRType_Value); ensureDefined(inner); if (IsFloatingPointType(unbox->type())) { LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type()); if (unbox->fallible()) assignSnapshot(lir, unbox->bailoutKind());