Enable nursery strings fixes from 67. Still crashes.

Enable nursery strings fixes from 67. Still crashes.
This commit is contained in:
win7-7 2026-01-15 21:28:02 +02:00 committed by wuggy
commit 6acf628a7b
13 changed files with 94 additions and 66 deletions

View file

@ -1159,15 +1159,15 @@ NewRope(JSContext* cx, unsigned argc, Value* vp)
heap = js::gc::TenuredHeap;
}
JSString* left = args[0].toString();
JSString* right = args[1].toString();
RootedString left(cx, args[0].toString());
RootedString right(cx, args[1].toString());
size_t length = JS_GetStringLength(left) + JS_GetStringLength(right);
if (length > JSString::MAX_LENGTH) {
JS_ReportErrorASCII(cx, "rope length exceeds maximum string length");
return false;
}
Rooted<JSRope*> str(cx, JSRope::new_<NoGC>(cx, left, right, length, heap));
Rooted<JSRope*> str(cx, JSRope::new_<CanGC>(cx, left, right, length, heap));
if (!str)
return false;

View file

@ -147,8 +147,9 @@ GCRuntime::tryNewNurseryString(JSContext* cx, size_t thingSize, AllocKind kind)
if (allowGC && !cx->suppressGC) {
cx->runtime()->gc.minorGC(JS::gcreason::OUT_OF_NURSERY);
// Exceeding gcMaxBytes while tenuring can disable the Nursery.
if (cx->nursery().isEnabled())
// Exceeding gcMaxBytes while tenuring can disable the Nursery, and
// other heuristics can disable nursery strings for this zone.
if (cx->nursery().isEnabled() && cx->zone()->allocNurseryStrings)
return static_cast<JSString*>(cx->nursery().allocateString(cx, cx->zone(), thingSize, kind));
}
return nullptr;

View file

@ -2053,7 +2053,6 @@ inline JSRope*
MarkStack::TaggedPtr::asTempRope() const
{
MOZ_ASSERT(tag() == TempRopeTag);
MOZ_ASSERT(ptr()->isTenured());
MOZ_ASSERT(ptr()->is<JSString>());
return static_cast<JSRope*>(ptr());
}

View file

