author | Tooru Fujisawa <arai_a@mac.com> |
Sat, 19 Mar 2016 02:42:08 +0900 | |
changeset 289377 | a3d994656b2bf373d1deb2cc13f559a4dcf15747 |
parent 289376 | 4fb38ce47512c913ec5b15ed85a383a7caab65e9 |
child 289378 | e32d4804b6760cc3ab76a83233bd41a044fa7342 |
push id | 30102 |
push user | ryanvm@gmail.com |
push date | Sat, 19 Mar 2016 15:23:17 +0000 |
treeherder | mozilla-central@720fb3d55e28 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 1248289 |
milestone | 48.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
|
--- a/js/src/builtin/Map.js +++ b/js/src/builtin/Map.js @@ -42,18 +42,20 @@ function MapIteratorNext() { // Steps 2-3. if (!IsObject(O) || !IsMapIterator(O)) return callFunction(CallMapIteratorMethodIfWrapped, O, "MapIteratorNext"); // Steps 4-5 (implemented in _GetNextMapEntryForIterator). // Steps 8-9 (omitted). var mapIterationResultPair = iteratorTemp.mapIterationResultPair; - if (!mapIterationResultPair) - mapIterationResultPair = iteratorTemp.mapIterationResultPair = [null, null]; + if (!mapIterationResultPair) { + mapIterationResultPair = iteratorTemp.mapIterationResultPair = + _CreateMapIterationResultPair(); + } var retVal = {value: undefined, done: true}; // Step 10.a, 11. var done = _GetNextMapEntryForIterator(O, mapIterationResultPair); if (!done) { // Steps 10.b-c (omitted).
--- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -181,17 +181,37 @@ MapIteratorObject::finalize(FreeOp* fop, { fop->delete_(MapIteratorObjectRange(static_cast<NativeObject*>(obj))); } bool MapIteratorObject::next(JSContext* cx, Handle<MapIteratorObject*> mapIterator, HandleArrayObject resultPairObj) { + // Check invariants for inlined _GetNextMapEntryForIterator. + + // The array should be tenured, so that post-barrier can be done simply. + MOZ_ASSERT(resultPairObj->isTenured()); + + // The array elements should be fixed. + MOZ_ASSERT(resultPairObj->hasFixedElements()); MOZ_ASSERT(resultPairObj->getDenseInitializedLength() == 2); + MOZ_ASSERT(resultPairObj->getDenseCapacity() >= 2); + +#ifdef DEBUG + // The array elements should be null, so that inlined + // _GetNextMapEntryForIterator doesn't have to perform pre-barrier. + RootedValue val(cx); + if (!GetElement(cx, resultPairObj, resultPairObj, 0, &val)) + return false; + MOZ_ASSERT(val.isNull()); + if (!GetElement(cx, resultPairObj, resultPairObj, 1, &val)) + return false; + MOZ_ASSERT(val.isNull()); +#endif ValueMap::Range* range = MapIteratorObjectRange(mapIterator); if (!range || range->empty()) { js_delete(range); mapIterator->setReservedSlot(RangeSlot, PrivateValue(nullptr)); return true; } switch (mapIterator->kind()) { @@ -208,16 +228,39 @@ MapIteratorObject::next(JSContext* cx, H resultPairObj->setDenseElementWithType(cx, 1, range->front().value); break; } } range->popFront(); return false; } +/* static */ JSObject* +MapIteratorObject::createResultPair(JSContext* cx) +{ + RootedArrayObject resultPairObj(cx, NewDenseFullyAllocatedArray(cx, 2, nullptr, TenuredObject)); + if (!resultPairObj) + return nullptr; + + Rooted<TaggedProto> proto(cx, resultPairObj->getTaggedProto()); + ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultPairObj->getClass(), proto); + if (!group) + return nullptr; + resultPairObj->setGroup(group); + + resultPairObj->setDenseInitializedLength(2); + resultPairObj->initDenseElement(0, NullValue()); + resultPairObj->initDenseElement(1, NullValue()); + + // See comments in MapIteratorObject::next. + AddTypePropertyId(cx, resultPairObj, JSID_VOID, TypeSet::UnknownType()); + + return resultPairObj; +} + /*** Map *****************************************************************************************/ const Class MapObject::class_ = { "Map", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Map), nullptr, // addProperty
--- a/js/src/builtin/MapObject.h +++ b/js/src/builtin/MapObject.h @@ -159,16 +159,18 @@ class MapIteratorObject : public NativeO static const JSFunctionSpec methods[]; static MapIteratorObject* create(JSContext* cx, HandleObject mapobj, ValueMap* data, MapObject::IteratorKind kind); static void finalize(FreeOp* fop, JSObject* obj); static bool next(JSContext* cx, Handle<MapIteratorObject*> mapIterator, HandleArrayObject resultPairObj); + static JSObject* createResultPair(JSContext* cx); + private: inline MapObject::IteratorKind kind() const; }; class SetObject : public NativeObject { public: enum IteratorKind { Values, Entries }; static JSObject* initClass(JSContext* cx, JSObject* obj);
--- a/js/src/ds/OrderedHashTable.h +++ b/js/src/ds/OrderedHashTable.h @@ -454,16 +454,32 @@ class OrderedHashTable // needed. ep = &ht->hashTable[newHash]; while (*ep && *ep > &entry) ep = &(*ep)->chain; entry.chain = *ep; *ep = &entry; } } + + static size_t offsetOfHashTable() { + return offsetof(Range, ht); + } + static size_t offsetOfI() { + return offsetof(Range, i); + } + static size_t offsetOfCount() { + return offsetof(Range, count); + } + static size_t offsetOfPrevP() { + return offsetof(Range, prevp); + } + static size_t offsetOfNext() { + return offsetof(Range, next); + } }; Range all() { return Range(this); } /* * Change the value of the given key. * * This calls Ops::hash on both the current key and the new key. @@ -501,16 +517,28 @@ class OrderedHashTable // needed. ep = &hashTable[newHash]; while (*ep && *ep > entry) ep = &(*ep)->chain; entry->chain = *ep; *ep = entry; } + static size_t offsetOfDataLength() { + return offsetof(OrderedHashTable, dataLength); + } + static size_t offsetOfData() { + return offsetof(OrderedHashTable, data); + } +#ifdef DEBUG + static size_t sizeofData() { + return sizeof(Data); + } +#endif + private: /* Logarithm base 2 of the number of buckets in the hash table initially. */ static uint32_t initialBucketsLog2() { return 1; } static uint32_t initialBuckets() { return 1 << initialBucketsLog2(); } /* * The maximum load factor (mean number of entries per bucket). * It is an invariant that @@ -675,16 +703,23 @@ class OrderedHashMap public: Entry() : key(), value() {} template <typename V> Entry(const Key& k, V&& v) : key(k), value(Forward<V>(v)) {} Entry(Entry&& rhs) : key(Move(rhs.key)), value(Move(rhs.value)) {} const Key key; Value value; + + static size_t offsetOfKey() { + return offsetof(Entry, key); + } + static size_t offsetOfValue() { + return offsetof(Entry, value); + } }; private: struct MapOps : OrderedHashPolicy { typedef Key KeyType; static void makeEmpty(Entry* e) { OrderedHashPolicy::makeEmpty(const_cast<Key*>(&e->key)); @@ -719,16 +754,28 @@ class OrderedHashMap } void rekeyOneEntry(const Key& current, const Key& newKey) { const Entry* e = get(current); if (!e) return; return impl.rekeyOneEntry(current, newKey, Entry(newKey, e->value)); } + + static size_t offsetOfImplDataLength() { + return Impl::offsetOfDataLength(); + } + static size_t offsetOfImplData() { + return Impl::offsetOfData(); + } +#ifdef DEBUG + static size_t sizeofImplData() { + return Impl::sizeofData(); + } +#endif }; template <class T, class OrderedHashPolicy, class AllocPolicy> class OrderedHashSet { private: struct SetOps : OrderedHashPolicy {
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3099,46 +3099,64 @@ class OutOfLineCallPostWriteBarrier : pu LInstruction* lir() const { return lir_; } const LAllocation* object() const { return object_; } }; -void -CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool) -{ - saveLiveVolatile(ool->lir()); - - const LAllocation* obj = ool->object(); - +static void +EmitPostWriteBarrier(MacroAssembler& masm, Register objreg, bool isGlobal, + AllocatableGeneralRegisterSet regs) +{ + Register runtimereg = regs.takeAny(); + masm.mov(ImmPtr(GetJitContext()->runtime), runtimereg); + + void (*fun)(JSRuntime*, JSObject*) = isGlobal ? PostGlobalWriteBarrier : PostWriteBarrier; + masm.setupUnalignedABICall(regs.takeAny()); + masm.passABIArg(runtimereg); + masm.passABIArg(objreg); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun)); +} + +void +CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) +{ AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); Register objreg; bool isGlobal = false; if (obj->isConstant()) { JSObject* object = &obj->toConstant()->toObject(); isGlobal = object->is<GlobalObject>(); objreg = regs.takeAny(); masm.movePtr(ImmGCPtr(object), objreg); } else { objreg = ToRegister(obj); regs.takeUnchecked(objreg); } - Register runtimereg = regs.takeAny(); - masm.mov(ImmPtr(GetJitContext()->runtime), runtimereg); - - void (*fun)(JSRuntime*, JSObject*) = isGlobal ? PostGlobalWriteBarrier : PostWriteBarrier; - masm.setupUnalignedABICall(regs.takeAny()); - masm.passABIArg(runtimereg); - masm.passABIArg(objreg); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun)); - + EmitPostWriteBarrier(masm, objreg, isGlobal, regs); +} + +void +CodeGenerator::emitPostWriteBarrier(Register objreg) +{ + AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); + regs.takeUnchecked(objreg); + EmitPostWriteBarrier(masm, objreg, false, regs); +} + +void +CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool) +{ + saveLiveVolatile(ool->lir()); + const LAllocation* obj = ool->object(); + emitPostWriteBarrier(obj); restoreLiveVolatile(ool->lir()); masm.jump(ool->rejoin()); } template <class LPostBarrierType> void CodeGenerator::visitPostWriteBarrierCommonO(LPostBarrierType* lir, OutOfLineCode* ool) @@ -5650,16 +5668,144 @@ CodeGenerator::visitSetArrayLength(LSetA RegisterOrInt32Constant newLength = ToRegisterOrInt32Constant(lir->index()); masm.inc32(&newLength); masm.store32(newLength, length); // Restore register value if it is used/captured after. masm.dec32(&newLength); } +static inline void +ValueMapRangeFront(MacroAssembler& masm, Register range, Register i, Register front) +{ + masm.loadPtr(Address(range, ValueMap::Range::offsetOfHashTable()), front); + masm.loadPtr(Address(front, ValueMap::offsetOfImplData()), front); + + MOZ_ASSERT(ValueMap::sizeofImplData() == 24); + masm.mulBy3(i, i); + masm.lshiftPtr(Imm32(3), i); + masm.addPtr(i, front); +} + +static inline void +ValueMapRangePopFront(MacroAssembler& masm, Register range, Register front, Register dataLength, + Register temp) +{ + Register i = temp; + + masm.add32(Imm32(1), Address(range, ValueMap::Range::offsetOfCount())); + + masm.load32(Address(range, ValueMap::Range::offsetOfI()), i); + masm.add32(Imm32(1), i); + + Label done, seek; + masm.bind(&seek); + masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done); + + MOZ_ASSERT(ValueMap::sizeofImplData() == 24); + masm.addPtr(Imm32(24), front); + + masm.branchTestMagic(Assembler::NotEqual, Address(front, ValueMap::Entry::offsetOfKey()), + JS_HASH_KEY_EMPTY, &done); + + masm.add32(Imm32(1), i); + masm.jump(&seek); + + masm.bind(&done); + masm.store32(i, Address(range, ValueMap::Range::offsetOfI())); +} + +static inline void +ValueMapRangeDestruct(MacroAssembler& masm, Register range, Register temp0, Register temp1) +{ + Register next = temp0; + Register prevp = temp1; + + masm.loadPtr(Address(range, ValueMap::Range::offsetOfNext()), next); + masm.loadPtr(Address(range, ValueMap::Range::offsetOfPrevP()), prevp); + masm.storePtr(next, Address(prevp, 0)); + + Label hasNoNext; + masm.branchTestPtr(Assembler::Zero, next, next, &hasNoNext); + + masm.storePtr(prevp, Address(next, ValueMap::Range::offsetOfPrevP())); + + masm.bind(&hasNoNext); + + masm.callFreeStub(range); +} + +void +CodeGenerator::visitGetNextMapEntryForIterator(LGetNextMapEntryForIterator* lir) +{ + Register iter = ToRegister(lir->iter()); + Register result = ToRegister(lir->result()); + Register temp = ToRegister(lir->temp0()); + Register dataLength = ToRegister(lir->temp1()); + Register range = ToRegister(lir->temp2()); + Register output = ToRegister(lir->output()); + + masm.loadPrivate(Address(iter, NativeObject::getFixedSlotOffset(MapIteratorObject::RangeSlot)), + range); + + Label iterDone, done; + masm.branchTestPtr(Assembler::Zero, range, range, &iterDone); + + masm.load32(Address(range, ValueMap::Range::offsetOfI()), temp); + masm.loadPtr(Address(range, ValueMap::Range::offsetOfHashTable()), dataLength); + masm.load32(Address(dataLength, ValueMap::offsetOfImplDataLength()), dataLength); + masm.branch32(Assembler::AboveOrEqual, temp, dataLength, &iterDone); + { + masm.push(iter); + + Register front = iter; + ValueMapRangeFront(masm, range, temp, front); + + size_t elementsOffset = NativeObject::offsetOfFixedElements(); + + Address keyAddress(front, ValueMap::Entry::offsetOfKey()); + Address valueAddress(front, ValueMap::Entry::offsetOfValue()); + masm.storeValue(keyAddress, Address(result, elementsOffset), temp); + masm.storeValue(valueAddress, Address(result, elementsOffset + sizeof(Value)), temp); + + Label keyIsNotObject, valueIsNotNurseryObject, emitBarrier; + masm.branchTestObject(Assembler::NotEqual, keyAddress, &keyIsNotObject); + masm.branchValueIsNurseryObject(Assembler::Equal, keyAddress, temp, + &emitBarrier); + masm.bind(&keyIsNotObject); + masm.branchTestObject(Assembler::NotEqual, valueAddress, &valueIsNotNurseryObject); + masm.branchValueIsNurseryObject(Assembler::NotEqual, valueAddress, temp, + &valueIsNotNurseryObject); + { + masm.bind(&emitBarrier); + saveVolatile(temp); + emitPostWriteBarrier(result); + restoreVolatile(temp); + } + masm.bind(&valueIsNotNurseryObject); + + ValueMapRangePopFront(masm, range, front, dataLength, temp); + + masm.pop(iter); + masm.move32(Imm32(0), output); + } + masm.jump(&done); + { + masm.bind(&iterDone); + + ValueMapRangeDestruct(masm, range, temp, dataLength); + + masm.storeValue(PrivateValue(nullptr), + Address(iter, NativeObject::getFixedSlotOffset(MapIteratorObject::RangeSlot))); + + masm.move32(Imm32(1), output); + } + masm.bind(&done); +} + void CodeGenerator::visitTypedArrayLength(LTypedArrayLength* lir) { Register obj = ToRegister(lir->object()); Register out = ToRegister(lir->output()); masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), out); }
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -134,16 +134,18 @@ class CodeGenerator : public CodeGenerat void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir); void visitGuardObjectIdentity(LGuardObjectIdentity* guard); void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir); void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir); void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir); void visitTypeBarrierV(LTypeBarrierV* lir); void visitTypeBarrierO(LTypeBarrierO* lir); void visitMonitorTypes(LMonitorTypes* lir); + void emitPostWriteBarrier(const LAllocation* obj); + void emitPostWriteBarrier(Register objreg); template <class LPostBarrierType> void visitPostWriteBarrierCommonO(LPostBarrierType* lir, OutOfLineCode* ool); template <class LPostBarrierType> void visitPostWriteBarrierCommonV(LPostBarrierType* lir, OutOfLineCode* ool); void visitPostWriteBarrierO(LPostWriteBarrierO* lir); void visitPostWriteElementBarrierO(LPostWriteElementBarrierO* lir); void visitPostWriteBarrierV(LPostWriteBarrierV* lir); void visitPostWriteElementBarrierV(LPostWriteElementBarrierV* lir); @@ -202,16 +204,17 @@ class CodeGenerator : public CodeGenerat void visitCreateThisWithTemplate(LCreateThisWithTemplate* lir); void visitCreateArgumentsObject(LCreateArgumentsObject* lir); void visitGetArgumentsObjectArg(LGetArgumentsObjectArg* lir); void visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir); void visitReturnFromCtor(LReturnFromCtor* lir); void visitComputeThis(LComputeThis* lir); void visitArrayLength(LArrayLength* lir); void visitSetArrayLength(LSetArrayLength* lir); + void visitGetNextMapEntryForIterator(LGetNextMapEntryForIterator* lir); void visitTypedArrayLength(LTypedArrayLength* lir); void visitTypedArrayElements(LTypedArrayElements* lir); void visitSetDisjointTypedElements(LSetDisjointTypedElements* lir); void visitTypedObjectElements(LTypedObjectElements* lir); void visitSetTypedObjectOffset(LSetTypedObjectOffset* lir); void visitTypedObjectDescr(LTypedObjectDescr* ins); void visitStringLength(LStringLength* lir); void visitSubstr(LSubstr* lir);
--- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -104,16 +104,18 @@ _(IntrinsicSubstringKernel) \ _(IntrinsicDefineDataProperty) \ \ _(IntrinsicIsArrayIterator) \ _(IntrinsicIsMapIterator) \ _(IntrinsicIsStringIterator) \ _(IntrinsicIsListIterator) \ \ + _(IntrinsicGetNextMapEntryForIterator) \ + \ _(IntrinsicIsTypedArray) \ _(IntrinsicIsPossiblyWrappedTypedArray) \ _(IntrinsicTypedArrayLength) \ _(IntrinsicPossiblyWrappedTypedArrayLength) \ _(IntrinsicSetDisjointTypedElements) \ \ _(IntrinsicObjectIsTypedObject) \ _(IntrinsicObjectIsTransparentTypedObject) \
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -836,16 +836,19 @@ class IonBuilder InliningStatus inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target); InliningStatus inlineAtomicsIsLockFree(CallInfo& callInfo); // Slot intrinsics. InliningStatus inlineUnsafeSetReservedSlot(CallInfo& callInfo); InliningStatus inlineUnsafeGetReservedSlot(CallInfo& callInfo, MIRType knownValueType); + // Map intrinsics. + InliningStatus inlineGetNextMapEntryForIterator(CallInfo& callInfo); + // TypedArray intrinsics. enum WrappingBehavior { AllowWrappedTypedArrays, RejectWrappedTypedArrays }; InliningStatus inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior); InliningStatus inlineIsTypedArray(CallInfo& callInfo); InliningStatus inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo); InliningStatus inlineTypedArrayLength(CallInfo& callInfo); InliningStatus inlinePossiblyWrappedTypedArrayLength(CallInfo& callInfo); InliningStatus inlineSetDisjointTypedElements(CallInfo& callInfo);
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2693,16 +2693,28 @@ LIRGenerator::visitSetArrayLength(MSetAr MOZ_ASSERT(ins->index()->type() == MIRType_Int32); MOZ_ASSERT(ins->index()->isConstant()); add(new(alloc()) LSetArrayLength(useRegister(ins->elements()), useRegisterOrConstant(ins->index())), ins); } void +LIRGenerator::visitGetNextMapEntryForIterator(MGetNextMapEntryForIterator* ins) +{ + MOZ_ASSERT(ins->iter()->type() == MIRType_Object); + MOZ_ASSERT(ins->result()->type() == MIRType_Object); + auto lir = new(alloc()) LGetNextMapEntryForIterator(useRegister(ins->iter()), + useRegister(ins->result()), + temp(), temp(), temp()); + define(lir, ins); + assignSafepoint(lir, ins); +} + +void LIRGenerator::visitTypedArrayLength(MTypedArrayLength* ins) { MOZ_ASSERT(ins->object()->type() == MIRType_Object); define(new(alloc()) LTypedArrayLength(useRegisterAtStart(ins->object())), ins); } void LIRGenerator::visitTypedArrayElements(MTypedArrayElements* ins)
--- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -187,16 +187,17 @@ class LIRGenerator : public LIRGenerator void visitStoreSlot(MStoreSlot* ins); void visitFilterTypeSet(MFilterTypeSet* ins); void visitTypeBarrier(MTypeBarrier* ins); void visitMonitorTypes(MMonitorTypes* ins); void visitPostWriteBarrier(MPostWriteBarrier* ins); void visitPostWriteElementBarrier(MPostWriteElementBarrier* ins); void visitArrayLength(MArrayLength* ins); void visitSetArrayLength(MSetArrayLength* ins); + void visitGetNextMapEntryForIterator(MGetNextMapEntryForIterator* ins); void visitTypedArrayLength(MTypedArrayLength* ins); void visitTypedArrayElements(MTypedArrayElements* ins); void visitSetDisjointTypedElements(MSetDisjointTypedElements* ins); void visitTypedObjectElements(MTypedObjectElements* ins); void visitSetTypedObjectOffset(MSetTypedObjectOffset* ins); void visitTypedObjectDescr(MTypedObjectDescr* ins); void visitInitializedLength(MInitializedLength* ins); void visitSetInitializedLength(MSetInitializedLength* ins);
--- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -255,16 +255,20 @@ IonBuilder::inlineNativeCall(CallInfo& c return inlineHasClass(callInfo, &MapIteratorObject::class_); case InlinableNative::IntrinsicIsStringIterator: return inlineHasClass(callInfo, &StringIteratorObject::class_); case InlinableNative::IntrinsicIsListIterator: return inlineHasClass(callInfo, &ListIteratorObject::class_); case InlinableNative::IntrinsicDefineDataProperty: return inlineDefineDataProperty(callInfo); + // Map intrinsics. + case InlinableNative::IntrinsicGetNextMapEntryForIterator: + return inlineGetNextMapEntryForIterator(callInfo); + // TypedArray intrinsics. case InlinableNative::IntrinsicIsTypedArray: return inlineIsTypedArray(callInfo); case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray: return inlineIsPossiblyWrappedTypedArray(callInfo); case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength: return inlinePossiblyWrappedTypedArrayLength(callInfo); case InlinableNative::IntrinsicTypedArrayLength: @@ -2186,16 +2190,56 @@ IonBuilder::inlineHasClass(CallInfo& cal } } callInfo.setImplicitlyUsedUnchecked(); return InliningStatus_Inlined; } IonBuilder::InliningStatus +IonBuilder::inlineGetNextMapEntryForIterator(CallInfo& callInfo) +{ + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); + return InliningStatus_NotInlined; + } + + MDefinition* iterArg = callInfo.getArg(0); + MDefinition* resultArg = callInfo.getArg(1); + + if (iterArg->type() != MIRType_Object) + return InliningStatus_NotInlined; + + TemporaryTypeSet* iterTypes = iterArg->resultTypeSet(); + const Class* iterClasp = iterTypes ? iterTypes->getKnownClass(constraints()) : nullptr; + if (iterClasp != &MapIteratorObject::class_) + return InliningStatus_NotInlined; + + if (resultArg->type() != MIRType_Object) + return InliningStatus_NotInlined; + + TemporaryTypeSet* resultTypes = resultArg->resultTypeSet(); + const Class* resultClasp = resultTypes ? resultTypes->getKnownClass(constraints()) : nullptr; + if (resultClasp != &ArrayObject::class_) + return InliningStatus_NotInlined; + + callInfo.setImplicitlyUsedUnchecked(); + + MInstruction* next = MGetNextMapEntryForIterator::New(alloc(), iterArg, + resultArg); + current->add(next); + current->push(next); + + if (!resumeAfter(next)) + return InliningStatus_Error; + + return InliningStatus_Inlined; +} + +IonBuilder::InliningStatus IonBuilder::inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior) { MOZ_ASSERT(!callInfo.constructing()); MOZ_ASSERT(callInfo.argc() == 1); if (callInfo.getArg(0)->type() != MIRType_Object) return InliningStatus_NotInlined; if (getInlineReturnType() != MIRType_Boolean)
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8700,16 +8700,44 @@ class MSetArrayLength MDefinition* index() const { return getOperand(1); } AliasSet getAliasSet() const override { return AliasSet::Store(AliasSet::ObjectFields); } }; +class MGetNextMapEntryForIterator + : public MBinaryInstruction, + public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data +{ + protected: + explicit MGetNextMapEntryForIterator(MDefinition* iter, MDefinition* result) + : MBinaryInstruction(iter, result) + { + setResultType(MIRType_Boolean); + } + + public: + INSTRUCTION_HEADER(GetNextMapEntryForIterator) + + static MGetNextMapEntryForIterator* New(TempAllocator& alloc, MDefinition* iter, MDefinition* result) + { + return new(alloc) MGetNextMapEntryForIterator(iter, result); + } + + MDefinition* iter() { + return getOperand(0); + } + + MDefinition* result() { + return getOperand(1); + } +}; + // Read the length of a typed array. class MTypedArrayLength : public MUnaryInstruction, public SingleObjectPolicy::Data { explicit MTypedArrayLength(MDefinition* obj) : MUnaryInstruction(obj) {
--- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -175,16 +175,17 @@ namespace jit { _(GuardReceiverPolymorphic) \ _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ _(GuardUnboxedExpando) \ _(LoadUnboxedExpando) \ _(ArrayLength) \ _(SetArrayLength) \ + _(GetNextMapEntryForIterator) \ _(TypedArrayLength) \ _(TypedArrayElements) \ _(SetDisjointTypedElements) \ _(TypedObjectDescr) \ _(TypedObjectElements) \ _(SetTypedObjectOffset) \ _(InitializedLength) \ _(SetInitializedLength) \
--- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -844,16 +844,19 @@ class MacroAssembler : public MacroAssem template <typename T> inline CodeOffsetJump branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label) PER_SHARED_ARCH; template <typename T> inline CodeOffsetJump branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label) PER_SHARED_ARCH; void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label) DEFINED_ON(arm, arm64, mips_shared, x86, x64); + void branchPtrInNurseryRange(Condition cond, const Address& address, Register temp, Label* label) + DEFINED_ON(x86); + void branchValueIsNurseryObject(Condition cond, const Address& address, Register temp, Label* label) PER_ARCH; void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) PER_ARCH; // This function compares a Value (lhs) which is having a private pointer // boxed inside a js::Value, with a raw pointer (rhs). inline void branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label) PER_ARCH; inline void branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs, Label* label) PER_SHARED_ARCH; @@ -1001,16 +1004,18 @@ class MacroAssembler : public MacroAssem DEFINED_ON(arm, arm64, mips32, mips64, x86_shared); inline void branchTestMagic(Condition cond, const Address& address, Label* label) PER_SHARED_ARCH; inline void branchTestMagic(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH; template <class L> inline void branchTestMagic(Condition cond, const ValueOperand& value, L label) DEFINED_ON(arm, arm64, mips32, mips64, x86_shared); + inline void branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label) PER_ARCH; + inline void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why, Label* label); void branchTestValue(Condition cond, const ValueOperand& lhs, const Value& rhs, Label* label) PER_ARCH; // Checks if given Value is evaluated to true or false in a condition. // The type of the value should match the type of the method. @@ -1028,16 +1033,23 @@ class MacroAssembler : public MacroAssem inline void branch32Impl(Condition cond, const T& length, const RegisterOrInt32Constant& key, Label* label); template <typename T, typename S> inline void branchPtrImpl(Condition cond, const T& lhs, const S& rhs, Label* label) DEFINED_ON(x86_shared); template <typename T> + void branchPtrInNurseryRangeImpl(Condition cond, const T& ptr, Register temp, Label* label) + DEFINED_ON(x86); + template <typename T> + void branchValueIsNurseryObjectImpl(Condition cond, const T& value, Register temp, Label* label) + DEFINED_ON(arm64, mips64, x64); + + template <typename T> inline void branchTestUndefinedImpl(Condition cond, const T& t, Label* label) DEFINED_ON(arm, arm64, x86_shared); template <typename T> inline void branchTestInt32Impl(Condition cond, const T& t, Label* label) DEFINED_ON(arm, arm64, x86_shared); template <typename T> inline void branchTestDoubleImpl(Condition cond, const T& t, Label* label) DEFINED_ON(arm, arm64, x86_shared);
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h +++ b/js/src/jit/arm/MacroAssembler-arm-inl.h @@ -1159,16 +1159,23 @@ MacroAssembler::branchTestMagic(Conditio template <typename T, class L> void MacroAssembler::branchTestMagicImpl(Condition cond, const T& t, L label) { cond = testMagic(cond, t); ma_b(label, cond); } +void +MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label) +{ + branchTestMagic(cond, valaddr, label); + branch32(cond, ToPayload(valaddr), Imm32(why), label); +} + //}}} check_macroassembler_style // =============================================================== void MacroAssemblerARMCompat::incrementInt32Value(const Address& addr) { asMasm().add32(Imm32(1), ToPayload(addr)); }
--- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -5018,16 +5018,31 @@ MacroAssembler::branchPtrInNurseryRange( ma_mov(Imm32(startChunk), scratch2); as_rsb(scratch2, scratch2, lsr(ptr, Nursery::ChunkShift)); branch32(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual, scratch2, Imm32(nursery.numChunks()), label); } void +MacroAssembler::branchValueIsNurseryObject(Condition cond, const Address& address, + Register temp, Label* label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + + Label done; + + branchTestObject(Assembler::NotEqual, address, cond == Assembler::Equal ? &done : label); + loadPtr(address, temp); + branchPtrInNurseryRange(cond, temp, InvalidReg, label); + + bind(&done); +} + +void MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) { MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); Label done; branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
--- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -840,16 +840,23 @@ class MacroAssemblerARMCompat : public M ma_mov(Imm32(jv.s.payload.i32), scratch2); ma_str(scratch2, ToPayload(dest)); } void storeValue(const Value& val, BaseIndex dest) { ScratchRegisterScope scratch(asMasm()); ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd); storeValue(val, Address(scratch, dest.offset)); } + void storeValue(const Address& src, const Address& dest, Register temp) { + load32(ToType(src), temp); + store32(temp, ToType(dest)); + + load32(ToPayload(src), temp); + store32(temp, ToPayload(dest)); + } void loadValue(Address src, ValueOperand val); void loadValue(Operand dest, ValueOperand val) { loadValue(dest.toAddress(), val); } void loadValue(const BaseIndex& addr, ValueOperand val); void tagValue(JSValueType type, Register payload, ValueOperand dest);
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h +++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h @@ -1258,16 +1258,24 @@ MacroAssembler::branchTestMagic(Conditio template <typename T, class L> void MacroAssembler::branchTestMagicImpl(Condition cond, const T& t, L label) { Condition c = testMagic(cond, t); B(label, c); } +void +MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label) +{ + uint64_t magic = MagicValue(why).asRawBits(); + cmpPtr(valaddr, ImmWord(magic)); + B(label, cond); +} + //}}} check_macroassembler_style // =============================================================== template <typename T> void MacroAssemblerCompat::addToStackPtr(T t) { asMasm().addPtr(t, getStackPointer());
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -712,33 +712,48 @@ MacroAssembler::branchPtrInNurseryRange( const Nursery& nursery = GetJitContext()->runtime->gcNursery(); movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp); addPtr(ptr, temp); branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual, temp, ImmWord(nursery.nurserySize()), label); } void +MacroAssembler::branchValueIsNurseryObject(Condition cond, const Address& address, Register temp, + Label* label) +{ + branchValueIsNurseryObjectImpl(cond, address, temp, label); +} + +void MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) { - MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + branchValueIsNurseryObjectImpl(cond, value.valueReg(), temp, label); +} + +template <typename T> +void +MacroAssembler::branchValueIsNurseryObjectImpl(Condition cond, const T& value, Register temp, + Label* label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); MOZ_ASSERT(temp != ScratchReg && temp != ScratchReg2); // Both may be used internally. const Nursery& nursery = GetJitContext()->runtime->gcNursery(); // Avoid creating a bogus ObjectValue below. if (!nursery.exists()) return; // 'Value' representing the start of the nursery tagged as a JSObject Value start = ObjectValue(*reinterpret_cast<JSObject*>(nursery.start())); movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), temp); - addPtr(value.valueReg(), temp); + addPtr(value, temp); branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual, temp, ImmWord(nursery.nurserySize()), label); } void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, const Value& rhs, Label* label) {
--- a/js/src/jit/arm64/MacroAssembler-arm64.h +++ b/js/src/jit/arm64/MacroAssembler-arm64.h @@ -274,16 +274,20 @@ class MacroAssemblerCompat : public vixl vixl::UseScratchRegisterScope temps(this); const Register scratch = temps.AcquireX().asUnsized(); moveValue(val, ValueOperand(scratch)); storeValue(ValueOperand(scratch), dest); } void storeValue(ValueOperand val, BaseIndex dest) { storePtr(val.valueReg(), dest); } + void storeValue(const Address& src, const Address& dest, Register temp) { + loadPtr(src, temp); + storePtr(temp, dest); + } template <typename T> void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, MIRType slotType) { if (valueType == MIRType_Double) { storeDouble(value.reg().typedReg().fpu(), dest); return; }
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h +++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h @@ -382,16 +382,23 @@ MacroAssembler::branchTestPrimitive(Cond template <class L> void MacroAssembler::branchTestMagic(Condition cond, const ValueOperand& value, L label) { ma_b(value.typeReg(), ImmTag(JSVAL_TAG_MAGIC), label, cond); } +void +MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label) +{ + branchTestMagic(cond, valaddr, label); + branch32(cond, ToPayload(valaddr), Imm32(why), label); +} + //}}} check_macroassembler_style // =============================================================== void MacroAssemblerMIPSCompat::incrementInt32Value(const Address& addr) { asMasm().add32(Imm32(1), ToPayload(addr)); }
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -2138,16 +2138,31 @@ MacroAssembler::callWithABINoProfiler(co call(t9); callWithABIPost(stackAdjust, result); } // =============================================================== // Branch functions void +MacroAssembler::branchValueIsNurseryObject(Condition cond, const Address& address, + Register temp, Label* label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + + Label done; + + branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label); + loadPtr(address, temp); + branchPtrInNurseryRange(cond, temp, InvalidReg, label); + + bind(&done); +} + +void MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) { MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); Label done; branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
--- a/js/src/jit/mips32/MacroAssembler-mips32.h +++ b/js/src/jit/mips32/MacroAssembler-mips32.h @@ -452,16 +452,23 @@ class MacroAssemblerMIPSCompat : public void storeValue(ValueOperand val, Operand dst); void storeValue(ValueOperand val, const BaseIndex& dest); void storeValue(JSValueType type, Register reg, BaseIndex dest); void storeValue(ValueOperand val, const Address& dest); void storeValue(JSValueType type, Register reg, Address dest); void storeValue(const Value& val, Address dest); void storeValue(const Value& val, BaseIndex dest); + void storeValue(const Address& src, const Address& dest, Register temp) { + load32(ToType(src), temp); + store32(temp, ToType(dest)); + + load32(ToPayload(src), temp); + store32(temp, ToPayload(dest)); + } void loadValue(Address src, ValueOperand val); void loadValue(Operand dest, ValueOperand val) { loadValue(dest.toAddress(), val); } void loadValue(const BaseIndex& addr, ValueOperand val); void tagValue(JSValueType type, Register payload, ValueOperand dest);
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h +++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h @@ -341,16 +341,25 @@ template <class L> void MacroAssembler::branchTestMagic(Condition cond, const ValueOperand& value, L label) { SecondScratchRegisterScope scratch2(*this); splitTag(value, scratch2); ma_b(scratch2, ImmTag(JSVAL_TAG_MAGIC), label, cond); } +void +MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label) +{ + uint64_t magic = MagicValue(why).asRawBits(); + ScratchRegisterScope scratch(*this); + loadPtr(valaddr, scratch); + ma_b(scratch, ImmWord(magic), cond, label); +} + //}}} check_macroassembler_style // =============================================================== void MacroAssemblerMIPS64Compat::incrementInt32Value(const Address& addr) { asMasm().add32(Imm32(1), addr); }
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp +++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp @@ -2300,27 +2300,42 @@ MacroAssembler::callWithABINoProfiler(co call(t9); callWithABIPost(stackAdjust, result); } // =============================================================== // Branch functions void +MacroAssembler::branchValueIsNurseryObject(Condition cond, const Address& address, Register temp, + Label* label) +{ + branchValueIsNurseryObject(cond, address, temp, label); +} + +void MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) { + branchValueIsNurseryObject(cond, value.valueReg(), temp, label); +} + +template <typename T> +void +MacroAssembler::branchValueIsNurseryObjectImpl(Condition cond, const T& value, Register temp, + Label* label) +{ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); // 'Value' representing the start of the nursery tagged as a JSObject const Nursery& nursery = GetJitContext()->runtime->gcNursery(); Value start = ObjectValue(*reinterpret_cast<JSObject *>(nursery.start())); movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), SecondScratchReg); - addPtr(value.valueReg(), SecondScratchReg); + addPtr(value, SecondScratchReg); branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual, SecondScratchReg, Imm32(nursery.nurserySize()), label); } void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, const Value& rhs, Label* label) {
--- a/js/src/jit/mips64/MacroAssembler-mips64.h +++ b/js/src/jit/mips64/MacroAssembler-mips64.h @@ -471,16 +471,20 @@ class MacroAssemblerMIPS64Compat : publi void storeValue(ValueOperand val, Operand dst); void storeValue(ValueOperand val, const BaseIndex& dest); void storeValue(JSValueType type, Register reg, BaseIndex dest); void storeValue(ValueOperand val, const Address& dest); void storeValue(JSValueType type, Register reg, Address dest); void storeValue(const Value& val, Address dest); void storeValue(const Value& val, BaseIndex dest); + void storeValue(const Address& src, const Address& dest, Register temp) { + loadPtr(src, temp); + storePtr(temp, dest); + } void loadValue(Address src, ValueOperand val); void loadValue(Operand dest, ValueOperand val) { loadValue(dest.toAddress(), val); } void loadValue(const BaseIndex& addr, ValueOperand val); void tagValue(JSValueType type, Register payload, ValueOperand dest);
--- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -4725,16 +4725,49 @@ class LSetArrayLength : public LInstruct const LAllocation* elements() { return getOperand(0); } const LAllocation* index() { return getOperand(1); } }; +class LGetNextMapEntryForIterator : public LInstructionHelper<1, 2, 3> +{ + public: + LIR_HEADER(GetNextMapEntryForIterator) + + explicit LGetNextMapEntryForIterator(const LAllocation& iter, const LAllocation& result, + const LDefinition& temp0, const LDefinition& temp1, + const LDefinition& temp2) + { + setOperand(0, iter); + setOperand(1, result); + setTemp(0, temp0); + setTemp(1, temp1); + setTemp(2, temp2); + } + + const LAllocation* iter() { + return getOperand(0); + } + const LAllocation* result() { + return getOperand(1); + } + const LDefinition* temp0() { + return getTemp(0); + } + const LDefinition* temp1() { + return getTemp(1); + } + const LDefinition* temp2() { + return getTemp(2); + } +}; + // Read the length of a typed array. class LTypedArrayLength : public LInstructionHelper<1, 1, 0> { public: LIR_HEADER(TypedArrayLength) explicit LTypedArrayLength(const LAllocation& obj) { setOperand(0, obj);
--- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -310,16 +310,17 @@ _(SetPropertyPolymorphicT) \ _(CallIteratorStart) \ _(IteratorStart) \ _(IteratorMore) \ _(IsNoIterAndBranch) \ _(IteratorEnd) \ _(ArrayLength) \ _(SetArrayLength) \ + _(GetNextMapEntryForIterator) \ _(TypedArrayLength) \ _(TypedArrayElements) \ _(SetDisjointTypedElements) \ _(TypedObjectDescr) \ _(TypedObjectElements) \ _(SetTypedObjectOffset) \ _(StringLength) \ _(ArgumentsLength) \
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h +++ b/js/src/jit/x64/MacroAssembler-x64-inl.h @@ -400,16 +400,24 @@ MacroAssembler::branchTest64(Condition c void MacroAssembler::branchTestBooleanTruthy(bool truthy, const ValueOperand& value, Label* label) { test32(value.valueReg(), value.valueReg()); j(truthy ? NonZero : Zero, label); } +void +MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label) +{ + uint64_t magic = MagicValue(why).asRawBits(); + cmpPtr(valaddr, ImmWord(magic)); + j(cond, label); +} + //}}} check_macroassembler_style // =============================================================== void MacroAssemblerX64::incrementInt32Value(const Address& addr) { asMasm().addPtr(Imm32(1), addr); }
--- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -441,33 +441,48 @@ MacroAssembler::branchPtrInNurseryRange( const Nursery& nursery = GetJitContext()->runtime->gcNursery(); movePtr(ImmWord(-ptrdiff_t(nursery.start())), scratch); addPtr(ptr, scratch); branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual, scratch, Imm32(nursery.nurserySize()), label); } void +MacroAssembler::branchValueIsNurseryObject(Condition cond, const Address& address, Register temp, + Label* label) +{ + branchValueIsNurseryObjectImpl(cond, address, temp, label); +} + +void MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) { + branchValueIsNurseryObjectImpl(cond, value.valueReg(), temp, label); +} + +template <typename T> +void +MacroAssembler::branchValueIsNurseryObjectImpl(Condition cond, const T& value, Register temp, + Label* label) +{ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); const Nursery& nursery = GetJitContext()->runtime->gcNursery(); // Avoid creating a bogus ObjectValue below. if (!nursery.exists()) return; // 'Value' representing the start of the nursery tagged as a JSObject Value start = ObjectValue(*reinterpret_cast<JSObject*>(nursery.start())); ScratchRegisterScope scratch(*this); movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), scratch); - addPtr(value.valueReg(), scratch); + addPtr(value, scratch); branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual, scratch, Imm32(nursery.nurserySize()), label); } void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, const Value& rhs, Label* label) {
--- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -139,16 +139,20 @@ class MacroAssemblerX64 : public MacroAs } else { mov(ImmWord(jv.asBits), scratch); } movq(scratch, Operand(dest)); } void storeValue(ValueOperand val, BaseIndex dest) { storeValue(val, Operand(dest)); } + void storeValue(const Address& src, const Address& dest, Register temp) { + loadPtr(src, temp); + storePtr(temp, dest); + } void loadValue(Operand src, ValueOperand val) { movq(src, val.valueReg()); } void loadValue(Address src, ValueOperand val) { loadValue(Operand(src), val); } void loadValue(const BaseIndex& src, ValueOperand val) { loadValue(Operand(src), val);
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h +++ b/js/src/jit/x86/MacroAssembler-x86-inl.h @@ -381,16 +381,23 @@ MacroAssembler::branchTest64(Condition c void MacroAssembler::branchTestBooleanTruthy(bool truthy, const ValueOperand& value, Label* label) { test32(value.payloadReg(), value.payloadReg()); j(truthy ? NonZero : Zero, label); } +void +MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label) +{ + branchTestMagic(cond, valaddr, label); + branch32(cond, ToPayload(valaddr), Imm32(why), label); +} + //}}} check_macroassembler_style // =============================================================== // Note: this function clobbers the source register. void MacroAssemblerX86::convertUInt32ToDouble(Register src, FloatRegister dest) { // src is [0, 2^32-1]
--- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -445,28 +445,57 @@ MacroAssembler::callWithABINoProfiler(co // =============================================================== // Branch functions void MacroAssembler::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label) { + MOZ_ASSERT(ptr != temp); + branchPtrInNurseryRangeImpl(cond, ptr, temp, label); +} + +void +MacroAssembler::branchPtrInNurseryRange(Condition cond, const Address& address, Register temp, + Label* label) +{ + branchPtrInNurseryRangeImpl(cond, address, temp, label); +} + +template <typename T> +void +MacroAssembler::branchPtrInNurseryRangeImpl(Condition cond, const T& ptr, Register temp, + Label* label) +{ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); - MOZ_ASSERT(ptr != temp); MOZ_ASSERT(temp != InvalidReg); // A temp register is required for x86. const Nursery& nursery = GetJitContext()->runtime->gcNursery(); movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp); addPtr(ptr, temp); branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual, temp, Imm32(nursery.nurserySize()), label); } void +MacroAssembler::branchValueIsNurseryObject(Condition cond, const Address& address, Register temp, + Label* label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + + Label done; + + branchTestObject(Assembler::NotEqual, address, cond == Assembler::Equal ? &done : label); + branchPtrInNurseryRange(cond, address, temp, label); + + bind(&done); +} + +void MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) { MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); Label done; branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
--- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -145,16 +145,26 @@ class MacroAssemblerX86 : public MacroAs void storeValue(const Value& val, const T& dest) { jsval_layout jv = JSVAL_TO_IMPL(val); storeTypeTag(ImmTag(jv.s.tag), Operand(dest)); storePayload(val, Operand(dest)); } void storeValue(ValueOperand val, BaseIndex dest) { storeValue(val, Operand(dest)); } + void storeValue(const Address& src, const Address& dest, Register temp) { + MOZ_ASSERT(src.base != temp); + MOZ_ASSERT(dest.base != temp); + + load32(ToType(src), temp); + store32(temp, ToType(dest)); + + load32(ToPayload(src), temp); + store32(temp, ToPayload(dest)); + } void loadValue(Operand src, ValueOperand val) { Operand payload = ToPayload(src); Operand type = ToType(src); // Ensure that loading the payload does not erase the pointer to the // Value in memory or the index. Register baseReg = Register::FromCode(src.base()); Register indexReg = (src.kind() == Operand::MEM_SCALE) ? Register::FromCode(src.index()) : InvalidReg;
--- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -695,16 +695,30 @@ intrinsic_GetNextMapEntryForIterator(JSC Rooted<MapIteratorObject*> mapIterator(cx, &args[0].toObject().as<MapIteratorObject>()); RootedArrayObject result(cx, &args[1].toObject().as<ArrayObject>()); args.rval().setBoolean(MapIteratorObject::next(cx, mapIterator, result)); return true; } static bool +intrinsic_CreateMapIterationResultPair(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + RootedObject result(cx, MapIteratorObject::createResultPair(cx)); + if (!result) + return false; + + args.rval().setObject(*result); + return true; +} + +static bool intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 0); RootedObject proto(cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global())); if (!proto) return false; @@ -1950,17 +1964,19 @@ static const JSFunctionSpec intrinsic_fu IntrinsicIsMapIterator), JS_INLINABLE_FN("IsStringIterator", intrinsic_IsInstanceOfBuiltin<StringIteratorObject>, 1,0, IntrinsicIsStringIterator), JS_INLINABLE_FN("IsListIterator", intrinsic_IsInstanceOfBuiltin<ListIteratorObject>, 1,0, IntrinsicIsListIterator), - JS_FN("_GetNextMapEntryForIterator", intrinsic_GetNextMapEntryForIterator, 3,0), + JS_FN("_CreateMapIterationResultPair", intrinsic_CreateMapIterationResultPair, 0, 0), + JS_INLINABLE_FN("_GetNextMapEntryForIterator", intrinsic_GetNextMapEntryForIterator, 2,0, + IntrinsicGetNextMapEntryForIterator), JS_FN("CallMapIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<MapIteratorObject>>, 2,0), JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0), JS_FN("CallStringIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<StringIteratorObject>>, 2,0),