Bug 1288715 - Refactor SharedScriptData to prepare for making it refcounted r=till

This commit is contained in:
Jon Coppeard 2016-08-08 10:13:47 +01:00
Родитель c3fe802818
Коммит 38ebe42aaa
2 изменённых файлов: 198 добавлений и 163 удалений

Просмотреть файл

@ -586,9 +586,6 @@ FindScopeObjectIndex(JSScript* script, NestedStaticScope& scope)
MOZ_CRASH("Scope not found");
}
static bool
SaveSharedScriptData(ExclusiveContext*, Handle<JSScript*>, SharedScriptData*, uint32_t);
enum XDRClassKind {
CK_BlockObject = 0,
CK_WithObject = 1,
@ -880,7 +877,6 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript
MOZ_ASSERT(!script->mainOffset());
script->mainOffset_ = prologueLength;
script->setLength(length);
script->funLength_ = funLength;
scriptp.set(script);
@ -954,22 +950,18 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript
script->nslots_ = nslots;
}
jsbytecode* code = script->code();
SharedScriptData* ssd;
auto scriptDataGuard = mozilla::MakeScopeExit([&] {
if (mode == XDR_DECODE)
script->freeScriptData();
});
if (mode == XDR_DECODE) {
ssd = SharedScriptData::new_(cx, length, nsrcnotes, natoms);
if (!ssd)
if (!script->createScriptData(cx, length, nsrcnotes, natoms))
return false;
code = ssd->data;
if (natoms != 0) {
script->natoms_ = natoms;
script->atoms = ssd->atoms();
}
}
jsbytecode* code = script->code();
if (!xdr->codeBytes(code, length) || !xdr->codeBytes(code + length, nsrcnotes)) {
if (mode == XDR_DECODE)
js_free(ssd);
return false;
}
@ -978,16 +970,17 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript
RootedAtom tmp(cx);
if (!XDRAtom(xdr, &tmp))
return false;
script->atoms[i].init(tmp);
script->atoms()[i].init(tmp);
} else {
RootedAtom tmp(cx, script->atoms[i]);
RootedAtom tmp(cx, script->atoms()[i]);
if (!XDRAtom(xdr, &tmp))
return false;
}
}
scriptDataGuard.release();
if (mode == XDR_DECODE) {
if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes))
if (!script->shareScriptData(cx))
return false;
}
@ -2416,53 +2409,69 @@ SharedScriptData*
js::SharedScriptData::new_(ExclusiveContext* cx, uint32_t codeLength,
uint32_t srcnotesLength, uint32_t natoms)
{
/*
* Ensure the atoms are aligned, as some architectures don't allow unaligned
* access.
*/
const uint32_t pointerSize = sizeof(JSAtom*);
const uint32_t pointerMask = pointerSize - 1;
const uint32_t dataOffset = offsetof(SharedScriptData, data);
uint32_t baseLength = codeLength + srcnotesLength;
uint32_t padding = (pointerSize - ((baseLength + dataOffset) & pointerMask)) & pointerMask;
uint32_t length = baseLength + padding + pointerSize * natoms;
SharedScriptData* entry = reinterpret_cast<SharedScriptData*>(
cx->zone()->pod_malloc<uint8_t>(length + dataOffset));
uint32_t dataLength = natoms * sizeof(GCPtrAtom) + codeLength + srcnotesLength;
uint32_t allocLength = offsetof(SharedScriptData, data_) + dataLength;
auto entry = reinterpret_cast<SharedScriptData*>(cx->zone()->pod_malloc<uint8_t>(allocLength));
if (!entry) {
ReportOutOfMemory(cx);
return nullptr;
}
entry->length = length;
entry->natoms = natoms;
entry->marked = false;
memset(entry->data + baseLength, 0, padding);
entry->dataLength_ = dataLength;
entry->natoms_ = natoms;
entry->codeLength_ = codeLength;
entry->marked_ = false;
/*
* Call constructors to initialize the storage that will be accessed as a
* GCPtrAtom array via atoms().
*/
GCPtrAtom* atoms = entry->atoms();
MOZ_ASSERT(reinterpret_cast<uintptr_t>(atoms) % sizeof(JSAtom*) == 0);
MOZ_ASSERT(reinterpret_cast<uintptr_t>(atoms) % sizeof(GCPtrAtom*) == 0);
for (unsigned i = 0; i < natoms; ++i)
new (&atoms[i]) GCPtrAtom();
return entry;
}
bool
JSScript::createScriptData(ExclusiveContext* cx, uint32_t codeLength, uint32_t srcnotesLength,
uint32_t natoms)
{
MOZ_ASSERT(!scriptData());
SharedScriptData* ssd = SharedScriptData::new_(cx, codeLength, srcnotesLength, natoms);
if (!ssd)
return false;
setScriptData(ssd);
return true;
}
void
JSScript::freeScriptData()
{
js_free(scriptData_);
scriptData_ = nullptr;
}
void
JSScript::setScriptData(js::SharedScriptData* data)
{
MOZ_ASSERT(!scriptData_);
scriptData_ = data;
}
/*
* Takes ownership of its *ssd parameter and either adds it into the runtime's
* ScriptDataTable or frees it if a matching entry already exists.
*
* Sets the |code| and |atoms| fields on the given JSScript.
*/
static bool
SaveSharedScriptData(ExclusiveContext* cx, Handle<JSScript*> script, SharedScriptData* ssd,
uint32_t nsrcnotes)
bool
JSScript::shareScriptData(ExclusiveContext* cx)
{
MOZ_ASSERT(script != nullptr);
MOZ_ASSERT(ssd != nullptr);
SharedScriptData* ssd = scriptData();
MOZ_ASSERT(ssd);
AutoLockForExclusiveAccess lock(cx);
@ -2470,13 +2479,12 @@ SaveSharedScriptData(ExclusiveContext* cx, Handle<JSScript*> script, SharedScrip
ScriptDataTable::AddPtr p = cx->scriptDataTable(lock).lookupForAdd(l);
if (p) {
js_free(ssd);
ssd = *p;
MOZ_ASSERT(ssd != *p);
freeScriptData();
setScriptData(*p);
} else {
if (!cx->scriptDataTable(lock).add(p, ssd)) {
script->setCode(nullptr);
script->atoms = nullptr;
js_free(ssd);
freeScriptData();
ReportOutOfMemory(cx);
return false;
}
@ -2491,35 +2499,19 @@ SaveSharedScriptData(ExclusiveContext* cx, Handle<JSScript*> script, SharedScrip
if (cx->isJSContext()) {
JSContext* ncx = cx->asJSContext();
if (JS::IsIncrementalGCInProgress(ncx) && ncx->gc.isFullGc())
ssd->marked = true;
scriptData()->setMarked(true);
}
script->setCode(ssd->data);
script->atoms = ssd->atoms();
return true;
}
static inline void
MarkScriptData(JSRuntime* rt, const jsbytecode* bytecode)
{
/*
* As an invariant, a ScriptBytecodeEntry should not be 'marked' outside of
* a GC. Since SweepScriptBytecodes is only called during a full gc,
* to preserve this invariant, only mark during a full gc.
*/
if (rt->gc.isFullGc())
SharedScriptData::fromBytecode(bytecode)->marked = true;
}
void
js::UnmarkScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock)
{
MOZ_ASSERT(rt->gc.isFullGc());
ScriptDataTable& table = rt->scriptDataTable(lock);
for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) {
SharedScriptData* entry = e.front();
entry->marked = false;
}
for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront())
e.front()->setMarked(false);
}
void
@ -2533,7 +2525,7 @@ js::SweepScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock)
for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) {
SharedScriptData* entry = e.front();
if (!entry->marked) {
if (!entry->marked()) {
js_free(entry);
e.removeFront();
}
@ -2833,14 +2825,13 @@ JSScript::fullyInitTrivial(ExclusiveContext* cx, Handle<JSScript*> script)
if (!partiallyInit(cx, script, 0, 0, 0, 0, 0, 0))
return false;
SharedScriptData* ssd = SharedScriptData::new_(cx, 1, 1, 0);
if (!ssd)
if (!script->createScriptData(cx, 1, 1, 0))
return false;
ssd->data[0] = JSOP_RETRVAL;
ssd->data[1] = SRC_NULL;
script->setLength(1);
return SaveSharedScriptData(cx, script, ssd, 1);
jsbytecode* code = script->code();
code[0] = JSOP_RETRVAL;
code[1] = SRC_NULL;
return script->shareScriptData(cx);
}
/* static */ void
@ -2926,19 +2917,16 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
script->lineno_ = bce->firstLine;
script->setLength(prologueLength + mainLength);
script->natoms_ = natoms;
SharedScriptData* ssd = SharedScriptData::new_(cx, script->length(), nsrcnotes, natoms);
if (!ssd)
if (!script->createScriptData(cx, prologueLength + mainLength, nsrcnotes, natoms))
return false;
jsbytecode* code = ssd->data;
jsbytecode* code = script->code();
PodCopy<jsbytecode>(code, bce->prologue.code.begin(), prologueLength);
PodCopy<jsbytecode>(code + prologueLength, bce->main.code.begin(), mainLength);
bce->copySrcNotes((jssrcnote*)(code + script->length()), nsrcnotes);
InitAtomMap(bce->atomIndices.getMap(), ssd->atoms());
InitAtomMap(bce->atomIndices.getMap(), script->atoms());
if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes))
if (!script->shareScriptData(cx))
return false;
if (bce->constList.length() != 0)
@ -3562,13 +3550,10 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri
memcpy(dst->data, src->data, size);
/* Script filenames, bytecodes and atoms are runtime-wide. */
dst->setCode(src->code());
dst->atoms = src->atoms;
dst->setScriptData(src->scriptData());
dst->setLength(src->length());
dst->lineno_ = src->lineno();
dst->mainOffset_ = src->mainOffset();
dst->natoms_ = src->natoms();
dst->funLength_ = src->funLength();
dst->nTypeSets_ = src->nTypeSets();
dst->nslots_ = src->nslots();
@ -3914,6 +3899,21 @@ JSScript::hasBreakpointsAt(jsbytecode* pc)
return site->enabledCount > 0;
}
void
SharedScriptData::traceChildren(JSTracer* trc)
{
for (uint32_t i = 0; i < natoms(); ++i)
TraceNullableEdge(trc, &atoms()[i], "atom");
/*
* As an invariant, a ScriptBytecodeEntry should not be 'marked' outside of
* a GC. Since SweepScriptBytecodes is only called during a full gc,
* to preserve this invariant, only mark during a full gc.
*/
if (trc->isMarkingTracer() && trc->runtime()->gc.isFullGc())
setMarked(true);
}
void
JSScript::traceChildren(JSTracer* trc)
{
@ -3926,10 +3926,8 @@ JSScript::traceChildren(JSTracer* trc)
static_cast<GCMarker*>(trc)->shouldCheckCompartments(),
zone()->isCollecting());
if (atoms) {
for (uint32_t i = 0; i < natoms(); ++i)
TraceNullableEdge(trc, &atoms[i], "atom");
}
if (scriptData())
scriptData()->traceChildren(trc);
if (hasObjects()) {
ObjectArray* objarray = objects();
@ -3952,13 +3950,9 @@ JSScript::traceChildren(JSTracer* trc)
if (maybeLazyScript())
TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
if (trc->isMarkingTracer()) {
if (trc->isMarkingTracer())
compartment()->mark();
if (code())
MarkScriptData(trc->runtime(), code());
}
bindings.trace(trc);
jit::TraceJitScripts(trc, this);

Просмотреть файл

@ -934,6 +934,89 @@ template<XDRMode mode>
bool
XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp);
/*
* Common data that can be shared between many scripts in a single runtime.
*/
class SharedScriptData
{
uint32_t dataLength_;
uint32_t natoms_;
uint32_t codeLength_;
mozilla::Atomic<bool, mozilla::ReleaseAcquire> marked_;
uintptr_t data_[1];
public:
static SharedScriptData* new_(ExclusiveContext* cx, uint32_t codeLength,
uint32_t srcnotesLength, uint32_t natoms);
uint32_t dataLength() const {
return dataLength_;
}
uint8_t* data() {
return reinterpret_cast<uint8_t*>(data_);
}
uint32_t natoms() const {
return natoms_;
}
GCPtrAtom* atoms() {
if (!natoms_)
return nullptr;
return reinterpret_cast<GCPtrAtom*>(data());
}
uint32_t codeLength() const {
return codeLength_;
}
jsbytecode* code() {
return reinterpret_cast<jsbytecode*>(data() + natoms_ * sizeof(GCPtrAtom));
}
bool marked() const {
return marked_;
}
void setMarked(bool marked) {
marked_ = marked;
}
void traceChildren(JSTracer* trc);
private:
SharedScriptData() = delete;
SharedScriptData(const SharedScriptData&) = delete;
SharedScriptData& operator=(const SharedScriptData&) = delete;
};
struct ScriptBytecodeHasher
{
struct Lookup
{
const uint8_t* data;
uint32_t length;
explicit Lookup(SharedScriptData* ssd) : data(ssd->data()), length(ssd->dataLength()) {}
};
static HashNumber hash(const Lookup& l) { return mozilla::HashBytes(l.data, l.length); }
static bool match(SharedScriptData* entry, const Lookup& lookup) {
if (entry->dataLength() != lookup.length)
return false;
return mozilla::PodEqual<uint8_t>(entry->data(), lookup.data, lookup.length);
}
};
typedef HashSet<SharedScriptData*,
ScriptBytecodeHasher,
SystemAllocPolicy> ScriptDataTable;
extern void
UnmarkScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock);
extern void
SweepScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock);
extern void
FreeScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock);
} /* namespace js */
class JSScript : public js::gc::TenuredCell
@ -980,13 +1063,11 @@ class JSScript : public js::gc::TenuredCell
// Word-sized fields.
private:
jsbytecode* code_; /* bytecodes and their immediate operands */
js::SharedScriptData* scriptData_;
public:
uint8_t* data; /* pointer to variable-length data array (see
comment above Create() for details) */
js::GCPtrAtom* atoms; /* maps immediate index to literal struct */
JSCompartment* compartment_;
private:
@ -1026,7 +1107,6 @@ class JSScript : public js::gc::TenuredCell
// 32-bit fields.
uint32_t length_; /* length of code vector */
uint32_t dataSize_; /* size of the used part of the data array */
uint32_t lineno_; /* base line number of script */
@ -1035,7 +1115,6 @@ class JSScript : public js::gc::TenuredCell
uint32_t mainOffset_;/* offset of main entry point from code, after
predef'ing prologue */
uint32_t natoms_; /* length of atoms array */
uint32_t nslots_; /* vars plus maximum stack depth */
/* Range of characters in scriptSource which contains this script's source. */
@ -1200,7 +1279,7 @@ class JSScript : public js::gc::TenuredCell
// instead of private to suppress -Wunused-private-field compiler warnings.
protected:
#if JS_BITS_PER_WORD == 32
// No padding currently required.
uint32_t padding_;
#endif
//
@ -1248,16 +1327,20 @@ class JSScript : public js::gc::TenuredCell
void setVersion(JSVersion v) { version = v; }
// Script bytecode is immutable after creation.
jsbytecode* code() const {
return code_;
}
size_t length() const {
return length_;
js::SharedScriptData* scriptData() {
return scriptData_;
}
void setCode(jsbytecode* code) { code_ = code; }
void setLength(size_t length) { length_ = length; }
// Script bytecode is immutable after creation.
jsbytecode* code() const {
if (!scriptData_)
return nullptr;
return scriptData_->code();
}
size_t length() const {
MOZ_ASSERT(scriptData_);
return scriptData_->codeLength();
}
jsbytecode* codeEnd() const { return code() + length(); }
@ -1714,6 +1797,12 @@ class JSScript : public js::gc::TenuredCell
private:
bool makeTypes(JSContext* cx);
bool createScriptData(js::ExclusiveContext* cx, uint32_t codeLength, uint32_t srcnotesLength,
uint32_t natoms);
bool shareScriptData(js::ExclusiveContext* cx);
void freeScriptData();
void setScriptData(js::SharedScriptData* data);
public:
uint32_t getWarmUpCount() const { return warmUpCount; }
uint32_t incWarmUpCounter(uint32_t amount = 1) { return warmUpCount += amount; }
@ -1807,11 +1896,18 @@ class JSScript : public js::gc::TenuredCell
bool hasLoops();
size_t natoms() const { return natoms_; }
size_t natoms() const {
MOZ_ASSERT(scriptData_);
return scriptData_->natoms();
}
js::GCPtrAtom* atoms() const {
MOZ_ASSERT(scriptData_);
return scriptData_->atoms();
}
js::GCPtrAtom& getAtom(size_t index) const {
MOZ_ASSERT(index < natoms());
return atoms[index];
return atoms()[index];
}
js::GCPtrAtom& getAtom(jsbytecode* pc) const {
@ -2411,61 +2507,6 @@ class LazyScript : public gc::TenuredCell
/* If this fails, add/remove padding within LazyScript. */
JS_STATIC_ASSERT(sizeof(LazyScript) % js::gc::CellSize == 0);
struct SharedScriptData
{
uint32_t length;
uint32_t natoms;
mozilla::Atomic<bool, mozilla::ReleaseAcquire> marked;
jsbytecode data[1];
static SharedScriptData* new_(ExclusiveContext* cx, uint32_t codeLength,
uint32_t srcnotesLength, uint32_t natoms);
GCPtrAtom* atoms() {
if (!natoms)
return nullptr;
return reinterpret_cast<GCPtrAtom*>(data + length - sizeof(JSAtom*) * natoms);
}
static SharedScriptData* fromBytecode(const jsbytecode* bytecode) {
return (SharedScriptData*)(bytecode - offsetof(SharedScriptData, data));
}
private:
SharedScriptData() = delete;
SharedScriptData(const SharedScriptData&) = delete;
};
struct ScriptBytecodeHasher
{
struct Lookup
{
jsbytecode* code;
uint32_t length;
explicit Lookup(SharedScriptData* ssd) : code(ssd->data), length(ssd->length) {}
};
static HashNumber hash(const Lookup& l) { return mozilla::HashBytes(l.code, l.length); }
static bool match(SharedScriptData* entry, const Lookup& lookup) {
if (entry->length != lookup.length)
return false;
return mozilla::PodEqual<jsbytecode>(entry->data, lookup.code, lookup.length);
}
};
typedef HashSet<SharedScriptData*,
ScriptBytecodeHasher,
SystemAllocPolicy> ScriptDataTable;
extern void
UnmarkScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock);
extern void
SweepScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock);
extern void
FreeScriptData(JSRuntime* rt, AutoLockForExclusiveAccess& lock);
struct ScriptAndCounts
{
/* This structure is stored and marked from the JSRuntime. */