@ -683,8 +683,10 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
TenureCountCache tenureCounts;
previousGC.reason = JS::gcreason::NO_REASON;
mozilla::Maybe<AutoTraceSession> session;
if (!isEmpty()) {
doCollection(reason, tenureCounts);
session.emplace(rt, JS::HeapState::MinorCollecting);
doCollection(reason, session.ref(), tenureCounts);
} else {
previousGC.nurseryUsedBytes = 0;
previousGC.nurseryCapacity = spaceToEnd(maxChunkCount());
@ -721,6 +723,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
}
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
if (shouldPretenure && zone->allocNurseryStrings && zone->tenuredStrings >= 30 * 1000) {
MOZ_ASSERT(session.isSome(), "discarding JIT code must be in an AutoTraceSession");
JSRuntime::AutoProhibitActiveContextChange apacc(rt);
CancelOffThreadIonCompile(zone);
bool preserving = zone->isPreservingCode();
@ -730,7 +733,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
if (jit::JitCompartment* jitComp = c->jitCompartment()) {
jitComp->discardStubs();
jitComp->stringsCanBeInNursery = false;
jitComp->setStringsCanBeInNursery(false);
}
}
zone->allocNurseryStrings = false;
@ -751,6 +754,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
disable();
endProfile(ProfileKey::Total);
session.reset(); // End the minor GC session, if running one.
minorGcCount_++;
int64_t totalTime = profileTimes_[ProfileKey::Total];
@ -785,9 +789,10 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
void
js::Nursery::doCollection(JS::gcreason::Reason reason,
AutoTraceSession& session,
TenureCountCache& tenureCounts)
{
AutoTraceSession session(rt, JS::HeapState::MinorCollecting);
JSRuntime* rt = runtime();
AutoSetThreadIsPerformingGC performingGC;
AutoDisableProxyCheck disableStrictProxyChecking(rt);
mozilla::DebugOnly<AutoEnterOOMUnsafeRegion> oomUnsafeRegion;

View file

@ -55,6 +55,7 @@ class HeapSlot;
namespace gc {
class AutoMaybeStartBackgroundAllocation;
class AutoTraceSession;
struct Cell;
class MinorCollectionTracer;
class RelocationOverlay;
@ -509,6 +510,7 @@ class Nursery
void* allocate(size_t size);
void doCollection(JS::gcreason::Reason reason,
gc::AutoTraceSession& sesssion,
gc::TenureCountCache& tenureCounts);
/*

View file

@ -1079,14 +1079,8 @@ EmitPostWriteBarrierS(MacroAssembler& masm,
masm.bind(&exit);
}
enum class FieldToBarrier {
REGEXP_PENDING_INPUT,
REGEXP_MATCHES_INPUT,
DEPENDENT_STRING_BASE
};
static void
EmitStoreBufferMutation(MacroAssembler& masm, Register holder, FieldToBarrier field,
EmitStoreBufferMutation(MacroAssembler& masm, Register holder, size_t offset,
Register buffer,
LiveGeneralRegisterSet& liveVolatiles,
void (*fun)(js::gc::StoreBuffer*, js::gc::Cell**))
@ -1105,19 +1099,7 @@ EmitStoreBufferMutation(MacroAssembler& masm, Register holder, FieldToBarrier fi
regs.takeUnchecked(holder);
Register addrReg = regs.takeAny();
switch (field) {
case FieldToBarrier::REGEXP_PENDING_INPUT:
masm.computeEffectiveAddress(Address(holder, RegExpStatics::offsetOfPendingInput()), addrReg);
break;
case FieldToBarrier::REGEXP_MATCHES_INPUT:
masm.computeEffectiveAddress(Address(holder, RegExpStatics::offsetOfMatchesInput()), addrReg);
break;
case FieldToBarrier::DEPENDENT_STRING_BASE:
masm.leaNewDependentStringBase(holder, addrReg);
break;
}
masm.computeEffectiveAddress(Address(holder, offset), addrReg);
bool needExtraReg = !regs.empty();
if (needExtraReg) {
@ -1140,7 +1122,7 @@ EmitStoreBufferMutation(MacroAssembler& masm, Register holder, FieldToBarrier fi
// Warning: this function modifies prev and next.
static void
EmitPostWriteBarrierS(MacroAssembler& masm,
Register string, FieldToBarrier field,
Register holder, size_t offset,
Register prev, Register next,
LiveGeneralRegisterSet& liveVolatiles)
{
@ -1160,7 +1142,7 @@ EmitPostWriteBarrierS(MacroAssembler& masm,
// buffer->putCell(cellp)
masm.bind(&putCell);
EmitStoreBufferMutation(masm, string, field, storebuffer, liveVolatiles,
EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
JSString::addCellAddressToStoreBuffer);
masm.jump(&exit);
@ -1169,7 +1151,7 @@ EmitPostWriteBarrierS(MacroAssembler& masm,
masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit);
masm.loadStoreBuffer(prev, storebuffer);
masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit);
EmitStoreBufferMutation(masm, string, field, storebuffer, liveVolatiles,
EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
JSString::removeCellAddressFromStoreBuffer);
masm.bind(&exit);
@ -1216,6 +1198,7 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
Register temp1, Register temp2, Register temp3,
size_t inputOutputDataStartOffset,
RegExpShared::CompilationMode mode,
bool stringsCanBeInNursery,
Label* notFound, Label* failure)
{
size_t matchPairsStartOffset = inputOutputDataStartOffset + sizeof(irregexp::InputOutputData);
@ -1393,22 +1376,26 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
masm.patchableCallPreBarrier(matchesInputAddress, MIRType::String);
masm.patchableCallPreBarrier(lazySourceAddress, MIRType::String);
if (stringsCanBeInNursery) {
// Writing into RegExpStatics tenured memory; must post-barrier.
if (temp1.volatile_())
volatileRegs.add(temp1);
if (temp1.volatile_())
volatileRegs.add(temp1);
masm.loadPtr(pendingInputAddress, temp2);
masm.storePtr(input, pendingInputAddress);
masm.movePtr(input, temp3);
EmitPostWriteBarrierS(masm, temp1, RegExpStatics::offsetOfPendingInput(),
temp2 /* prev */, temp3 /* next */, volatileRegs);
// Writing into RegExpStatics tenured memory; must post-barrier.
masm.loadPtr(pendingInputAddress, temp2);
masm.storePtr(input, pendingInputAddress);
masm.movePtr(input, temp3);
EmitPostWriteBarrierS(masm, temp1, FieldToBarrier::REGEXP_PENDING_INPUT,
temp2 /* prev */, temp3 /* next */, volatileRegs);
masm.loadPtr(matchesInputAddress, temp2);
masm.storePtr(input, matchesInputAddress);
masm.movePtr(input, temp3);
EmitPostWriteBarrierS(masm, temp1, FieldToBarrier::REGEXP_MATCHES_INPUT,
temp2 /* prev */, temp3 /* next */, volatileRegs);
masm.loadPtr(matchesInputAddress, temp2);
masm.storePtr(input, matchesInputAddress);
masm.movePtr(input, temp3);
EmitPostWriteBarrierS(masm, temp1, RegExpStatics::offsetOfMatchesInput(),
temp2 /* prev */, temp3 /* next */, volatileRegs);
} else {
masm.storePtr(input, pendingInputAddress);
masm.storePtr(input, matchesInputAddress);
}
masm.storePtr(lastIndex, Address(temp1, RegExpStatics::offsetOfLazyIndex()));
masm.store32(Imm32(1), Address(temp1, RegExpStatics::offsetOfPendingLazyEvaluation()));
@ -1449,6 +1436,7 @@ public:
// Caller should call generateFallback after masm.ret(), to generate
// fallback path.
void generate(MacroAssembler& masm, const JSAtomState& names,
CompileRuntime* runtime,
bool latin1, Register string,
Register base, Register temp1, Register temp2,
BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
@ -1461,6 +1449,7 @@ public:
void
CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names,
CompileRuntime* runtime,
bool latin1, Register string,
Register base, Register temp1, Register temp2,
BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
@ -1715,7 +1704,7 @@ JitCompartment::generateRegExpMatcherStub(JSContext* cx)
Label notFound, oolEntry;
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
temp1, temp2, temp5, inputOutputDataStartOffset,
RegExpShared::Normal, &notFound, &oolEntry))
RegExpShared::Normal, stringsCanBeInNursery, &notFound, &oolEntry))
{
return nullptr;
}
@ -1813,7 +1802,9 @@ JitCompartment::generateRegExpMatcherStub(JSContext* cx)
Label isUndefined, storeDone;
masm.branch32(Assembler::LessThan, stringIndexAddress, Imm32(0), &isUndefined);
depStr[isLatin].generate(masm, cx->names(), isLatin, temp3, input, temp4, temp5,
depStr[isLatin].generate(masm, cx->names(),
CompileRuntime::get(cx->runtime()),
isLatin, temp3, input, temp4, temp5,
stringIndexAddress, stringLimitAddress, stringsCanBeInNursery, failure);
masm.storeValue(JSVAL_TYPE_STRING, temp3, stringAddress);
@ -2030,7 +2021,8 @@ JitCompartment::generateRegExpSearcherStub(JSContext* cx)
Label notFound, oolEntry;
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
temp1, temp2, temp3, inputOutputDataStartOffset,
RegExpShared::Normal, &notFound, &oolEntry))
RegExpShared::Normal, stringsCanBeInNursery,
&notFound, &oolEntry))
{
return nullptr;
}
@ -2181,7 +2173,8 @@ JitCompartment::generateRegExpTesterStub(JSContext* cx)
Label notFound, oolEntry;
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
temp1, temp2, temp3, 0,
RegExpShared::MatchOnly, &notFound, &oolEntry))
RegExpShared::MatchOnly, stringsCanBeInNursery,
&notFound, &oolEntry))
{
return nullptr;
}
@ -3768,11 +3761,13 @@ EmitPostWriteBarrier(MacroAssembler& masm, Register objreg, JSObject* maybeConst
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));
if (isGlobal)
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostGlobalWriteBarrier));
else
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
masm.bind(&exit);
}

