Bug 1400278 - Replace runtime allocation with system or zone allocation where possible r=jandem

This commit is contained in:
Jon Coppeard 2017-09-20 13:19:32 +01:00
parent d37d34fd39
commit 056acc6fae
25 changed files with 92 additions and 87 deletions

View File

@ -688,7 +688,7 @@ struct ZoneStats
js_delete(allStrings);
}
bool initStrings(JSRuntime* rt);
bool initStrings();
void addSizes(const ZoneStats& other) {
MOZ_ASSERT(isTotals);
@ -808,7 +808,7 @@ struct CompartmentStats
js_delete(allClasses);
}
bool initClasses(JSRuntime* rt);
bool initClasses();
void addSizes(const CompartmentStats& other) {
MOZ_ASSERT(isTotals);

View File

@ -252,7 +252,7 @@ struct Zone : public JS::shadow::Zone,
}
bool isCollecting() const {
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
return isCollectingFromAnyThread();
}
@ -435,7 +435,7 @@ struct Zone : public JS::shadow::Zone,
bool triggerGCForTooMuchMalloc() {
JSRuntime* rt = runtimeFromAnyThread();
if (CurrentThreadCanAccessRuntime(rt)) {
if (js::CurrentThreadCanAccessRuntime(rt)) {
return rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC,
gcMallocCounter.bytes(), gcMallocCounter.maxBytes());
}
@ -620,7 +620,7 @@ struct Zone : public JS::shadow::Zone,
void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) {
MOZ_ASSERT(src != tgt);
MOZ_ASSERT(!IsInsideNursery(tgt));
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
MOZ_ASSERT(!uniqueIds().has(tgt));
uniqueIds().rekeyIfMoved(src, tgt);
@ -659,6 +659,28 @@ struct Zone : public JS::shadow::Zone,
// Delete an empty compartment after its contents have been merged.
void deleteEmptyCompartment(JSCompartment* comp);
/*
* This variation of calloc will call the large-allocation-failure callback
* on OOM and retry the allocation.
*/
template <typename T>
T* pod_callocCanGC(size_t numElems) {
T* p = pod_calloc<T>(numElems);
if (MOZ_LIKELY(!!p))
return p;
size_t bytes;
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
reportAllocationOverflow();
return nullptr;
}
JSRuntime* rt = runtimeFromActiveCooperatingThread();
p = static_cast<T*>(rt->onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
if (!p)
return nullptr;
updateMallocCounter(bytes);
return p;
}
private:
js::ZoneGroupData<js::jit::JitZone*> jitZone_;

View File

@ -7912,7 +7912,7 @@ JitCompartment::generateStringConcatStub(JSContext* cx)
}
JitCode*
JitRuntime::generateMallocStub(JSContext* cx)
JitZone::generateMallocStub(JSContext* cx)
{
const Register regReturn = CallTempReg0;
const Register regNBytes = CallTempReg0;
@ -7928,12 +7928,12 @@ JitRuntime::generateMallocStub(JSContext* cx)
masm.PushRegsInMask(save);
const Register regTemp = regs.takeAnyGeneral();
const Register regRuntime = regTemp;
const Register regZone = regTemp;
MOZ_ASSERT(regTemp != regNBytes);
masm.setupUnalignedABICall(regTemp);
masm.movePtr(ImmPtr(cx->runtime()), regRuntime);
masm.passABIArg(regRuntime);
masm.movePtr(ImmPtr(cx->zone()), regZone);
masm.passABIArg(regZone);
masm.passABIArg(regNBytes);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, MallocWrapper));
masm.storeCallWordResult(regReturn);

View File

