Bug 1551796 part 3 - Merge ICScript into JitScript. r=tcampbell

ICScript and JitScript had the same lifetime already, but this eliminates
a malloc/free and some extra dereferences (especially for accessing ICEntries
from Baseline Interpreter code).

This is just the minimal set of changes to make it easier to review. Follow-up
changes should:

* Move (now) JitScript methods to JitScript.cpp

* Merge FillBytecodeTypeMap with JitScript::initICEntries so we do just a single
  bytecode walk.

* Move JitScript from the js namespace to js::jit.

Differential Revision: https://phabricator.services.mozilla.com/D32103

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jan de Mooij 2019-05-23 07:55:15 +00:00
parent d68e697c37
commit 932197282d
21 changed files with 356 additions and 365 deletions

View File

@ -255,16 +255,14 @@ void Zone::discardJitCode(FreeOp* fop,
script->maybeReleaseJitScript();
}
// The optimizedStubSpace will be purged below so make sure ICScript
// doesn't point into it. We do this after (potentially) releasing types
// because JitScript contains the ICScript* and there's no need to
// purge stubs if we just destroyed the Typescript.
if (discardBaselineCode && script->hasICScript()) {
script->icScript()->purgeOptimizedStubs(script);
}
// Finally, reset the active flag.
if (JitScript* jitScript = script->jitScript()) {
// If we did not release the JitScript, we need to purge optimized IC
// stubs because the optimizedStubSpace will be purged below.
if (discardBaselineCode) {
jitScript->purgeOptimizedStubs(script);
}
// Finally, reset the active flag.
jitScript->resetActive();
}
}
@ -527,10 +525,11 @@ void MemoryTracker::adopt(MemoryTracker& other) {
static const char* MemoryUseName(MemoryUse use) {
switch (use) {
#define DEFINE_CASE(Name) \
case MemoryUse::Name: return #Name;
JS_FOR_EACH_MEMORY_USE(DEFINE_CASE)
#undef DEFINE_CASE
# define DEFINE_CASE(Name) \
case MemoryUse::Name: \
return #Name;
JS_FOR_EACH_MEMORY_USE(DEFINE_CASE)
# undef DEFINE_CASE
}
MOZ_CRASH("Unknown memory use");
@ -551,8 +550,7 @@ MemoryTracker::~MemoryTracker() {
fprintf(stderr, "Missing calls to JS::RemoveAssociatedMemory:\n");
for (auto r = map.all(); !r.empty(); r.popFront()) {
fprintf(stderr, " %p 0x%zx %s\n", r.front().key().cell,
r.front().value(),
fprintf(stderr, " %p 0x%zx %s\n", r.front().key().cell, r.front().value(),
MemoryUseName(r.front().key().use));
}

View File

@ -1073,7 +1073,7 @@ static bool InitFromBailout(JSContext* cx, size_t frameNo, HandleFunction fun,
const uint32_t pcOff = script->pcToOffset(pc);
BaselineScript* baselineScript = script->baselineScript();
ICScript* icScript = script->icScript();
JitScript* jitScript = script->jitScript();
#ifdef DEBUG
uint32_t expectedDepth;
@ -1136,7 +1136,7 @@ static bool InitFromBailout(JSContext* cx, size_t frameNo, HandleFunction fun,
// Not every monitored op has a monitored fallback stub, e.g.
// JSOP_NEWOBJECT, which always returns the same type for a
// particular script/pc location.
ICEntry& icEntry = icScript->icEntryFromPCOffset(pcOff);
ICEntry& icEntry = jitScript->icEntryFromPCOffset(pcOff);
ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
if (fallbackStub->isMonitoredFallback()) {
enterMonitorChain = true;
@ -1153,7 +1153,7 @@ static bool InitFromBailout(JSContext* cx, size_t frameNo, HandleFunction fun,
builder.setResumeFramePtr(prevFramePtr);
if (enterMonitorChain) {
ICEntry& icEntry = icScript->icEntryFromPCOffset(pcOff);
ICEntry& icEntry = jitScript->icEntryFromPCOffset(pcOff);
ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
MOZ_ASSERT(fallbackStub->isMonitoredFallback());
JitSpew(JitSpew_BaselineBailouts, " [TYPE-MONITOR CHAIN]");
@ -1337,7 +1337,7 @@ static bool InitFromBailout(JSContext* cx, size_t frameNo, HandleFunction fun,
// Calculate and write out return address.
// The icEntry in question MUST have an inlinable fallback stub.
ICEntry& icEntry = icScript->icEntryFromPCOffset(pcOff);
ICEntry& icEntry = jitScript->icEntryFromPCOffset(pcOff);
MOZ_ASSERT(IsInlinableFallback(icEntry.firstStub()->getChainFallback()));
RetAddrEntry& retAddrEntry =

View File

@ -542,8 +542,8 @@ bool BaselineCodeGen<Handler>::emitOutOfLinePostBarrierSlot() {
template <>
bool BaselineCompilerCodeGen::emitNextIC() {
// Emit a call to an IC stored in ICScript. Calls to this must match the
// ICEntry order in ICScript: first the non-op IC entries for |this| and
// Emit a call to an IC stored in JitScript. Calls to this must match the
// ICEntry order in JitScript: first the non-op IC entries for |this| and
// formal arguments, then the for-op IC entries for JOF_IC ops.
JSScript* script = handler.script();
@ -553,7 +553,7 @@ bool BaselineCompilerCodeGen::emitNextIC() {
// to loop until we find an ICEntry for the current pc.
const ICEntry* entry;
do {
entry = &script->icScript()->icEntry(handler.icEntryIndex());
entry = &script->jitScript()->icEntry(handler.icEntryIndex());
handler.moveToNextICEntry();
} while (entry->pcOffset() < pcOffset);
@ -1112,9 +1112,8 @@ void BaselineInterpreterCodeGen::emitInitFrameFields() {
// Initialize interpreterICEntry.
masm.loadPtr(Address(scratch1, JSScript::offsetOfJitScript()), scratch2);
masm.loadPtr(Address(scratch2, JitScript::offsetOfICScript()), scratch2);
masm.computeEffectiveAddress(Address(scratch2, ICScript::offsetOfICEntries()),
scratch2);
masm.computeEffectiveAddress(
Address(scratch2, JitScript::offsetOfICEntries()), scratch2);
masm.storePtr(scratch2, frame.addressOfInterpreterICEntry());
// Initialize interpreterPC.
@ -6266,9 +6265,8 @@ bool BaselineInterpreterCodeGen::emit_JSOP_JUMPTARGET() {
// Compute ICEntry* and store to frame->interpreterICEntry.
loadScript(scratch2);
masm.loadPtr(Address(scratch2, JSScript::offsetOfJitScript()), scratch2);
masm.loadPtr(Address(scratch2, JitScript::offsetOfICScript()), scratch2);
masm.computeEffectiveAddress(
BaseIndex(scratch2, scratch1, TimesOne, ICScript::offsetOfICEntries()),
BaseIndex(scratch2, scratch1, TimesOne, JitScript::offsetOfICEntries()),
scratch2);
masm.storePtr(scratch2, frame.addressOfInterpreterICEntry());
return true;

View File

@ -526,7 +526,7 @@ class BaselineCompilerHandler {
JSScript* script_;
jsbytecode* pc_;
// Index of the current ICEntry in the script's ICScript.
// Index of the current ICEntry in the script's JitScript.
uint32_t icEntryIndex_;
bool compileDebugInstrumentation_;

View File

@ -110,9 +110,9 @@ bool BaselineFrame::pushVarEnvironment(JSContext* cx, HandleScope scope) {
void BaselineFrame::setInterpreterPC(jsbytecode* pc) {
uint32_t pcOffset = script()->pcToOffset(pc);
ICScript* icScript = script()->icScript();
JitScript* jitScript = script()->jitScript();
interpreterPC_ = pc;
interpreterICEntry_ = icScript->interpreterICEntryFromPCOffset(pcOffset);
interpreterICEntry_ = jitScript->interpreterICEntryFromPCOffset(pcOffset);
}
bool BaselineFrame::initForOsr(InterpreterFrame* fp, uint32_t numStackValues) {

View File

@ -156,36 +156,25 @@ class MOZ_RAII FallbackStubAllocator {
}
};
/* static */
UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
} // namespace jit
bool JitScript::initICEntries(JSContext* cx, JSScript* script) {
MOZ_ASSERT(cx->realm()->jitRealm());
MOZ_ASSERT(jit::IsBaselineEnabled(cx));
const uint32_t numICEntries = script->numICEntries();
MOZ_ASSERT(numICEntries() == script->numICEntries());
// Allocate the ICScript.
UniquePtr<ICScript> icScript(
script->zone()->pod_malloc_with_extra<ICScript, ICEntry>(numICEntries));
if (!icScript) {
ReportOutOfMemory(cx);
return nullptr;
}
new (icScript.get()) ICScript(numICEntries);
// TODO(bug 1551796): move JitScript into jit namespace so we don't need this.
using namespace js::jit;
// We need to call prepareForDestruction on ICScript before we |delete| it.
auto prepareForDestruction = mozilla::MakeScopeExit(
[&] { icScript->prepareForDestruction(cx->zone()); });
FallbackStubAllocator alloc(cx, icScript->fallbackStubSpace_);
FallbackStubAllocator alloc(cx, fallbackStubSpace_);
// Index of the next ICEntry to initialize.
uint32_t icEntryIndex = 0;
using Kind = BaselineICFallbackKind;
ICScript* icScriptPtr = icScript.get();
auto addIC = [cx, icScriptPtr, &icEntryIndex, script](jsbytecode* pc,
ICStub* stub) {
auto addIC = [cx, this, &icEntryIndex, script](jsbytecode* pc, ICStub* stub) {
if (!stub) {
MOZ_ASSERT(cx->isExceptionPending());
mozilla::Unused << cx; // Silence -Wunused-lambda-capture in opt builds.
@ -194,7 +183,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
// Initialize the ICEntry.
uint32_t offset = pc ? script->pcToOffset(pc) : ICEntry::ProloguePCOffset;
ICEntry& entryRef = icScriptPtr->icEntry(icEntryIndex);
ICEntry& entryRef = icEntry(icEntryIndex);
icEntryIndex++;
new (&entryRef) ICEntry(stub, offset);
@ -215,14 +204,14 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
ICStub* stub =
alloc.newStub<ICTypeMonitor_Fallback>(Kind::TypeMonitor, nullptr, 0);
if (!addIC(nullptr, stub)) {
return nullptr;
return false;
}
for (size_t i = 0; i < fun->nargs(); i++) {
ICStub* stub = alloc.newStub<ICTypeMonitor_Fallback>(Kind::TypeMonitor,
nullptr, i + 1);
if (!addIC(nullptr, stub)) {
return nullptr;
return false;
}
}
}
@ -248,7 +237,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_IFNE: {
ICStub* stub = alloc.newStub<ICToBool_Fallback>(Kind::ToBool);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -258,7 +247,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_DEC: {
ICStub* stub = alloc.newStub<ICUnaryArith_Fallback>(Kind::UnaryArith);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -276,7 +265,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_POW: {
ICStub* stub = alloc.newStub<ICBinaryArith_Fallback>(Kind::BinaryArith);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -290,7 +279,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_STRICTNE: {
ICStub* stub = alloc.newStub<ICCompare_Fallback>(Kind::Compare);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -298,7 +287,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
ICStub* stub =
alloc.newStub<ICWarmUpCounter_Fallback>(Kind::WarmUpCounter);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -306,12 +295,12 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
ObjectGroup* group =
ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
if (!group) {
return nullptr;
return false;
}
ICStub* stub =
alloc.newStub<ICNewArray_Fallback>(Kind::NewArray, group);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -319,7 +308,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_NEWINIT: {
ICStub* stub = alloc.newStub<ICNewObject_Fallback>(Kind::NewObject);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -331,7 +320,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_STRICTSETELEM: {
ICStub* stub = alloc.newStub<ICSetElem_Fallback>(Kind::SetElem);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -347,7 +336,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_STRICTSETGNAME: {
ICStub* stub = alloc.newStub<ICSetProp_Fallback>(Kind::SetProp);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -357,14 +346,14 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_GETBOUNDNAME: {
ICStub* stub = alloc.newStub<ICGetProp_Fallback>(Kind::GetProp);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
case JSOP_GETPROP_SUPER: {
ICStub* stub = alloc.newStub<ICGetProp_Fallback>(Kind::GetPropSuper);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -372,28 +361,28 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_CALLELEM: {
ICStub* stub = alloc.newStub<ICGetElem_Fallback>(Kind::GetElem);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
case JSOP_GETELEM_SUPER: {
ICStub* stub = alloc.newStub<ICGetElem_Fallback>(Kind::GetElemSuper);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
case JSOP_IN: {
ICStub* stub = alloc.newStub<ICIn_Fallback>(Kind::In);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
case JSOP_HASOWN: {
ICStub* stub = alloc.newStub<ICHasOwn_Fallback>(Kind::HasOwn);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -401,7 +390,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_GETGNAME: {
ICStub* stub = alloc.newStub<ICGetName_Fallback>(Kind::GetName);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -409,7 +398,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_BINDGNAME: {
ICStub* stub = alloc.newStub<ICBindName_Fallback>(Kind::BindName);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -418,7 +407,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
ICStub* stub =
alloc.newStub<ICTypeMonitor_Fallback>(Kind::TypeMonitor, nullptr);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -426,7 +415,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
ICStub* stub =
alloc.newStub<ICGetIntrinsic_Fallback>(Kind::GetIntrinsic);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -439,7 +428,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_STRICTEVAL: {
ICStub* stub = alloc.newStub<ICCall_Fallback>(Kind::Call);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -447,7 +436,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_NEW: {
ICStub* stub = alloc.newStub<ICCall_Fallback>(Kind::CallConstructing);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -456,7 +445,7 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_STRICTSPREADEVAL: {
ICStub* stub = alloc.newStub<ICCall_Fallback>(Kind::SpreadCall);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -465,14 +454,14 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
ICStub* stub =
alloc.newStub<ICCall_Fallback>(Kind::SpreadCallConstructing);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
case JSOP_INSTANCEOF: {
ICStub* stub = alloc.newStub<ICInstanceOf_Fallback>(Kind::InstanceOf);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -480,14 +469,14 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
case JSOP_TYPEOFEXPR: {
ICStub* stub = alloc.newStub<ICTypeOf_Fallback>(Kind::TypeOf);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
case JSOP_ITER: {
ICStub* stub = alloc.newStub<ICGetIterator_Fallback>(Kind::GetIterator);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -496,12 +485,12 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
cx, nullptr, 0, TenuredObject,
ObjectGroup::NewArrayKind::UnknownIndex);
if (!templateObject) {
return nullptr;
return false;
}
ICStub* stub =
alloc.newStub<ICRest_Fallback>(Kind::Rest, templateObject);
if (!addIC(pc, stub)) {
return nullptr;
return false;
}
break;
}
@ -511,13 +500,13 @@ UniquePtr<ICScript> ICScript::create(JSContext* cx, JSScript* script) {
}
// Assert all ICEntries have been initialized.
MOZ_ASSERT(icEntryIndex == numICEntries);
MOZ_ASSERT(icEntryIndex == numICEntries());
prepareForDestruction.release();
return icScript;
return true;
}
namespace jit {
ICStubConstIterator& ICStubConstIterator::operator++() {
MOZ_ASSERT(currentStub_ != nullptr);
currentStub_ = currentStub_->next();
@ -1098,7 +1087,7 @@ bool ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx,
JSScript* script) {
MOZ_ASSERT(fallbackMonitorStub_ == nullptr);
ICStubSpace* space = script->icScript()->fallbackStubSpace();
ICStubSpace* space = script->jitScript()->fallbackStubSpace();
FallbackStubAllocator alloc(cx, *space);
auto* stub = alloc.newStub<ICTypeMonitor_Fallback>(
BaselineICFallbackKind::TypeMonitor, this);
@ -1152,7 +1141,7 @@ bool ICCacheIR_Updated::initUpdatingChain(JSContext* cx, ICStubSpace* space) {
ICStubSpace* ICStubCompiler::StubSpaceForStub(bool makesGCCalls,
JSScript* script) {
if (makesGCCalls) {
return script->icScript()->fallbackStubSpace();
return script->jitScript()->fallbackStubSpace();
}
return script->zone()->jitZone()->optimizedStubSpace();
}
@ -1297,15 +1286,19 @@ void ICStubCompilerBase::PushStubPayload(MacroAssembler& masm,
masm.adjustFrame(sizeof(intptr_t));
}
void ICScript::noteAccessedGetter(uint32_t pcOffset) {
ICEntry& entry = icEntryFromPCOffset(pcOffset);
ICFallbackStub* stub = entry.fallbackStub();
} // namespace jit
void JitScript::noteAccessedGetter(uint32_t pcOffset) {
jit::ICEntry& entry = icEntryFromPCOffset(pcOffset);
jit::ICFallbackStub* stub = entry.fallbackStub();
if (stub->isGetProp_Fallback()) {
stub->toGetProp_Fallback()->noteAccessedGetter();
}
}
namespace jit {
// TypeMonitor_Fallback
//
@ -2493,15 +2486,19 @@ bool FallbackICCodeCompiler::emit_SetElem() {
return tailCallVM<Fn, DoSetElemFallback>(masm);
}
void ICScript::noteHasDenseAdd(uint32_t pcOffset) {
ICEntry& entry = icEntryFromPCOffset(pcOffset);
ICFallbackStub* stub = entry.fallbackStub();
} // namespace jit
void JitScript::noteHasDenseAdd(uint32_t pcOffset) {
jit::ICEntry& entry = icEntryFromPCOffset(pcOffset);
jit::ICFallbackStub* stub = entry.fallbackStub();
if (stub->isSetElem_Fallback()) {
stub->toSetElem_Fallback()->noteHasDenseAdd();
}
}
namespace jit {
template <typename T>
void StoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
const ValueOperand& value, const T& dest,

View File

@ -269,102 +269,6 @@ class ICEntry {
void trace(JSTracer* trc);
};
// [SMDOC] ICScript
//
// ICScript contains IC data used by Baseline (Ion has its own IC chains, stored
// in IonScript).
//
// For each IC we store an ICEntry, which points to the first ICStub in the
// chain. Note that multiple stubs in the same zone can share Baseline IC code.
// This works because the stub data is stored in the ICStub instead of baked in
// in the stub code.
//
// Storing this separate from BaselineScript simplifies debug mode OSR because
// the ICScript can be reused when we replace the BaselineScript. It also makes
// it easier to experiment with interpreter ICs in the future because the
// interpreter and Baseline JIT will be able to use exactly the same IC data.
//
// ICScript contains the following:
//
// * Fallback stub space: this stores all fallback stubs and the "can GC" stubs.
// These stubs are never purged before destroying the ICScript. (Other stubs
// are stored in the optimized stub space stored in JitZone and can be
// discarded more eagerly. See ICScript::purgeOptimizedStubs.)
//
// * List of IC entries, in the following order:
//
// - Type monitor IC for |this|.
// - Type monitor IC for each formal argument.
// - IC for each JOF_IC bytecode op.
//
// ICScript is stored in JitScript and allocated/destroyed at the same time.
class ICScript {
// Allocated space for fallback stubs.
FallbackICStubSpace fallbackStubSpace_ = {};
uint32_t numICEntries_;
explicit ICScript(uint32_t numICEntries) : numICEntries_(numICEntries) {}
ICEntry* icEntryList() {
return (ICEntry*)(reinterpret_cast<uint8_t*>(this) + sizeof(ICScript));
}
public:
static MOZ_MUST_USE js::UniquePtr<ICScript> create(JSContext* cx,
JSScript* script);
~ICScript() {
// The contents of the fallback stub space are removed and freed
// separately after the next minor GC. See prepareForDestruction.
MOZ_ASSERT(fallbackStubSpace_.isEmpty());
}
void prepareForDestruction(Zone* zone) {
// When the script contains pointers to nursery things, the store buffer can
// contain entries that point into the fallback stub space. Since we can
// destroy scripts outside the context of a GC, this situation could result
// in us trying to mark invalid store buffer entries.
//
// Defer freeing any allocated blocks until after the next minor GC.
fallbackStubSpace_.freeAllAfterMinorGC(zone);
}
FallbackICStubSpace* fallbackStubSpace() { return &fallbackStubSpace_; }
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t* data,
size_t* fallbackStubs) const {
*data += mallocSizeOf(this);
// |data| already includes the ICStubSpace itself, so use
// sizeOfExcludingThis.
*fallbackStubs += fallbackStubSpace_.sizeOfExcludingThis(mallocSizeOf);
}
size_t numICEntries() const { return numICEntries_; }
ICEntry& icEntry(size_t index) {
MOZ_ASSERT(index < numICEntries());
return icEntryList()[index];
}
void noteAccessedGetter(uint32_t pcOffset);
void noteHasDenseAdd(uint32_t pcOffset);
void trace(JSTracer* trc);
void purgeOptimizedStubs(JSScript* script);
ICEntry* interpreterICEntryFromPCOffset(uint32_t pcOffset);
ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset);
ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset,
ICEntry* prevLookedUpEntry);
ICEntry& icEntryFromPCOffset(uint32_t pcOffset);
ICEntry& icEntryFromPCOffset(uint32_t pcOffset, ICEntry* prevLookedUpEntry);
static constexpr size_t offsetOfICEntries() { return sizeof(ICScript); }
};
class ICMonitoredStub;
class ICMonitoredFallbackStub;

View File

@ -127,7 +127,10 @@ static bool GetCacheIRReceiverForNativeSetSlot(ICCacheIR_Updated* stub,
return true;
}
ICScript* BaselineInspector::icScript() const { return script->icScript(); }
JitScript* BaselineInspector::jitScript() const {
MOZ_ASSERT(script->hasJitScript());
return script->jitScript();
}
ICEntry& BaselineInspector::icEntryFromPC(jsbytecode* pc) {
ICEntry* entry = maybeICEntryFromPC(pc);
@ -136,10 +139,10 @@ ICEntry& BaselineInspector::icEntryFromPC(jsbytecode* pc) {
}
ICEntry* BaselineInspector::maybeICEntryFromPC(jsbytecode* pc) {
MOZ_ASSERT(hasICScript());
MOZ_ASSERT(hasJitScript());
MOZ_ASSERT(isValidPC(pc));
ICEntry* ent = icScript()->maybeICEntryFromPCOffset(script->pcToOffset(pc),
prevLookedUpEntry);
ICEntry* ent = jitScript()->maybeICEntryFromPCOffset(script->pcToOffset(pc),
prevLookedUpEntry);
if (!ent) {
return nullptr;
}
@ -156,7 +159,7 @@ bool BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc,
// uncacheable access.
MOZ_ASSERT(receivers.empty());
if (!hasICScript()) {
if (!hasJitScript()) {
return true;
}
@ -203,7 +206,7 @@ bool BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc,
}
ICStub* BaselineInspector::monomorphicStub(jsbytecode* pc) {
if (!hasICScript()) {
if (!hasJitScript()) {
return nullptr;
}
@ -227,7 +230,7 @@ ICStub* BaselineInspector::monomorphicStub(jsbytecode* pc) {
bool BaselineInspector::dimorphicStub(jsbytecode* pc, ICStub** pfirst,
ICStub** psecond) {
if (!hasICScript()) {
if (!hasJitScript()) {
return false;
}
@ -575,7 +578,7 @@ static bool TryToSpecializeBinaryArithOp(ICStub** stubs, uint32_t nstubs,
}
MIRType BaselineInspector::expectedBinaryArithSpecialization(jsbytecode* pc) {
if (!hasICScript()) {
if (!hasJitScript()) {
return MIRType::None;
}
@ -610,7 +613,7 @@ MIRType BaselineInspector::expectedBinaryArithSpecialization(jsbytecode* pc) {
}
bool BaselineInspector::hasSeenNonIntegerIndex(jsbytecode* pc) {
if (!hasICScript()) {
if (!hasJitScript()) {
return false;
}
@ -623,7 +626,7 @@ bool BaselineInspector::hasSeenNonIntegerIndex(jsbytecode* pc) {
}
bool BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode* pc) {
if (!hasICScript()) {
if (!hasJitScript()) {
return false;
}
@ -637,7 +640,7 @@ bool BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode* pc) {
}
bool BaselineInspector::hasSeenAccessedGetter(jsbytecode* pc) {
if (!hasICScript()) {
if (!hasJitScript()) {
return false;
}
@ -651,7 +654,7 @@ bool BaselineInspector::hasSeenAccessedGetter(jsbytecode* pc) {
}
bool BaselineInspector::hasSeenDoubleResult(jsbytecode* pc) {
if (!hasICScript()) {
if (!hasJitScript()) {
return false;
}
@ -720,7 +723,7 @@ JSObject* MaybeTemplateObject(ICStub* stub, MetaTwoByteKind kind,
}
JSObject* BaselineInspector::getTemplateObject(jsbytecode* pc) {
if (!hasICScript()) {
if (!hasJitScript()) {
return nullptr;
}
@ -760,7 +763,7 @@ JSObject* BaselineInspector::getTemplateObject(jsbytecode* pc) {
}
ObjectGroup* BaselineInspector::getTemplateObjectGroup(jsbytecode* pc) {
if (!hasICScript()) {
if (!hasJitScript()) {
return nullptr;
}
@ -780,7 +783,7 @@ ObjectGroup* BaselineInspector::getTemplateObjectGroup(jsbytecode* pc) {
JSFunction* BaselineInspector::getSingleCallee(jsbytecode* pc) {
MOZ_ASSERT(*pc == JSOP_NEW);
if (!hasICScript()) {
if (!hasJitScript()) {
return nullptr;
}
@ -815,7 +818,7 @@ JSFunction* BaselineInspector::getSingleCallee(jsbytecode* pc) {
JSObject* BaselineInspector::getTemplateObjectForNative(jsbytecode* pc,
Native native) {
if (!hasICScript()) {
if (!hasJitScript()) {
return nullptr;
}
@ -845,7 +848,7 @@ JSObject* BaselineInspector::getTemplateObjectForNative(jsbytecode* pc,
JSObject* BaselineInspector::getTemplateObjectForClassHook(jsbytecode* pc,
const Class* clasp) {
if (!hasICScript()) {
if (!hasJitScript()) {
return nullptr;
}
@ -1204,7 +1207,7 @@ bool BaselineInspector::commonGetPropFunction(
jsbytecode* pc, jsid id, bool innerized, JSObject** holder,
Shape** holderShape, JSFunction** commonGetter, Shape** globalShape,
bool* isOwnProperty, ReceiverVector& receivers) {
if (!hasICScript()) {
if (!hasJitScript()) {
return false;
}
@ -1286,7 +1289,7 @@ static JSFunction* GetMegamorphicGetterSetterFunction(
bool BaselineInspector::megamorphicGetterSetterFunction(
jsbytecode* pc, jsid id, bool isGetter, JSFunction** getterOrSetter) {
if (!hasICScript()) {
if (!hasJitScript()) {
return false;
}
@ -1451,7 +1454,7 @@ bool BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder,
JSFunction** commonSetter,
bool* isOwnProperty,
ReceiverVector& receivers) {
if (!hasICScript()) {
if (!hasJitScript()) {
return false;
}
@ -1551,7 +1554,7 @@ bool BaselineInspector::maybeInfoForProtoReadSlot(jsbytecode* pc,
MOZ_ASSERT(receivers.empty());
MOZ_ASSERT(!*holder);
if (!hasICScript()) {
if (!hasJitScript()) {
return true;
}
@ -1614,7 +1617,7 @@ static MIRType GetCacheIRExpectedInputType(ICCacheIR_Monitored* stub) {
}
MIRType BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc) {
if (!hasICScript()) {
if (!hasJitScript()) {
return MIRType::Value;
}
@ -1653,7 +1656,7 @@ bool BaselineInspector::instanceOfData(jsbytecode* pc, Shape** shape,
uint32_t* slot,
JSObject** prototypeObject) {
MOZ_ASSERT(*pc == JSOP_INSTANCEOF);
if (!hasICScript()) {
if (!hasJitScript()) {
return false;
}

View File

@ -47,9 +47,9 @@ class BaselineInspector {
MOZ_ASSERT(script);
}
bool hasICScript() const { return script->hasICScript(); }
bool hasJitScript() const { return script->hasJitScript(); }
ICScript* icScript() const;
JitScript* jitScript() const;
private:
#ifdef DEBUG
@ -63,7 +63,7 @@ class BaselineInspector {
ICInspectorType makeICInspector(jsbytecode* pc,
ICStub::Kind expectedFallbackKind) {
ICEntry* ent = nullptr;
if (hasICScript()) {
if (hasJitScript()) {
ent = &icEntryFromPC(pc);
MOZ_ASSERT(ent->fallbackStub()->kind() == expectedFallbackKind);
}

View File

@ -517,7 +517,7 @@ void BaselineScript::trace(JSTracer* trc) {
TraceNullableEdge(trc, &templateEnv_, "baseline-template-environment");
}
void ICScript::trace(JSTracer* trc) {
void JitScript::trace(JSTracer* trc) {
// Mark all IC stub codes hanging off the IC stub entries.
for (size_t i = 0; i < numICEntries(); i++) {
ICEntry& ent = icEntry(i);
@ -620,12 +620,12 @@ CompactBufferReader BaselineScript::pcMappingReader(size_t indexEntry) {
}
struct ICEntries {
ICScript* const icScript_;
JitScript* const jitScript_;
explicit ICEntries(ICScript* icScript) : icScript_(icScript) {}
explicit ICEntries(JitScript* jitScript) : jitScript_(jitScript) {}
size_t numEntries() const { return icScript_->numICEntries(); }
ICEntry& operator[](size_t index) const { return icScript_->icEntry(index); }
size_t numEntries() const { return jitScript_->numICEntries(); }
ICEntry& operator[](size_t index) const { return jitScript_->icEntry(index); }
};
struct RetAddrEntries {
@ -712,7 +712,7 @@ uint8_t* BaselineScript::returnAddressForEntry(const RetAddrEntry& ent) {
return method()->raw() + ent.returnOffset().offset();
}
ICEntry* ICScript::maybeICEntryFromPCOffset(uint32_t pcOffset) {
ICEntry* JitScript::maybeICEntryFromPCOffset(uint32_t pcOffset) {
// This method ignores prologue IC entries. There can be at most one
// non-prologue IC per bytecode op.
@ -729,14 +729,14 @@ ICEntry* ICScript::maybeICEntryFromPCOffset(uint32_t pcOffset) {
return &entry;
}
ICEntry& ICScript::icEntryFromPCOffset(uint32_t pcOffset) {
ICEntry& JitScript::icEntryFromPCOffset(uint32_t pcOffset) {
ICEntry* entry = maybeICEntryFromPCOffset(pcOffset);
MOZ_RELEASE_ASSERT(entry);
return *entry;
}
ICEntry* ICScript::maybeICEntryFromPCOffset(uint32_t pcOffset,
ICEntry* prevLookedUpEntry) {
ICEntry* JitScript::maybeICEntryFromPCOffset(uint32_t pcOffset,
ICEntry* prevLookedUpEntry) {
// Do a linear forward search from the last queried PC offset, or fallback to
// a binary search if the last offset is too far away.
if (prevLookedUpEntry && pcOffset >= prevLookedUpEntry->pcOffset() &&
@ -756,14 +756,14 @@ ICEntry* ICScript::maybeICEntryFromPCOffset(uint32_t pcOffset,
return maybeICEntryFromPCOffset(pcOffset);
}
ICEntry& ICScript::icEntryFromPCOffset(uint32_t pcOffset,
ICEntry* prevLookedUpEntry) {
ICEntry& JitScript::icEntryFromPCOffset(uint32_t pcOffset,
ICEntry* prevLookedUpEntry) {
ICEntry* entry = maybeICEntryFromPCOffset(pcOffset, prevLookedUpEntry);
MOZ_RELEASE_ASSERT(entry);
return *entry;
}
ICEntry* ICScript::interpreterICEntryFromPCOffset(uint32_t pcOffset) {
ICEntry* JitScript::interpreterICEntryFromPCOffset(uint32_t pcOffset) {
// We have to return the entry to store in BaselineFrame::interpreterICEntry
// when resuming in the Baseline Interpreter at pcOffset. The bytecode op at
// pcOffset does not necessarily have an ICEntry, so we want to return the
@ -1197,8 +1197,8 @@ void BaselineInterpreter::toggleCodeCoverageInstrumentation(bool enable) {
toggleCodeCoverageInstrumentationUnchecked(enable);
}
void ICScript::purgeOptimizedStubs(JSScript* script) {
MOZ_ASSERT(script->icScript() == this);
void JitScript::purgeOptimizedStubs(JSScript* script) {
MOZ_ASSERT(script->jitScript() == this);
Zone* zone = script->zone();
if (zone->isGCSweeping() && IsAboutToBeFinalizedDuringSweep(*script)) {
@ -1294,18 +1294,18 @@ bool HasEnteredCounters(ICEntry& entry) {
}
void jit::JitSpewBaselineICStats(JSScript* script, const char* dumpReason) {
MOZ_ASSERT(script->hasICScript());
MOZ_ASSERT(script->hasJitScript());
JSContext* cx = TlsContext.get();
AutoStructuredSpewer spew(cx, SpewChannel::BaselineICStats, script);
if (!spew) {
return;
}
ICScript* icScript = script->icScript();
JitScript* jitScript = script->jitScript();
spew->property("reason", dumpReason);
spew->beginListProperty("entries");
for (size_t i = 0; i < icScript->numICEntries(); i++) {
ICEntry& entry = icScript->icEntry(i);
for (size_t i = 0; i < jitScript->numICEntries(); i++) {
ICEntry& entry = jitScript->icEntry(i);
if (!HasEnteredCounters(entry)) {
continue;
}
@ -1352,13 +1352,7 @@ void jit::FinishDiscardBaselineScript(FreeOp* fop, JSScript* script) {
void jit::AddSizeOfBaselineData(JSScript* script,
mozilla::MallocSizeOf mallocSizeOf,
size_t* data, size_t* fallbackStubs) {
if (script->hasICScript()) {
// ICScript is stored in JitScript but we report its size here and not
// in JitScript::sizeOfIncludingThis.
script->icScript()->addSizeOfIncludingThis(mallocSizeOf, data,
fallbackStubs);
}
size_t* data) {
if (script->hasBaselineScript()) {
script->baselineScript()->addSizeOfIncludingThis(mallocSizeOf, data);
}

View File

@ -575,7 +575,7 @@ bool BaselineCompileFromBaselineInterpreter(JSContext* cx, BaselineFrame* frame,
void FinishDiscardBaselineScript(FreeOp* fop, JSScript* script);
void AddSizeOfBaselineData(JSScript* script, mozilla::MallocSizeOf mallocSizeOf,
size_t* data, size_t* fallbackStubs);
size_t* data);
void ToggleBaselineProfiling(JSRuntime* runtime, bool enable);

View File

@ -3146,8 +3146,8 @@ void jit::TraceJitScripts(JSTracer* trc, JSScript* script) {
jit::BaselineScript::Trace(trc, script->baselineScript());
}
if (script->hasICScript()) {
script->icScript()->trace(trc);
if (script->hasJitScript()) {
script->jitScript()->trace(trc);
}
}

View File

@ -18,6 +18,7 @@
#include "vm/TypeInference-inl.h"
using namespace js;
using namespace js::jit;
static void FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap) {
uint32_t added = 0;
@ -35,28 +36,28 @@ static void FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap) {
}
static size_t NumTypeSets(JSScript* script) {
size_t num = script->numBytecodeTypeSets() + 1 /* this */;
if (JSFunction* fun = script->functionNonDelazifying()) {
num += fun->nargs();
}
// We rely on |num| being in a safe range to prevent overflow when allocating
// JitScript.
// We rely on |num| not overflowing below.
static_assert(JSScript::MaxBytecodeTypeSets == UINT16_MAX,
"JSScript typesets should have safe range to avoid overflow");
static_assert(JSFunction::NArgsBits == 16,
"JSFunction nargs should have safe range to avoid overflow");
size_t num = script->numBytecodeTypeSets() + 1 /* this */;
if (JSFunction* fun = script->functionNonDelazifying()) {
num += fun->nargs();
}
return num;
}
JitScript::JitScript(JSScript* script, ICScriptPtr&& icScript,
uint32_t numTypeSets)
: icScript_(std::move(icScript)), numTypeSets_(numTypeSets) {
JitScript::JitScript(JSScript* script, uint32_t typeSetOffset,
uint32_t bytecodeTypeMapOffset)
: typeSetOffset_(typeSetOffset),
bytecodeTypeMapOffset_(bytecodeTypeMapOffset) {
setTypesGeneration(script->zone()->types.generation);
StackTypeSet* array = typeArrayDontCheckGeneration();
for (unsigned i = 0; i < numTypeSets; i++) {
for (uint32_t i = 0, len = numTypeSets(); i < len; i++) {
new (&array[i]) StackTypeSet();
}
@ -84,38 +85,52 @@ bool JSScript::createJitScript(JSContext* cx) {
return true;
}
UniquePtr<jit::ICScript> icScript(jit::ICScript::create(cx, this));
if (!icScript) {
return false;
}
// We need to call prepareForDestruction on ICScript before we |delete| it.
auto prepareForDestruction = mozilla::MakeScopeExit(
[&] { icScript->prepareForDestruction(cx->zone()); });
size_t numTypeSets = NumTypeSets(this);
size_t bytecodeTypeMapEntries = numBytecodeTypeSets();
// Calculate allocation size. This cannot overflow, see comment in
// NumTypeSets.
static_assert(sizeof(JitScript) ==
sizeof(StackTypeSet) + offsetof(JitScript, typeArray_),
"typeArray_ must be last member of JitScript");
size_t allocSize =
(offsetof(JitScript, typeArray_) + numTypeSets * sizeof(StackTypeSet) +
bytecodeTypeMapEntries * sizeof(uint32_t));
static_assert(sizeof(JitScript) % sizeof(uintptr_t) == 0,
"Trailing arrays must be aligned properly");
static_assert(sizeof(ICEntry) % sizeof(uintptr_t) == 0,
"Trailing arrays must be aligned properly");
static_assert(sizeof(StackTypeSet) % sizeof(uintptr_t) == 0,
"Trailing arrays must be aligned properly");
auto jitScript =
reinterpret_cast<JitScript*>(cx->pod_malloc<uint8_t>(allocSize));
if (!jitScript) {
// Calculate allocation size.
CheckedInt<uint32_t> allocSize = sizeof(JitScript);
allocSize += CheckedInt<uint32_t>(numICEntries()) * sizeof(ICEntry);
allocSize += CheckedInt<uint32_t>(numTypeSets) * sizeof(StackTypeSet);
allocSize += CheckedInt<uint32_t>(numBytecodeTypeSets()) * sizeof(uint32_t);
if (!allocSize.isValid()) {
ReportAllocationOverflow(cx);
return false;
}
prepareForDestruction.release();
void* raw = cx->pod_malloc<uint8_t>(allocSize.value());
MOZ_ASSERT(uintptr_t(raw) % alignof(JitScript) == 0);
if (!raw) {
return false;
}
uint32_t typeSetOffset = sizeof(JitScript) + numICEntries() * sizeof(ICEntry);
uint32_t bytecodeTypeMapOffset =
typeSetOffset + numTypeSets * sizeof(StackTypeSet);
UniquePtr<JitScript> jitScript(
new (raw) JitScript(this, typeSetOffset, bytecodeTypeMapOffset));
// Sanity check the length computations.
MOZ_ASSERT(jitScript->numICEntries() == numICEntries());
MOZ_ASSERT(jitScript->numTypeSets() == numTypeSets);
// We need to call prepareForDestruction on JitScript before we |delete| it.
auto prepareForDestruction = mozilla::MakeScopeExit(
[&] { jitScript->prepareForDestruction(cx->zone()); });
if (!jitScript->initICEntries(cx, this)) {
return false;
}
MOZ_ASSERT(!jitScript_);
jitScript_ =
new (jitScript) JitScript(this, std::move(icScript), numTypeSets);
prepareForDestruction.release();
jitScript_ = jitScript.release();
// We have a JitScript so we can set the script's jitCodeRaw_ pointer to the
// Baseline Interpreter code.
@ -123,19 +138,19 @@ bool JSScript::createJitScript(JSContext* cx) {
#ifdef DEBUG
AutoSweepJitScript sweep(this);
StackTypeSet* typeArray = jitScript->typeArrayDontCheckGeneration();
StackTypeSet* typeArray = jitScript_->typeArrayDontCheckGeneration();
for (unsigned i = 0; i < numBytecodeTypeSets(); i++) {
InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u %p",
InferSpewColor(&typeArray[i]), &typeArray[i],
InferSpewColorReset(), i, this);
}
StackTypeSet* thisTypes = jitScript->thisTypes(sweep, this);
StackTypeSet* thisTypes = jitScript_->thisTypes(sweep, this);
InferSpew(ISpewOps, "typeSet: %sT%p%s this %p", InferSpewColor(thisTypes),
thisTypes, InferSpewColorReset(), this);
unsigned nargs =
functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0;
for (unsigned i = 0; i < nargs; i++) {
StackTypeSet* types = jitScript->argTypes(sweep, this, i);
StackTypeSet* types = jitScript_->argTypes(sweep, this, i);
InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u %p", InferSpewColor(types),
types, InferSpewColorReset(), i, this);
}
@ -158,8 +173,7 @@ void JSScript::maybeReleaseJitScript() {
}
void JitScript::destroy(Zone* zone) {
icScript_->prepareForDestruction(zone);
prepareForDestruction(zone);
js_delete(this);
}

View File

@ -7,6 +7,7 @@
#ifndef jit_JitScript_h
#define jit_JitScript_h
#include "jit/BaselineIC.h"
#include "js/UniquePtr.h"
#include "vm/TypeInference.h"
@ -14,30 +15,72 @@ class JSScript;
namespace js {
namespace jit {
class ICScript;
} // namespace jit
// JitScript stores type inference and JIT data for a script. Scripts with a
// JitScript can run in the Baseline Interpreter.
class JitScript {
// [SMDOC] JitScript
//
// JitScript stores type inference data, Baseline ICs and other JIT-related data
// for a script. Scripts with a JitScript can run in the Baseline Interpreter.
//
// IC Data
// -------
//
// JitScript contains IC data used by Baseline (Interpreter and JIT). Ion has
// its own IC chains stored in IonScript.
//
// For each IC we store an ICEntry, which points to the first ICStub in the
// chain. Note that multiple stubs in the same zone can share Baseline IC code.
// This works because the stub data is stored in the ICStub instead of baked in
// in the stub code.
//
// Storing this separate from BaselineScript allows us to use the same ICs in
// the Baseline Interpreter and Baseline JIT. It also simplifies debug mode OSR
// because the JitScript can be reused when we have to recompile the
// BaselineScript.
//
// JitScript contains the following IC data structures:
//
// * Fallback stub space: this stores all fallback stubs and the "can GC" stubs.
// These stubs are never purged before destroying the JitScript. (Other stubs
// are stored in the optimized stub space stored in JitZone and can be
// discarded more eagerly. See JitScript::purgeOptimizedStubs.)
//
// * List of IC entries, in the following order:
//
// - Type monitor IC for |this|.
// - Type monitor IC for each formal argument.
// - IC for each JOF_IC bytecode op.
//
// Memory Layout
// -------------
//
// JitScript has various trailing (variable-length) arrays. The memory layout is
// as follows:
//
// Item | Offset
// ------------------------+------------------------
// JitScript | 0
// ICEntry[] | sizeof(JitScript)
// StackTypeSet[] | typeSetOffset_
// uint32_t[] | bytecodeTypeMapOffset_
// (= bytecode type map) |
//
// These offsets are also used to compute numICEntries and numTypeSets.
class alignas(uintptr_t) JitScript final {
friend class ::JSScript;
// Allocated space for fallback IC stubs.
jit::FallbackICStubSpace fallbackStubSpace_ = {};
// The freeze constraints added to stack type sets will only directly
// invalidate the script containing those stack type sets. This Vector
// contains compilations that inlined this script, so we can invalidate
// them as well.
RecompileInfoVector inlinedCompilations_;
// ICScript and JitScript have the same lifetimes, so we store a pointer to
// ICScript here to not increase sizeof(JSScript).
using ICScriptPtr = js::UniquePtr<js::jit::ICScript>;
ICScriptPtr icScript_;
// Offset of the StackTypeSet array.
uint32_t typeSetOffset_ = 0;
// Number of TypeSets in typeArray_.
uint32_t numTypeSets_;
// Offset of the bytecode type map.
uint32_t bytecodeTypeMapOffset_ = 0;
// This field is used to avoid binary searches for the sought entry when
// bytecode map queries are in linear order.
@ -57,15 +100,14 @@ class JitScript {
};
Flags flags_ = {}; // Zero-initialize flags.
// Variable-size array. This is followed by the bytecode type map.
StackTypeSet typeArray_[1];
jit::ICEntry* icEntries() {
uint8_t* base = reinterpret_cast<uint8_t*>(this);
return reinterpret_cast<jit::ICEntry*>(base + offsetOfICEntries());
}
StackTypeSet* typeArrayDontCheckGeneration() {
// Ensure typeArray_ is the last data member of JitScript.
static_assert(sizeof(JitScript) ==
sizeof(typeArray_) + offsetof(JitScript, typeArray_),
"typeArray_ must be the last member of JitScript");
return const_cast<StackTypeSet*>(typeArray_);
uint8_t* base = reinterpret_cast<uint8_t*>(this);
return reinterpret_cast<StackTypeSet*>(base + typeSetOffset_);
}
uint32_t typesGeneration() const { return uint32_t(flags_.typesGeneration); }
@ -75,7 +117,18 @@ class JitScript {
}
public:
JitScript(JSScript* script, ICScriptPtr&& icScript, uint32_t numTypeSets);
JitScript(JSScript* script, uint32_t typeSetOffset,
uint32_t bytecodeTypeMapOffset);
#ifdef DEBUG
~JitScript() {
// The contents of the fallback stub space are removed and freed
// separately after the next minor GC. See prepareForDestruction.
MOZ_ASSERT(fallbackStubSpace_.isEmpty());
}
#endif
MOZ_MUST_USE bool initICEntries(JSContext* cx, JSScript* script);
bool hasFreezeConstraints(const js::AutoSweepJitScript& sweep) const {
MOZ_ASSERT(sweep.jitScript() == this);
@ -103,7 +156,12 @@ class JitScript {
return inlinedCompilations_.append(info);
}
uint32_t numTypeSets() const { return numTypeSets_; }
uint32_t numICEntries() const {
return (typeSetOffset_ - offsetOfICEntries()) / sizeof(jit::ICEntry);
}
uint32_t numTypeSets() const {
return (bytecodeTypeMapOffset_ - typeSetOffset_) / sizeof(StackTypeSet);
}
uint32_t* bytecodeTypeMapHint() { return &bytecodeTypeMapHint_; }
@ -111,11 +169,6 @@ class JitScript {
void setActive() { flags_.active = true; }
void resetActive() { flags_.active = false; }
jit::ICScript* icScript() const {
MOZ_ASSERT(icScript_);
return icScript_.get();
}
/* Array of type sets for variables and JOF_TYPESET ops. */
StackTypeSet* typeArray(const js::AutoSweepJitScript& sweep) {
MOZ_ASSERT(sweep.jitScript() == this);
@ -123,8 +176,8 @@ class JitScript {
}
uint32_t* bytecodeTypeMap() {
MOZ_ASSERT(numTypeSets_ > 0);
return reinterpret_cast<uint32_t*>(typeArray_ + numTypeSets_);
uint8_t* base = reinterpret_cast<uint8_t*>(this);
return reinterpret_cast<uint32_t*>(base + bytecodeTypeMapOffset_);
}
inline StackTypeSet* thisTypes(const AutoSweepJitScript& sweep,
@ -188,22 +241,53 @@ class JitScript {
void destroy(Zone* zone);
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
// Note: icScript_ size is reported in jit::AddSizeOfBaselineData.
return mallocSizeOf(this);
}
static constexpr size_t offsetOfICScript() {
// Note: icScript_ is a UniquePtr that stores the raw pointer. If that ever
// changes and this assertion fails, we should stop using UniquePtr.
static_assert(sizeof(icScript_) == sizeof(uintptr_t),
"JIT code assumes icScript_ is pointer-sized");
return offsetof(JitScript, icScript_);
}
static constexpr size_t offsetOfICEntries() { return sizeof(JitScript); }
#ifdef DEBUG
void printTypes(JSContext* cx, HandleScript script);
#endif
void prepareForDestruction(Zone* zone) {
// When the script contains pointers to nursery things, the store buffer can
// contain entries that point into the fallback stub space. Since we can
// destroy scripts outside the context of a GC, this situation could result
// in us trying to mark invalid store buffer entries.
//
// Defer freeing any allocated blocks until after the next minor GC.
fallbackStubSpace_.freeAllAfterMinorGC(zone);
}
jit::FallbackICStubSpace* fallbackStubSpace() { return &fallbackStubSpace_; }
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t* data,
size_t* fallbackStubs) const {
*data += mallocSizeOf(this);
// |data| already includes the ICStubSpace itself, so use
// sizeOfExcludingThis.
*fallbackStubs += fallbackStubSpace_.sizeOfExcludingThis(mallocSizeOf);
}
jit::ICEntry& icEntry(size_t index) {
MOZ_ASSERT(index < numICEntries());
return icEntries()[index];
}
void noteAccessedGetter(uint32_t pcOffset);
void noteHasDenseAdd(uint32_t pcOffset);
void trace(JSTracer* trc);
void purgeOptimizedStubs(JSScript* script);
jit::ICEntry* interpreterICEntryFromPCOffset(uint32_t pcOffset);
jit::ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset);
jit::ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset,
jit::ICEntry* prevLookedUpEntry);
jit::ICEntry& icEntryFromPCOffset(uint32_t pcOffset);
jit::ICEntry& icEntryFromPCOffset(uint32_t pcOffset,
jit::ICEntry* prevLookedUpEntry);
};
// Ensures no JitScripts are purged in the current zone.

View File

@ -1553,8 +1553,8 @@ static MOZ_ALWAYS_INLINE bool SetObjectElementOperation(
int32_t i = JSID_TO_INT(id);
if ((uint32_t)i >= length) {
// Annotate script if provided with information (e.g. baseline)
if (script && script->hasICScript() && IsSetElemPC(pc)) {
script->icScript()->noteHasDenseAdd(script->pcToOffset(pc));
if (script && script->hasJitScript() && IsSetElemPC(pc)) {
script->jitScript()->noteHasDenseAdd(script->pcToOffset(pc));
}
}
}

View File

@ -182,9 +182,4 @@ inline bool JSScript::isDebuggee() const {
return realm_->debuggerObservesAllExecution() || hasDebugScript();
}
inline js::jit::ICScript* JSScript::icScript() const {
MOZ_ASSERT(hasICScript());
return jitScript_->icScript();
}
#endif /* vm_JSScript_inl_h */

View File

@ -1725,9 +1725,8 @@ class ScriptSource::LoadSourceMatcher {
}
if (!ss_->setRetrievedSource(
cx_,
EntryUnits<Utf8Unit>(reinterpret_cast<Utf8Unit*>(utf8Source)),
*length)) {
cx_, EntryUnits<Utf8Unit>(reinterpret_cast<Utf8Unit*>(utf8Source)),
*length)) {
return false;
}
@ -2748,8 +2747,8 @@ XDRResult ScriptSource::codeBinASTData(XDRState<mode>* const xdr,
// XDR the BinAST data.
Maybe<SharedImmutableString> binASTData;
if (mode == XDR_DECODE) {
auto bytes = xdr->cx()->template make_pod_array<char>(Max<size_t>(
binASTLength, 1));
auto bytes =
xdr->cx()->template make_pod_array<char>(Max<size_t>(binASTLength, 1));
if (!bytes) {
return xdr->fail(JS::TranscodeResult_Throw);
}
@ -2820,7 +2819,8 @@ XDRResult ScriptSource::codeBinASTData(XDRState<mode>* const xdr,
JSAtom** atomsBase = binASTMetadata->atomsBase();
auto slices = binASTMetadata->sliceBase();
const char* sourceBase =
(mode == XDR_ENCODE ? ss->data.as<BinAST>().string : *binASTData).chars();
(mode == XDR_ENCODE ? ss->data.as<BinAST>().string : *binASTData)
.chars();
for (uint32_t i = 0; i < numStrings; i++) {
uint8_t isNull;
@ -3373,7 +3373,8 @@ void js::FreeScriptData(JSRuntime* rt) {
#ifdef DEBUG
if (numLive > 0) {
fprintf(stderr, "ERROR: GC found %zu live SharedScriptData at shutdown\n", numLive);
fprintf(stderr, "ERROR: GC found %zu live SharedScriptData at shutdown\n",
numLive);
}
#endif
@ -4039,8 +4040,15 @@ size_t JSScript::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(data_);
}
size_t JSScript::sizeOfJitScript(mozilla::MallocSizeOf mallocSizeOf) const {
return jitScript_ ? jitScript_->sizeOfIncludingThis(mallocSizeOf) : 0;
void JSScript::addSizeOfJitScript(mozilla::MallocSizeOf mallocSizeOf,
size_t* sizeOfJitScript,
size_t* sizeOfBaselineFallbackStubs) const {
if (!jitScript_) {
return;
}
jitScript_->addSizeOfIncludingThis(mallocSizeOf, sizeOfJitScript,
sizeOfBaselineFallbackStubs);
}
js::GlobalObject& JSScript::uninlinedGlobal() const { return global(); }
@ -5511,14 +5519,16 @@ JS::ubi::Base::Size JS::ubi::Concrete<JSScript>::size(
Size size = gc::Arena::thingSize(get().asTenured().getAllocKind());
size += get().sizeOfData(mallocSizeOf);
size += get().sizeOfJitScript(mallocSizeOf);
size_t jitScriptSize = 0;
size_t fallbackStubSize = 0;
get().addSizeOfJitScript(mallocSizeOf, &jitScriptSize, &fallbackStubSize);
size += jitScriptSize;
size += fallbackStubSize;
size_t baselineSize = 0;
size_t baselineStubsSize = 0;
jit::AddSizeOfBaselineData(&get(), mallocSizeOf, &baselineSize,
&baselineStubsSize);
jit::AddSizeOfBaselineData(&get(), mallocSizeOf, &baselineSize);
size += baselineSize;
size += baselineStubsSize;
size += jit::SizeOfIonData(&get(), mallocSizeOf);

View File

@ -55,7 +55,6 @@ namespace js {
namespace jit {
struct BaselineScript;
class ICScript;
struct IonScriptCounts;
} // namespace jit
@ -1568,7 +1567,7 @@ class alignas(uintptr_t) SharedScriptData final {
// Index into the scopes array of the body scope.
uint32_t bodyScopeIndex = 0;
// Number of IC entries to allocate in ICScript for Baseline ICs.
// Number of IC entries to allocate in JitScript for Baseline ICs.
uint32_t numICEntries = 0;
// ES6 function length.
@ -2467,14 +2466,6 @@ class JSScript : public js::gc::TenuredCell {
inline void setBaselineScript(JSRuntime* rt,
js::jit::BaselineScript* baselineScript);
inline js::jit::ICScript* icScript() const;
bool hasICScript() const {
// ICScript is stored in JitScript so we have an ICScript iff we have a
// JitScript.
return !!jitScript_;
}
void updateJitCodeRaw(JSRuntime* rt);
static size_t offsetOfBaselineScript() {
@ -2604,6 +2595,7 @@ class JSScript : public js::gc::TenuredCell {
/* Ensure the script has a JitScript. */
inline bool ensureHasJitScript(JSContext* cx, js::AutoKeepJitScripts&);
bool hasJitScript() const { return jitScript_ != nullptr; }
js::JitScript* jitScript() { return jitScript_; }
void maybeReleaseJitScript();
@ -2734,7 +2726,10 @@ class JSScript : public js::gc::TenuredCell {
*/
size_t computedSizeOfData() const;
size_t sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const;
size_t sizeOfJitScript(mozilla::MallocSizeOf mallocSizeOf) const;
void addSizeOfJitScript(mozilla::MallocSizeOf mallocSizeOf,
size_t* sizeOfJitScript,
size_t* sizeOfBaselineFallbackStubs) const;
size_t dataSize() const { return dataSize_; }

View File

@ -445,10 +445,10 @@ static void StatsCellCallback(JSRuntime* rt, void* data, void* thing,
realmStats.scriptsGCHeap += thingSize;
realmStats.scriptsMallocHeapData +=
script->sizeOfData(rtStats->mallocSizeOf_);
realmStats.jitScripts += script->sizeOfJitScript(rtStats->mallocSizeOf_);
jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_,
&realmStats.baselineData,
script->addSizeOfJitScript(rtStats->mallocSizeOf_, &realmStats.jitScripts,
&realmStats.baselineStubsFallback);
jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_,
&realmStats.baselineData);
realmStats.ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_);
CollectScriptSourceStats<granularity>(closure, script->scriptSource());
break;

View File

@ -2248,12 +2248,12 @@ static MOZ_ALWAYS_INLINE bool GetExistingProperty(
{
jsbytecode* pc;
JSScript* script = cx->currentScript(&pc);
if (script && script->hasICScript()) {
if (script && script->hasJitScript()) {
switch (JSOp(*pc)) {
case JSOP_GETPROP:
case JSOP_CALLPROP:
case JSOP_LENGTH:
script->icScript()->noteAccessedGetter(script->pcToOffset(pc));
script->jitScript()->noteAccessedGetter(script->pcToOffset(pc));
break;
default:
break;
@ -3109,4 +3109,4 @@ bool js::CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target,
}
return true;
}
}

View File

@ -38,7 +38,6 @@ class HeapTypeSetKey;
namespace jit {
class ICScript;
struct IonScript;
class TempAllocator;