View file

@ -439,7 +439,7 @@ JitCompartment::~JitCompartment()
}
bool
JitCompartment::initialize(JSContext* cx)
JitCompartment::initialize(JSContext* cx, bool zoneHasNurseryStrings)
{
stubCodes_ = cx->new_<ICStubCodeMap>(cx->runtime());
if (!stubCodes_)
@ -450,7 +450,7 @@ JitCompartment::initialize(JSContext* cx)
return false;
}
stringsCanBeInNursery = cx->nursery().canAllocateStrings();
setStringsCanBeInNursery(zoneHasNurseryStrings);
return true;
}

View file

@ -552,6 +552,8 @@ class JitCompartment
JitCode* regExpSearcherStub_;
JitCode* regExpTesterStub_;
bool stringsCanBeInNursery;
JitCode* generateStringConcatStub(JSContext* cx);
JitCode* generateRegExpMatcherStub(JSContext* cx);
JitCode* generateRegExpSearcherStub(JSContext* cx);
@ -619,7 +621,7 @@ class JitCompartment
JitCompartment();
~JitCompartment();
MOZ_MUST_USE bool initialize(JSContext* cx);
[[nodiscard]] bool initialize(JSContext* cx, bool zoneHasNurseryStrings);
// Initialize code stubs only used by Ion, not Baseline.
MOZ_MUST_USE bool ensureIonStubsExist(JSContext* cx);
@ -638,6 +640,24 @@ class JitCompartment
regExpTesterStub_ = nullptr;
}
bool hasStubs() const {
// Check each stub individually
if (stringConcatStub_)
return true;
if (regExpMatcherStub_)
return true;
if (regExpSearcherStub_)
return true;
if (regExpTesterStub_)
return true;
return false;
}
void setStringsCanBeInNursery(bool allow) {
MOZ_ASSERT(!hasStubs());
stringsCanBeInNursery = allow;
}
JitCode* regExpMatcherStubNoBarrier() const {
return regExpMatcherStub_;
}
@ -672,8 +692,6 @@ class JitCompartment
}
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
bool stringsCanBeInNursery;
};
// Called from JSCompartment::discardJitCode().