@ -141,6 +141,12 @@ CompileZone::runtime()
return CompileRuntime::get(zone()->runtimeFromAnyThread());
}
JitCode*
CompileZone::mallocStub()
{
return zone()->jitZone()->mallocStub();
}
bool
CompileZone::isAtomsZone()
{

View File

@ -68,6 +68,8 @@ class CompileZone
CompileRuntime* runtime();
bool isAtomsZone();
JitCode* mallocStub();
#ifdef DEBUG
const void* addressOfIonBailAfter();
#endif

View File

@ -320,11 +320,6 @@ JitRuntime::initialize(JSContext* cx, AutoLockForExclusiveAccess& lock)
if (!objectGroupPreBarrier_)
return false;
JitSpew(JitSpew_Codegen, "# Emitting malloc stub");
mallocStub_ = generateMallocStub(cx);
if (!mallocStub_)
return false;
JitSpew(JitSpew_Codegen, "# Emitting free stub");
freeStub_ = generateFreeStub(cx);
if (!freeStub_)
@ -470,6 +465,13 @@ JitZone::init(JSContext* cx)
return false;
}
JitSpew(JitSpew_Codegen, "# Emitting malloc stub");
mallocStub_ = generateMallocStub(cx);
if (!mallocStub_) {
ReportOutOfMemory(cx);
return false;
}
return true;
}

View File

