diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 02fda9d577..d04a7033f2 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -157,14 +157,17 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitMonitorTypes(LMonitorTypes* lir); void emitPostWriteBarrier(const LAllocation* obj); void emitPostWriteBarrier(Register objreg); - template - void visitPostWriteBarrierCommonO(LPostBarrierType* lir, OutOfLineCode* ool); + void emitPostWriteBarrierS(Address address, Register prev, Register next); + template + void visitPostWriteBarrierCommon(LPostBarrierType* lir, OutOfLineCode* ool); template void visitPostWriteBarrierCommonV(LPostBarrierType* lir, OutOfLineCode* ool); void visitPostWriteBarrierO(LPostWriteBarrierO* lir); void visitPostWriteElementBarrierO(LPostWriteElementBarrierO* lir); void visitPostWriteBarrierV(LPostWriteBarrierV* lir); void visitPostWriteElementBarrierV(LPostWriteElementBarrierV* lir); + void visitPostWriteBarrierS(LPostWriteBarrierS* lir) override; + void visitPostWriteElementBarrierS(LPostWriteElementBarrierS* lir) override; void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool); void visitOutOfLineCallPostWriteElementBarrier(OutOfLineCallPostWriteElementBarrier* ool); void visitCallNative(LCallNative* call); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index cf6b7400bb..06a9b67168 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -450,14 +450,7 @@ JitCompartment::initialize(JSContext* cx) return false; } - cacheIRStubCodes_ = cx->new_(cx->runtime()); - if (!cacheIRStubCodes_) - return false; - - if (!cacheIRStubCodes_->init()) { - ReportOutOfMemory(cx); - return false; - } + stringsCanBeInNursery = cx->nursery().canAllocateStrings(); return true; } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index e93e282a14..eef25fe4c9 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -9094,7 +9094,11 @@ jit::NeedsPostBarrier(MDefinition* value) { if (!GetJitContext()->runtime->gcNursery().exists()) return false; - return value->mightBeType(MIRType::Object); + if (value->mightBeType(MIRType::Object)) + return true; + if (value->mightBeType(MIRType::String) && runtime->canNurseryAllocateStrings()) + return true; + return false; } bool diff --git a/js/src/jit/JitCompartment.h b/js/src/jit/JitCompartment.h index 8db98e7734..eb45171d34 100644 --- a/js/src/jit/JitCompartment.h +++ b/js/src/jit/JitCompartment.h @@ -665,6 +665,8 @@ class JitCompartment } size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + bool stringsCanBeInNursery; }; // Called from JSCompartment::discardJitCode(). diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index e977300210..bc381c447e 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -115,7 +115,7 @@ TryToUseImplicitInterruptCheck(MIRGraph& graph, MBasicBlock* backedge) continue; } - MOZ_ASSERT_IF(iter->isPostWriteBarrierO() || iter->isPostWriteBarrierV(), + MOZ_ASSERT_IF(iter->isPostWriteBarrierO() || iter->isPostWriteBarrierV() || iter->isPostWriteBarrierS(), iter->safepoint()); if (iter->safepoint()) @@ -2761,6 +2761,17 @@ LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins) add(lir, ins); assignSafepoint(lir, ins); break; + } + case MIRType::String: { + LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); + LPostWriteBarrierS* lir = + new(alloc()) LPostWriteBarrierS(useConstantObject + ? useOrConstant(ins->object()) + : useRegister(ins->object()), + useRegister(ins->value()), tmp); + add(lir, ins); + assignSafepoint(lir, ins); + break; } case MIRType::Value: { LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); @@ -2810,6 +2821,19 @@ LIRGenerator::visitPostWriteElementBarrier(MPostWriteElementBarrier* ins) assignSafepoint(lir, ins); break; } + case MIRType::String: { + LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); + LPostWriteElementBarrierS* lir = + new(alloc()) LPostWriteElementBarrierS(useConstantObject + ? useOrConstant(ins->object()) + : useRegister(ins->object()), + useRegister(ins->value()), + useRegister(ins->index()), + tmp); + add(lir, ins); + assignSafepoint(lir, ins); + break; + } case MIRType::Value: { LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp(); LPostWriteElementBarrierV* lir = diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index dc175d8032..bbac2df8b9 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -94,6 +94,10 @@ class MIRGenerator return isProfilerInstrumentationEnabled() && !info().isAnalysis(); } + bool stringsCanBeInNursery() const { + return stringsCanBeInNursery_; + } + bool safeForMinorGC() const { return safeForMinorGC_; } @@ -194,6 +198,7 @@ class MIRGenerator bool instrumentedProfiling_; bool instrumentedProfilingIsCached_; bool safeForMinorGC_; + bool stringsCanBeInNursery_; void addAbortedPreliminaryGroup(ObjectGroup* group); diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 12034958b3..fdb9b1752a 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -37,6 +37,7 @@ MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOpti instrumentedProfiling_(false), instrumentedProfilingIsCached_(false), safeForMinorGC_(true), + stringsCanBeInNursery_(GetJitContext()->runtime()->canNurseryAllocateStrings() : false), minWasmHeapLength_(0), options(options), gs_(alloc) diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 2da9b2dee8..b1510109f4 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -693,11 +693,10 @@ MacroAssembler::shouldNurseryAllocate(gc::AllocKind allocKind, gc::InitialHeap i // Inline version of Nursery::allocateObject. If the object has dynamic slots, // this fills in the slots_ pointer. void -MacroAssembler::nurseryAllocate(Register result, Register temp, gc::AllocKind allocKind, - size_t nDynamicSlots, gc::InitialHeap initialHeap, Label* fail) +MacroAssembler::nurseryAllocateObject(Register result, Register temp, gc::AllocKind allocKind, + size_t nDynamicSlots, Label* fail) { MOZ_ASSERT(IsNurseryAllocable(allocKind)); - MOZ_ASSERT(initialHeap != gc::TenuredHeap); // We still need to allocate in the nursery, per the comment in // shouldNurseryAllocate; however, we need to insert into the @@ -807,8 +806,10 @@ MacroAssembler::allocateObject(Register result, Register temp, gc::AllocKind all checkAllocatorState(fail); - if (shouldNurseryAllocate(allocKind, initialHeap)) - return nurseryAllocate(result, temp, allocKind, nDynamicSlots, initialHeap, fail); + if (shouldNurseryAllocate(allocKind, initialHeap)) { + MOZ_ASSERT(initialHeap == gc::DefaultHeap); + return nurseryAllocateObject(result, temp, allocKind, nDynamicSlots, fail); + } if (!nDynamicSlots) return freeListAllocate(result, temp, allocKind, fail); @@ -869,15 +870,78 @@ MacroAssembler::allocateNonObject(Register result, Register temp, gc::AllocKind } void -MacroAssembler::newGCString(Register result, Register temp, Label* fail) +MacroAssembler::nurseryAllocateString(Register result, Register temp, gc::AllocKind allocKind, + Label* fail) { - allocateNonObject(result, temp, js::gc::AllocKind::STRING, fail); +MOZ_ASSERT(IsNurseryAllocable(allocKind)); + + // No explicit check for nursery.isEnabled() is needed, as the comparison + // with the nursery's end will always fail in such cases. + + CompileZone* zone = GetJitContext()->compartment->zone(); + int thingSize = int(gc::Arena::thingSize(allocKind)); + int totalSize = js::Nursery::stringHeaderSize() + thingSize; + MOZ_ASSERT(totalSize % gc::CellAlignBytes == 0); + + // The nursery position (allocation pointer) and the nursery end are stored + // very close to each other. In practice, the zone will probably be close + // (within 32 bits) as well. If so, use relative offsets between them, to + // avoid multiple 64-bit immediate loads. + auto nurseryPosAddr = intptr_t(zone->addressOfNurseryPosition()); + auto nurseryEndAddr = intptr_t(zone->addressOfNurseryCurrentEnd()); + auto zoneAddr = intptr_t(zone); + + intptr_t maxOffset = std::max(std::abs(nurseryPosAddr - zoneAddr), + std::abs(nurseryEndAddr - zoneAddr)); + if (maxOffset < (1 << 31)) { + movePtr(ImmPtr(zone), temp); // temp holds the Zone pointer from here on. + loadPtr(Address(temp, nurseryPosAddr - zoneAddr), result); + addPtr(Imm32(totalSize), result); // result points past this allocation. + branchPtr(Assembler::Below, Address(temp, nurseryEndAddr - zoneAddr), result, fail); + storePtr(result, Address(temp, nurseryPosAddr - zoneAddr)); // Update position. + subPtr(Imm32(thingSize), result); // Point result at Cell data. + storePtr(temp, Address(result, -js::Nursery::stringHeaderSize())); // Store Zone* + } else { + // Otherwise, the zone is far from the nursery pointers. But the + // nursery pos/end pointers are still near each other. + movePtr(ImmPtr(zone->addressOfNurseryPosition()), temp); + loadPtr(Address(temp, 0), result); + addPtr(Imm32(totalSize), result); + branchPtr(Assembler::Below, Address(temp, nurseryEndAddr - nurseryPosAddr), result, fail); + storePtr(result, Address(temp, 0)); + subPtr(Imm32(thingSize), result); + storePtr(ImmPtr(zone), Address(result, -js::Nursery::stringHeaderSize())); + } +} + +// Inlined equivalent of gc::AllocateString, jumping to fail if nursery +// allocation requested but unsuccessful. +void +MacroAssembler::allocateString(Register result, Register temp, gc::AllocKind allocKind, + gc::InitialHeap initialHeap, Label* fail) +{ + MOZ_ASSERT(allocKind == gc::AllocKind::STRING || allocKind == gc::AllocKind::FAT_INLINE_STRING); + + checkAllocatorState(fail); + + if (shouldNurseryAllocate(allocKind, initialHeap)) { + MOZ_ASSERT(initialHeap == gc::DefaultHeap); + return nurseryAllocateString(result, temp, allocKind, fail); + } + + freeListAllocate(result, temp, allocKind, fail); +} +MacroAssembler::newGCString(Register result, Register temp, Label* fail, bool attemptNursery) +{ + allocateString(result, temp, js::gc::AllocKind::STRING, + attemptNursery ? gc::DefaultHeap : gc::TenuredHeap, fail); } void -MacroAssembler::newGCFatInlineString(Register result, Register temp, Label* fail) +MacroAssembler::newGCFatInlineString(Register result, Register temp, Label* fail, bool attemptNursery) { - allocateNonObject(result, temp, js::gc::AllocKind::FAT_INLINE_STRING, fail); + allocateString(result, temp, js::gc::AllocKind::FAT_INLINE_STRING, + attemptNursery ? gc::DefaultHeap : gc::TenuredHeap, fail); } void @@ -1259,6 +1323,16 @@ MacroAssembler::initUnboxedObjectContents(Register object, UnboxedPlainObject* t } } +void +MacroAssembler::leaNewDependentStringBase(Register str, Register dest) +{ + MOZ_ASSERT(str != dest); + + // Spectre-safe because this is a newly allocated dependent string, thus we + // are certain of its type and the type of its base field. + computeEffectiveAddress(Address(str, JSDependentString::offsetOfBase()), dest); +} + void MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result, Label* fail) diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index c51210dd90..6ed7aae118 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -1021,6 +1021,10 @@ class MacroAssembler : public MacroAssemblerSpecific inline void branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, Label* label) DEFINED_ON(arm, arm64, mips_shared, x86, x64, loongarch64); + // Given a pointer to a GC Cell, retrieve the StoreBuffer pointer from its + // chunk trailer, or nullptr if it is in the tenured heap. + void loadStoreBuffer(Register ptr, Register buffer) PER_ARCH; + template inline CodeOffsetJump branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label) PER_SHARED_ARCH; template @@ -1030,8 +1034,9 @@ class MacroAssembler : public MacroAssemblerSpecific DEFINED_ON(arm, arm64, mips_shared, x86, x64, loongarch64); void branchPtrInNurseryChunk(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; + void branchValueIsNurseryCell(Condition cond, const Address& address, Register temp, Label* label) PER_ARCH; + void branchValueIsNurseryCell(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). @@ -1253,7 +1258,7 @@ class MacroAssembler : public MacroAssemblerSpecific void branchPtrInNurseryChunkImpl(Condition cond, Register ptr, Label* label) DEFINED_ON(x86); template - void branchValueIsNurseryObjectImpl(Condition cond, const T& value, Register temp, Label* label) + void branchValueIsNurseryCellImpl(Condition cond, const T& value, Register temp, Label* label) DEFINED_ON(arm64, mips64, x64); template @@ -1492,12 +1497,13 @@ class MacroAssembler : public MacroAssemblerSpecific void loadStringChars(Register str, Register dest); void loadStringChar(Register str, Register index, Register output, Label* fail); + void leaNewDependentStringBase(Register str, Register dest); + + void loadJSContext(Register dest); - void loadJSContext(Register dest) { - movePtr(ImmPtr(GetJitContext()->runtime->getJSContext()), dest); - } void loadJitActivation(Register dest) { - loadPtr(AbsoluteAddress(GetJitContext()->runtime->addressOfActivation()), dest); + loadJSContext(dest); + loadPtr(Address(dest, offsetof(JSContext, activation_)), dest); } void loadWasmActivationFromTls(Register dest) { loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, cx)), dest); @@ -1749,11 +1755,15 @@ class MacroAssembler : public MacroAssemblerSpecific private: void checkAllocatorState(Label* fail); bool shouldNurseryAllocate(gc::AllocKind allocKind, gc::InitialHeap initialHeap); - void nurseryAllocate(Register result, Register temp, gc::AllocKind allocKind, - size_t nDynamicSlots, gc::InitialHeap initialHeap, Label* fail); + void nurseryAllocateObject(Register result, Register temp, gc::AllocKind allocKind, + size_t nDynamicSlots, Label* fail); void freeListAllocate(Register result, Register temp, gc::AllocKind allocKind, Label* fail); void allocateObject(Register result, Register temp, gc::AllocKind allocKind, uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label* fail); + void nurseryAllocateString(Register result, Register temp, gc::AllocKind allocKind, + Label* fail); + void allocateString(Register result, Register temp, gc::AllocKind allocKind, + gc::InitialHeap initialHeap, Label* fail); void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label* fail); void copySlotsFromTemplate(Register obj, const NativeObject* templateObj, uint32_t start, uint32_t end); @@ -1779,8 +1789,8 @@ class MacroAssembler : public MacroAssemblerSpecific void initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject); - void newGCString(Register result, Register temp, Label* fail); - void newGCFatInlineString(Register result, Register temp, Label* fail); + void newGCString(Register result, Register temp, Label* fail, bool attemptNursery); + void newGCFatInlineString(Register result, Register temp, Label* fail, bool attemptNursery); // Compares two strings for equality based on the JSOP. // This checks for identical pointers, atoms and length and fails for everything else. diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 812c9792a1..54344a8b3d 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1161,7 +1161,8 @@ AssertValidObjectPtr(JSContext* cx, JSObject* obj) // Check what we can, so that we'll hopefully assert/crash if we get a // bogus object (pointer). MOZ_ASSERT(obj->compartment() == cx->compartment()); - MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime()); + MOZ_ASSERT(obj->zoneFromAnyThread() == cx->zone()); + MOZ_ASSERT(obj->runtimeFromActiveCooperatingThread() == cx->runtime()); MOZ_ASSERT_IF(!obj->hasLazyGroup() && obj->maybeShape(), obj->group()->clasp() == obj->maybeShape()->getObjectClass()); @@ -1170,7 +1171,6 @@ AssertValidObjectPtr(JSContext* cx, JSObject* obj) MOZ_ASSERT(obj->isAligned()); gc::AllocKind kind = obj->asTenured().getAllocKind(); MOZ_ASSERT(gc::IsObjectAllocKind(kind)); - MOZ_ASSERT(obj->asTenured().zone() == cx->zone()); } #endif } diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 797a69ff29..ab786d8794 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -6954,6 +6954,33 @@ class LPostWriteBarrierO : public LInstructionHelper<0, 2, 1> } }; +// Generational write barrier used when writing a string to an object. +class LPostWriteBarrierS : public LInstructionHelper<0, 2, 1> +{ + public: + LIR_HEADER(PostWriteBarrierS) + + LPostWriteBarrierS(const LAllocation& obj, const LAllocation& value, + const LDefinition& temp) { + setOperand(0, obj); + setOperand(1, value); + setTemp(0, temp); + } + + const MPostWriteBarrier* mir() const { + return mir_->toPostWriteBarrier(); + } + const LAllocation* object() { + return getOperand(0); + } + const LAllocation* value() { + return getOperand(1); + } + const LDefinition* temp() { + return getTemp(0); + } +}; + // Generational write barrier used when writing a value to another object. class LPostWriteBarrierV : public LInstructionHelper<0, 1 + BOX_PIECES, 1> { @@ -7016,6 +7043,42 @@ class LPostWriteElementBarrierO : public LInstructionHelper<0, 3, 1> } }; +// Generational write barrier used when writing a string to an object's +// elements. +class LPostWriteElementBarrierS : public LInstructionHelper<0, 3, 1> +{ + public: + LIR_HEADER(PostWriteElementBarrierS) + + LPostWriteElementBarrierS(const LAllocation& obj, const LAllocation& value, + const LAllocation& index, const LDefinition& temp) { + setOperand(0, obj); + setOperand(1, value); + setOperand(2, index); + setTemp(0, temp); + } + + const MPostWriteElementBarrier* mir() const { + return mir_->toPostWriteElementBarrier(); + } + + const LAllocation* object() { + return getOperand(0); + } + + const LAllocation* value() { + return getOperand(1); + } + + const LAllocation* index() { + return getOperand(2); + } + + const LDefinition* temp() { + return getTemp(0); + } +}; + // Generational write barrier used when writing a value to another object's // elements. class LPostWriteElementBarrierV : public LInstructionHelper<0, 2 + BOX_PIECES, 1> diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 048cdfaf21..ebc333a850 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -223,8 +223,10 @@ _(TypeBarrierO) \ _(MonitorTypes) \ _(PostWriteBarrierO) \ + _(PostWriteBarrierS) \ _(PostWriteBarrierV) \ _(PostWriteElementBarrierO) \ + _(PostWriteElementBarrierS) \ _(PostWriteElementBarrierV) \ _(InitializedLength) \ _(SetInitializedLength) \ diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 7b3b1caad2..9755d3a355 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -558,6 +558,15 @@ MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result) // =============================================================== // Branch functions +void +MacroAssembler::loadStoreBuffer(Register ptr, Register buffer) +{ + if (ptr != buffer) + movePtr(ptr, buffer); + orPtr(Imm32(gc::ChunkMask), buffer); + loadPtr(Address(buffer, gc::ChunkStoreBufferOffsetFromLastByte), buffer); +} + void MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr, Register temp, Label* label) { @@ -573,24 +582,9 @@ MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr, Register t Imm32(int32_t(gc::ChunkLocation::Nursery)), 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, temp, label); -} - -template -void -MacroAssembler::branchValueIsNurseryObjectImpl(Condition cond, const T& value, Register temp, - Label* label) { MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); MOZ_ASSERT(temp != InvalidReg); @@ -606,6 +600,48 @@ MacroAssembler::branchValueIsNurseryObjectImpl(Condition cond, const T& value, R bind(&done); } +template +void +MacroAssembler::branchValueIsNurseryCellImpl(Condition cond, const T& value, Register temp, + Label* label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + MOZ_ASSERT(temp != InvalidReg); + Label done, checkAddress, checkObjectAddress; + + Register tag = temp; + splitTag(value, tag); + branchTestObject(Assembler::Equal, tag, &checkObjectAddress); + branchTestString(Assembler::NotEqual, tag, cond == Assembler::Equal ? &done : label); + + unboxString(value, temp); + jump(&checkAddress); + + bind(&checkObjectAddress); + unboxObject(value, temp); + + bind(&checkAddress); + orPtr(Imm32(gc::ChunkMask), temp); + branch32(cond, Address(temp, gc::ChunkLocationOffsetFromLastByte), + Imm32(int32_t(gc::ChunkLocation::Nursery)), label); + + bind(&done); +} + +void +MacroAssembler::branchValueIsNurseryCell(Condition cond, const Address& address, Register temp, + Label* label) +{ + branchValueIsNurseryCellImpl(cond, address, temp, label); +} + +void +MacroAssembler::branchValueIsNurseryCell(Condition cond, ValueOperand value, Register temp, + Label* label) +{ + branchValueIsNurseryCellImpl(cond, value, temp, label); +} + void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, const Value& rhs, Label* label) diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 61dbad688b..531ad1f64c 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -596,6 +596,17 @@ class JSString : public js::gc::Cell js::gc::TenuredCell::writeBarrierPre(&thing->asTenured()); } + static void addCellAddressToStoreBuffer(js::gc::StoreBuffer* buffer, js::gc::Cell** cellp) + { + buffer->putCell(cellp); + } + + static void removeCellAddressFromStoreBuffer(js::gc::StoreBuffer* buffer, js::gc::Cell** cellp) + { + buffer->unputCell(cellp); + } + + static void writeBarrierPost(void* cellp, JSString* prev, JSString* next) { // See JSObject::writeBarrierPost for a description of the logic here. MOZ_ASSERT(cellp); diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index 07e78d8a6d..f2f03f05f9 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -136,13 +136,23 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) Label postBarrier; for (size_t i = 0; i < layout.properties().length(); i++) { const UnboxedLayout::Property& property = layout.properties()[i]; + if (!UnboxedTypeNeedsPostBarrier(property.type)) + continue; + + Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value)); if (property.type == JSVAL_TYPE_OBJECT) { - Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value)); Label notObject; masm.branchTestObject(Assembler::NotEqual, valueAddress, ¬Object); Register valueObject = masm.extractObject(valueAddress, scratch1); masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2, &postBarrier); masm.bind(¬Object); + } else { + MOZ_ASSERT(property.type == JSVAL_TYPE_STRING); + Label notString; + masm.branchTestString(Assembler::NotEqual, valueAddress, ¬String); + masm.unboxString(valueAddress, scratch1); + masm.branchPtrInNurseryChunk(Assembler::Equal, scratch1, scratch2, &postBarrier); + masm.bind(¬String); } } diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h index 325af339b8..ffb6aca9c4 100644 --- a/js/src/vm/UnboxedObject.h +++ b/js/src/vm/UnboxedObject.h @@ -40,7 +40,7 @@ UnboxedTypeNeedsPreBarrier(JSValueType type) static inline bool UnboxedTypeNeedsPostBarrier(JSValueType type) { - return type == JSVAL_TYPE_OBJECT; + return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT; } // Class tracking information specific to unboxed objects.