Bug 903519 - Strings in the nursery: JIT

Bug 903519 - Strings in the nursery: JIT, majority of it was not done before due CodeGenerator.cpp
This commit is contained in:
win7-7 2026-01-05 00:10:13 +02:00 committed by wuggy
commit 2c7a142dc6
16 changed files with 286 additions and 48 deletions

View file

@ -157,14 +157,17 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitMonitorTypes(LMonitorTypes* lir);
void emitPostWriteBarrier(const LAllocation* obj);
void emitPostWriteBarrier(Register objreg);
template <class LPostBarrierType>
void visitPostWriteBarrierCommonO(LPostBarrierType* lir, OutOfLineCode* ool);
void emitPostWriteBarrierS(Address address, Register prev, Register next);
template <class LPostBarrierType, MIRType nurseryType>
void visitPostWriteBarrierCommon(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);
void visitPostWriteBarrierS(LPostWriteBarrierS* lir) override;
void visitPostWriteElementBarrierS(LPostWriteElementBarrierS* lir) override;
void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool);
void visitOutOfLineCallPostWriteElementBarrier(OutOfLineCallPostWriteElementBarrier* ool);
void visitCallNative(LCallNative* call);

View file

@ -450,14 +450,7 @@ JitCompartment::initialize(JSContext* cx)
return false;
}
cacheIRStubCodes_ = cx->new_<CacheIRStubCodeMap>(cx->runtime());
if (!cacheIRStubCodes_)
return false;
if (!cacheIRStubCodes_->init()) {
ReportOutOfMemory(cx);
return false;
}
stringsCanBeInNursery = cx->nursery().canAllocateStrings();
return true;
}

View file

@ -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

View file

@ -665,6 +665,8 @@ class JitCompartment
}
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
bool stringsCanBeInNursery;
};
// Called from JSCompartment::discardJitCode().

View file

@ -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 =

View file

@ -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);

View file

@ -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)

View file

@ -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)

View file

@ -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 <typename T>
inline CodeOffsetJump branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label) PER_SHARED_ARCH;
template <typename T>
@ -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 <typename T>
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 <typename T>
@ -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.

View file

@ -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
}

View file

@ -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>

View file

@ -223,8 +223,10 @@
_(TypeBarrierO) \
_(MonitorTypes) \
_(PostWriteBarrierO) \
_(PostWriteBarrierS) \
_(PostWriteBarrierV) \
_(PostWriteElementBarrierO) \
_(PostWriteElementBarrierS) \
_(PostWriteElementBarrierV) \
_(InitializedLength) \
_(SetInitializedLength) \

View file

@ -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 <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 != InvalidReg);
@ -606,6 +600,48 @@ MacroAssembler::branchValueIsNurseryObjectImpl(Condition cond, const T& value, R
bind(&done);
}
template <typename T>
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)

View file

@ -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);

View file

@ -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, &notObject);
Register valueObject = masm.extractObject(valueAddress, scratch1);
masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2, &postBarrier);
masm.bind(&notObject);
} else {
MOZ_ASSERT(property.type == JSVAL_TYPE_STRING);
Label notString;
masm.branchTestString(Assembler::NotEqual, valueAddress, &notString);
masm.unboxString(valueAddress, scratch1);
masm.branchPtrInNurseryChunk(Assembler::Equal, scratch1, scratch2, &postBarrier);
masm.bind(&notString);
}
}

View file

@ -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.