@ -129,8 +129,7 @@ class JitRuntime
ExclusiveAccessLockWriteOnceData<JitCode*> shapePreBarrier_;
ExclusiveAccessLockWriteOnceData<JitCode*> objectGroupPreBarrier_;
// Thunk to call malloc/free.
ExclusiveAccessLockWriteOnceData<JitCode*> mallocStub_;
// Thunk to call free.
ExclusiveAccessLockWriteOnceData<JitCode*> freeStub_;
// Thunk called to finish compilation of an IonScript.
@ -165,7 +164,6 @@ class JitRuntime
JitCode* generateBailoutHandler(JSContext* cx);
JitCode* generateInvalidator(JSContext* cx);
JitCode* generatePreBarrier(JSContext* cx, MIRType type);
JitCode* generateMallocStub(JSContext* cx);
JitCode* generateFreeStub(JSContext* cx);
JitCode* generateDebugTrapHandler(JSContext* cx);
JitCode* generateBaselineDebugModeOSRHandler(JSContext* cx, uint32_t* noFrameRegPopOffsetOut);
@ -286,10 +284,6 @@ class JitRuntime
}
}
JitCode* mallocStub() const {
return mallocStub_;
}
JitCode* freeStub() const {
return freeStub_;
}
@ -420,6 +414,9 @@ class JitZone
IcStubCodeMapGCPolicy<CacheIRStubKey>>;
BaselineCacheIRStubCodeMap baselineCacheIRStubCodes_;
// Thunk to call malloc.
WriteOnceData<JitCode*> mallocStub_;
public:
MOZ_MUST_USE bool init(JSContext* cx);
void sweep(FreeOp* fop);
@ -473,6 +470,13 @@ class JitZone
void purgeIonCacheIRStubInfo() {
ionCacheIRStubInfoSet_.finish();
}
JitCode* mallocStub() const {
return mallocStub_;
}
private:
JitCode* generateMallocStub(JSContext* cx);
};
enum class BailoutReturnStub {

View File

@ -852,7 +852,7 @@ MacroAssembler::callMallocStub(size_t nbytes, Register result, Label* fail)
if (regNBytes != result)
push(regNBytes);
move32(Imm32(nbytes), regNBytes);
call(GetJitContext()->runtime->jitRuntime()->mallocStub());
call(GetJitContext()->compartment->zone()->mallocStub());
if (regNBytes != result) {
movePtr(regNBytes, result);
pop(regNBytes);

View File

@ -542,10 +542,10 @@ InterruptCheck(JSContext* cx)
}
void*
MallocWrapper(JSRuntime* rt, size_t nbytes)
MallocWrapper(JS::Zone* zone, size_t nbytes)
{
AutoUnsafeCallWithABI unsafe;
return rt->pod_malloc<uint8_t>(nbytes);
return zone->pod_malloc<uint8_t>(nbytes);
}
JSObject*

View File

@ -679,7 +679,7 @@ SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValu
MOZ_MUST_USE bool
InterruptCheck(JSContext* cx);
void* MallocWrapper(JSRuntime* rt, size_t nbytes);
void* MallocWrapper(JS::Zone* zone, size_t nbytes);
JSObject* NewCallObject(JSContext* cx, HandleShape shape, HandleObjectGroup group);
JSObject* NewSingletonCallObject(JSContext* cx, HandleShape shape);
JSObject* NewStringObject(JSContext* cx, HandleString str);

View File

@ -711,7 +711,7 @@ CodeGeneratorShared::createNativeToBytecodeScriptList(JSContext* cx)
}
// Allocate array for list.
JSScript** data = cx->runtime()->pod_malloc<JSScript*>(scriptList.length());
JSScript** data = cx->zone()->pod_malloc<JSScript*>(scriptList.length());
if (!data)
return false;
@ -758,7 +758,7 @@ CodeGeneratorShared::generateCompactNativeToBytecodeMap(JSContext* cx, JitCode*
MOZ_ASSERT(numRegions > 0);
// Writer is done, copy it to sized buffer.
uint8_t* data = cx->runtime()->pod_malloc<uint8_t>(writer.length());
uint8_t* data = cx->zone()->pod_malloc<uint8_t>(writer.length());
if (!data) {
js_free(nativeToBytecodeScriptList_);
return false;
@ -914,7 +914,7 @@ CodeGeneratorShared::generateCompactTrackedOptimizationsMap(JSContext* cx, JitCo
MOZ_ASSERT(attemptsTableOffset > typesTableOffset);
// Copy over the table out of the writer's buffer.
uint8_t* data = cx->runtime()->pod_malloc<uint8_t>(writer.length());
uint8_t* data = cx->zone()->pod_malloc<uint8_t>(writer.length());
if (!data)
return false;

View File

@ -1334,7 +1334,7 @@ JS_malloc(JSContext* cx, size_t nbytes)
{
AssertHeapIsIdle();
CHECK_REQUEST(cx);
return static_cast<void*>(cx->runtime()->pod_malloc<uint8_t>(nbytes));
return static_cast<void*>(cx->zone()->pod_malloc<uint8_t>(nbytes));
}
JS_PUBLIC_API(void*)

View File

@ -81,7 +81,7 @@ struct CommonNameInfo
bool
JSRuntime::initializeAtoms(JSContext* cx)
{
atoms_ = cx->new_<AtomSet>();
atoms_ = js_new<AtomSet>();
if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
return false;
@ -97,7 +97,7 @@ JSRuntime::initializeAtoms(JSContext* cx)
return true;
}
staticStrings = cx->new_<StaticStrings>();
staticStrings = js_new<StaticStrings>();
if (!staticStrings || !staticStrings->init(cx))
return false;
@ -116,7 +116,7 @@ JSRuntime::initializeAtoms(JSContext* cx)
#undef COMMON_NAME_INFO
};
commonNames = cx->new_<JSAtomState>();
commonNames = js_new<JSAtomState>();
if (!commonNames)
return false;
@ -132,7 +132,7 @@ JSRuntime::initializeAtoms(JSContext* cx)
emptyString = commonNames->empty;
// Create the well-known symbols.
wellKnownSymbols = cx->new_<WellKnownSymbols>();
wellKnownSymbols = js_new<WellKnownSymbols>();
if (!wellKnownSymbols)
return false;
@ -242,9 +242,9 @@ JSRuntime::transformToPermanentAtoms(JSContext* cx)
// of the atoms table into permanentAtoms and mark each as permanent.
MOZ_ASSERT(!permanentAtoms);
permanentAtoms = cx->new_<FrozenAtomSet>(atoms_); // takes ownership of atoms_
permanentAtoms = js_new<FrozenAtomSet>(atoms_); // takes ownership of atoms_
atoms_ = cx->new_<AtomSet>();
atoms_ = js_new<AtomSet>();
if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
return false;

View File

@ -1150,7 +1150,7 @@ js::UseInternalJobQueues(JSContext* cx)
MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),
"js::UseInternalJobQueues must be called early during runtime startup.");
MOZ_ASSERT(!cx->jobQueue);
auto* queue = cx->new_<PersistentRooted<JobQueue>>(cx, JobQueue(SystemAllocPolicy()));
auto* queue = js_new<PersistentRooted<JobQueue>>(cx, JobQueue(SystemAllocPolicy()));
if (!queue)
return false;

View File