View file

@ -596,10 +596,11 @@ GetDynamicName(JSContext* cx, JSObject* envChain, JSString* str, Value* vp)
}
void
PostWriteBarrier(JSRuntime* rt, JSObject* obj)
PostWriteBarrier(JSRuntime* rt, js::gc::Cell* cell)
{
MOZ_ASSERT(!IsInsideNursery(obj));
rt->gc.storeBuffer.putWholeCell(obj);
JS::AutoCheckCannotGC nogc;
MOZ_ASSERT(!IsInsideNursery(cell));
rt->gc.storeBuffer().putWholeCell(cell);
}
static const size_t MAX_WHOLE_CELL_BUFFER_SIZE = 4096;

View file

@ -22,6 +22,12 @@ class InlineTypedObject;
class GeneratorObject;
class TypedArrayObject;
namespace gc {
struct Cell;
}
namespace jit {
enum DataType {
@ -653,8 +659,7 @@ CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHa
void GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp);
void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
void PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index);
void PostWriteBarrier(JSRuntime* rt, js::gc::Cell* cell);
void PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj);
uint32_t GetIndexFromString(JSString* str);

View file

@ -200,7 +200,7 @@ JSCompartment::ensureJitCompartmentExists(JSContext* cx)
if (!jitCompartment_)
return false;
if (!jitCompartment_->initialize(cx)) {
if (!jitCompartment_->initialize(cx, zone()->allocNurseryStrings)) {
js_delete(jitCompartment_);
jitCompartment_ = nullptr;
return false;

View file

@ -173,7 +173,7 @@ CompiledScriptMatches(CompilationSelector selector, JSScript* target)
bool match(JSScript* script) { return script == builder_->script(); }
bool match(JSCompartment* comp) { return comp == builder_->script()->compartment(); }
bool match(Zone* zone) { return zone == builder_->script()->zone(); }
bool match(Zone* zone) { return zone == builder_->script()->zoneFromAnyThread(); }
bool match(JSRuntime* runtime) { return runtime == builder_->script()->runtimeFromAnyThread(); }
bool match(AllCompilations all) { return true; }
bool match(ZonesInState zbs) {

View file

@ -13,6 +13,7 @@
#include "mozilla/TypeTraits.h"
#include "mozilla/Unused.h"
#include "gc/GCInternals.h"
#include "gc/Marking.h"
#include "gc/Nursery.h"
#include "js/UbiNode.h"
@ -198,6 +199,7 @@ JSString::dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) c
if (flags & HAS_BASE_BIT) fputs(" HAS_BASE", fp);
if (flags & INLINE_CHARS_BIT) fputs(" INLINE_CHARS", fp);
if (flags & NON_ATOM_BIT) fputs(" NON_ATOM", fp);
else fputs(" (ATOM)", fp);
if (isPermanentAtom()) fputs(" PERMANENT", fp);
if (flags & LATIN1_CHARS_BIT) fputs(" LATIN1", fp);
if (!isTenured()) fputs(" NURSERY", fp);