@ -143,7 +143,6 @@ struct JSContext : public JS::RootingContext,
void recoverFromOutOfMemory();
inline void updateMallocCounter(size_t nbytes) {
// Note: this is racy.
runtime()->updateMallocCounter(zone(), nbytes);
}

View File

@ -662,7 +662,7 @@ struct JSCompartment
const JS::CompartmentBehaviors& behaviors() const { return behaviors_; }
JSRuntime* runtimeFromActiveCooperatingThread() const {
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
return runtime_;
}

View File

@ -2976,7 +2976,7 @@ js::WatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id, JS::Hand
WatchpointMap* wpmap = cx->compartment()->watchpointMap;
if (!wpmap) {
wpmap = cx->runtime()->new_<WatchpointMap>();
wpmap = cx->zone()->new_<WatchpointMap>();
if (!wpmap || !wpmap->init()) {
ReportOutOfMemory(cx);
js_delete(wpmap);

View File

@ -3912,7 +3912,7 @@ JSScript::getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc)
BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
if (!site) {
site = cx->runtime()->new_<JSBreakpointSite>(this, pc);
site = cx->zone()->new_<JSBreakpointSite>(this, pc);
if (!site) {
ReportOutOfMemory(cx);
return nullptr;

View File

@ -304,7 +304,7 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
static ArrayBufferObject::BufferContents
AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes)
{
uint8_t* p = cx->runtime()->pod_callocCanGC<uint8_t>(nbytes);
uint8_t* p = cx->zone()->pod_callocCanGC<uint8_t>(nbytes);
if (!p)
ReportOutOfMemory(cx);

View File

@ -6527,7 +6527,7 @@ struct DebuggerScriptSetBreakpointMatcher
if (!site)
return false;
site->inc(cx_->runtime()->defaultFreeOp());
if (cx_->runtime()->new_<Breakpoint>(dbg_, site, handler_))
if (cx_->zone()->new_<Breakpoint>(dbg_, site, handler_))
return true;
site->dec(cx_->runtime()->defaultFreeOp());
site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());
@ -6544,7 +6544,7 @@ struct DebuggerScriptSetBreakpointMatcher
if (!site)
return false;
site->inc(cx_->runtime()->defaultFreeOp());
if (cx_->runtime()->new_<WasmBreakpoint>(dbg_, site, handler_, instance.object()))
if (cx_->zone()->new_<WasmBreakpoint>(dbg_, site, handler_, instance.object()))
return true;
site->dec(cx_->runtime()->defaultFreeOp());
site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());
@ -11651,7 +11651,7 @@ namespace dbg {
/* static */ GarbageCollectionEvent::Ptr
GarbageCollectionEvent::Create(JSRuntime* rt, ::js::gcstats::Statistics& stats, uint64_t gcNumber)
{
auto data = rt->make_unique<GarbageCollectionEvent>(gcNumber);
auto data = MakeUnique<GarbageCollectionEvent>(gcNumber);
if (!data)
return nullptr;

View File

@ -316,7 +316,7 @@ StatsZoneCallback(JSRuntime* rt, void* data, Zone* zone)
// CollectRuntimeStats reserves enough space.
MOZ_ALWAYS_TRUE(rtStats->zoneStatsVector.growBy(1));
ZoneStats& zStats = rtStats->zoneStatsVector.back();
if (!zStats.initStrings(rt))
if (!zStats.initStrings())
MOZ_CRASH("oom");
rtStats->initExtraZoneStats(zone, &zStats);
rtStats->currZoneStats = &zStats;
@ -341,7 +341,7 @@ StatsCompartmentCallback(JSContext* cx, void* data, JSCompartment* compartment)
// CollectRuntimeStats reserves enough space.
MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
CompartmentStats& cStats = rtStats->compartmentStatsVector.back();
if (!cStats.initClasses(cx->runtime()))
if (!cStats.initClasses())
MOZ_CRASH("oom");
rtStats->initExtraCompartmentStats(compartment, &cStats);
@ -620,10 +620,10 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKin
}
bool
ZoneStats::initStrings(JSRuntime* rt)
ZoneStats::initStrings()
{
isTotals = false;
allStrings = rt->new_<StringsHashMap>();
allStrings = js_new<StringsHashMap>();
if (!allStrings || !allStrings->init()) {
js_delete(allStrings);
allStrings = nullptr;
@ -633,10 +633,10 @@ ZoneStats::initStrings(JSRuntime* rt)
}
bool
CompartmentStats::initClasses(JSRuntime* rt)
CompartmentStats::initClasses()
{
isTotals = false;
allClasses = rt->new_<ClassesHashMap>();
allClasses = js_new<ClassesHashMap>();
if (!allClasses || !allClasses->init()) {
js_delete(allClasses);
allClasses = nullptr;

View File

@ -218,12 +218,12 @@ JSRuntime::init(JSContext* cx, uint32_t maxbytes, uint32_t maxNurseryBytes)
if (!gc.init(maxbytes, maxNurseryBytes))
return false;
ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this, nullptr));
ScopedJSDeletePtr<Zone> atomsZone(js_new<Zone>(this, nullptr));
if (!atomsZone || !atomsZone->init(true))
return false;
JS::CompartmentOptions options;
ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options));
ScopedJSDeletePtr<JSCompartment> atomsCompartment(js_new<JSCompartment>(atomsZone.get(), options));
if (!atomsCompartment || !atomsCompartment->init(nullptr))
return false;

View File

@ -48,7 +48,6 @@
#include "vm/CommonPropertyNames.h"
#include "vm/DateTime.h"
#include "vm/GeckoProfiler.h"
#include "vm/MallocProvider.h"
#include "vm/Scope.h"
#include "vm/SharedImmutableStringsCache.h"
#include "vm/Stack.h"
@ -981,6 +980,8 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
JS_FRIEND_API(void*) onOutOfMemoryCanGC(js::AllocFunction allocator, size_t nbytes,
void* reallocPtr = nullptr);
static const unsigned LARGE_ALLOCATION = 25 * 1024 * 1024;
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* runtime);
private:
@ -1016,38 +1017,6 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
js::ActiveThreadData<JS::OutOfMemoryCallback> oomCallback;
js::ActiveThreadData<void*> oomCallbackData;
/*
* These variations of malloc/calloc/realloc will call the
* large-allocation-failure callback on OOM and retry the allocation.
*/
static const unsigned LARGE_ALLOCATION = 25 * 1024 * 1024;
template <typename T>
T* pod_callocCanGC(size_t numElems) {
T* p = pod_calloc<T>(numElems);
if (MOZ_LIKELY(!!p))
return p;
size_t bytes;
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
reportAllocationOverflow();
return nullptr;
}
return static_cast<T*>(onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
}
template <typename T>
T* pod_reallocCanGC(T* p, size_t oldSize, size_t newSize) {
T* p2 = pod_realloc<T>(p, oldSize, newSize);
if (MOZ_LIKELY(!!p2))
return p2;
size_t bytes;
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes))) {
reportAllocationOverflow();
return nullptr;
}
return static_cast<T*>(onOutOfMemoryCanGC(js::AllocFunction::Realloc, bytes, p));
}
/*
* Debugger.Memory functions like takeCensus use this embedding-provided
* function to assess the size of malloc'd blocks of memory.

View File

@ -64,7 +64,8 @@ bool
JS::WeakMapPtr<K, V>::init(JSContext* cx)
{
MOZ_ASSERT(!initialized());
typename details::Utils<K, V>::PtrType map = cx->runtime()->new_<typename details::Utils<K,V>::Type>(cx);
typename details::Utils<K, V>::PtrType map =
cx->zone()->new_<typename details::Utils<K,V>::Type>(cx);
if (!map || !map->init())
return false;
ptr = map;

View File

@ -417,7 +417,7 @@ DebugState::getOrCreateBreakpointSite(JSContext* cx, uint32_t offset)
WasmBreakpointSiteMap::AddPtr p = breakpointSites_.lookupForAdd(offset);
if (!p) {
site = cx->runtime()->new_<WasmBreakpointSite>(this, offset);
site = cx->zone()->new_<WasmBreakpointSite>(this, offset);
if (!site || !breakpointSites_.add(p, offset, site)) {
js_delete(site);
ReportOutOfMemory(cx);