From b7a2ff75e72d2db85f84604561b1a40576e75e5b Mon Sep 17 00:00:00 2001 From: Chris Leary Date: Thu, 22 Sep 2011 13:22:30 -0700 Subject: [PATCH] Bug 684039: rewrite JS LIFO allocator, avoids thrashing. (r=luke) --HG-- rename : js/src/jsarena.h => js/src/ds/LifoAlloc.h --- caps/src/nsScriptSecurityManager.cpp | 1 - js/src/Makefile.in | 9 +- js/src/ds/LifoAlloc.cpp | 149 ++++++++++++ js/src/ds/LifoAlloc.h | 339 +++++++++++++++++++++++++++ js/src/frontend/ParseMaps.cpp | 6 +- js/src/jsanalyze.cpp | 60 +++-- js/src/jsanalyze.h | 21 +- js/src/jsapi.cpp | 10 +- js/src/jsarena.cpp | 258 -------------------- js/src/jsarena.h | 317 ------------------------- js/src/jscntxt.cpp | 27 +-- js/src/jscntxt.h | 38 +-- js/src/jscntxtinlines.h | 6 + js/src/jscompartment.cpp | 13 +- js/src/jscompartment.h | 3 +- js/src/jsdbgapi.cpp | 11 +- js/src/jsemit.cpp | 13 +- js/src/jsemit.h | 8 +- js/src/jsfun.cpp | 4 +- js/src/jsinfer.cpp | 101 ++++---- js/src/jsinfer.h | 15 +- js/src/jsinferinlines.h | 6 +- js/src/jsinterp.cpp | 1 - js/src/jsiter.cpp | 1 - js/src/jsobj.cpp | 1 - js/src/json.cpp | 1 - js/src/jsopcode.cpp | 98 +++----- js/src/jsopcode.h | 3 +- js/src/jsparse.cpp | 16 +- js/src/jsprvtd.h | 24 ++ js/src/jsregexpinlines.h | 29 +-- js/src/jsscan.cpp | 1 - js/src/jsscope.cpp | 1 - js/src/jsscript.cpp | 13 +- js/src/jsscript.h | 2 - js/src/jstl.h | 3 +- js/src/jstracer.cpp | 10 +- js/src/jsutil.h | 2 + js/src/methodjit/Compiler.cpp | 7 +- js/src/methodjit/FrameState.cpp | 4 +- js/src/methodjit/LoopState.cpp | 2 +- js/src/shell/js.cpp | 36 +-- mfbt/Util.h | 1 + 43 files changed, 726 insertions(+), 945 deletions(-) create mode 100644 js/src/ds/LifoAlloc.cpp create mode 100644 js/src/ds/LifoAlloc.h delete mode 100644 js/src/jsarena.cpp delete mode 100644 js/src/jsarena.h diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index 461545a8b20..70121998a0c 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -59,7 +59,6 @@ #include "nsDOMError.h" #include "nsDOMCID.h" #include "jsdbgapi.h" -#include "jsarena.h" #include "jsfun.h" #include "jsobj.h" #include "nsIXPConnect.h" diff --git a/js/src/Makefile.in b/js/src/Makefile.in index b0fd3b5dfdc..c595fa33607 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -101,7 +101,6 @@ CPPSRCS = \ jsalloc.cpp \ jsanalyze.cpp \ jsapi.cpp \ - jsarena.cpp \ jsarray.cpp \ jsatom.cpp \ jsbool.cpp \ @@ -161,6 +160,7 @@ CPPSRCS = \ Stack.cpp \ String.cpp \ ParseMaps.cpp \ + LifoAlloc.cpp \ Unicode.cpp \ $(NULL) @@ -174,7 +174,6 @@ INSTALLED_HEADERS = \ js.msg \ jsalloc.h \ jsapi.h \ - jsarena.h \ jsatom.h \ jsbit.h \ jsclass.h \ @@ -241,9 +240,10 @@ INSTALLED_HEADERS = \ VPATH += \ $(srcdir)/vm \ $(srcdir)/frontend \ + $(srcdir)/ds \ $(NULL) -EXPORTS_NAMESPACES = vm +EXPORTS_NAMESPACES = vm ds EXPORTS_vm = \ ArgumentsObject.h \ @@ -255,6 +255,9 @@ EXPORTS_vm = \ Unicode.h \ $(NULL) +EXPORTS_ds = \ + LifoAlloc.h + ############################################### # BEGIN include sources for low-level code shared with Gecko # diff --git a/js/src/ds/LifoAlloc.cpp b/js/src/ds/LifoAlloc.cpp new file mode 100644 index 00000000000..94b8442d20d --- /dev/null +++ b/js/src/ds/LifoAlloc.cpp @@ -0,0 +1,149 @@ +#include "LifoAlloc.h" + +#include + +using namespace js; + +namespace js { +namespace detail { + +BumpChunk * +BumpChunk::new_(size_t chunkSize) +{ + JS_ASSERT(RoundUpPow2(chunkSize) == chunkSize); + void *mem = js_malloc(chunkSize); + if (!mem) + return NULL; + BumpChunk *result = new (mem) BumpChunk(chunkSize - sizeof(BumpChunk)); + + /* + * We assume that the alignment of sAlign is less than that of + * the underlying memory allocator -- creating a new BumpChunk should + * always satisfy the sAlign alignment constraint. + */ + JS_ASSERT(AlignPtr(result->bump) == result->bump); + return result; +} + +void * +BumpChunk::tryAllocUnaligned(size_t n) +{ + char *oldBump = bump; + char *newBump = bump + n; + if (newBump > limit) + return NULL; + + setBump(newBump); + return oldBump; +} + +} /* namespace detail */ +} /* namespace js */ + +void +LifoAlloc::freeAll() +{ + while (first) { + BumpChunk *victim = first; + first = first->next(); + BumpChunk::delete_(victim); + } + first = latest = NULL; +} + +void +LifoAlloc::freeUnused() +{ + /* Don't free anything if we have outstanding marks. */ + if (markCount || !first) + return; + + JS_ASSERT(first && latest); + + /* Rewind through any unused chunks. */ + if (!latest->used()) { + BumpChunk *lastUsed = NULL; + for (BumpChunk *it = first; it != latest; it = it->next()) { + if (it->used()) + lastUsed = it; + } + if (!lastUsed) { + freeAll(); + return; + } + latest = lastUsed; + } + + /* Free all chunks after |latest|. */ + size_t freed = 0; + for (BumpChunk *victim = latest->next(); victim; victim = victim->next()) { + BumpChunk::delete_(victim); + freed++; + } +} + +LifoAlloc::BumpChunk * +LifoAlloc::getOrCreateChunk(size_t n) +{ + if (first) { + /* Look for existing, unused BumpChunks to satisfy the request. */ + while (latest->next()) { + latest = latest->next(); + latest->resetBump(); /* This was an unused BumpChunk on the chain. */ + if (latest->canAlloc(n)) + return latest; + } + } + + size_t defaultChunkFreeSpace = defaultChunkSize_ - sizeof(BumpChunk); + size_t chunkSize = n > defaultChunkFreeSpace + ? RoundUpPow2(n + sizeof(BumpChunk)) + : defaultChunkSize_; + + /* If we get here, we couldn't find an existing BumpChunk to fill the request. */ + BumpChunk *newChunk = BumpChunk::new_(chunkSize); + if (!newChunk) + return NULL; + if (!first) { + latest = first = newChunk; + } else { + JS_ASSERT(latest && !latest->next()); + latest->setNext(newChunk); + latest = newChunk; + } + return newChunk; +} + +void * +LifoAlloc::allocUnaligned(size_t n) +{ + void *result; + if (latest && (result = latest->tryAllocUnaligned(n))) + return result; + + return alloc(n); +} + +void * +LifoAlloc::reallocUnaligned(void *origPtr, size_t origSize, size_t incr) +{ + JS_ASSERT(first && latest); + + /* + * Maybe we can grow the latest allocation in a BumpChunk. + * + * Note: we could also realloc the whole BumpChunk in the !canAlloc + * case, but this should not be a frequently hit case. + */ + if (latest + && origPtr == (char *) latest->mark() - origSize + && latest->canAllocUnaligned(incr)) { + JS_ALWAYS_TRUE(allocUnaligned(incr)); + return origPtr; + } + + /* Otherwise, memcpy. */ + size_t newSize = origSize + incr; + void *newPtr = allocUnaligned(newSize); + return newPtr ? memcpy(newPtr, origPtr, origSize) : NULL; +} diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h new file mode 100644 index 00000000000..da367f20def --- /dev/null +++ b/js/src/ds/LifoAlloc.h @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 12, 2009. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. + * + * Contributor(s): + * Chris Leary + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef LifoAlloc_h__ +#define LifoAlloc_h__ + +/* + * Lifetime-based fast allocation, inspired by much prior art, including + * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" + * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). + * + * This data structure supports stacky LIFO allocation (mark/release and + * LifoAllocScope). It does not maintain one contiguous segment; instead, it + * maintains a bunch of linked memory segments. In order to prevent malloc/free + * thrashing, unused segments are deallocated when garbage collection occurs. + */ + +#include "jsutil.h" +#include "jstl.h" + +namespace js { + +namespace detail { + +static const size_t LIFO_ALLOC_ALIGN = 8; + +JS_ALWAYS_INLINE +char * +AlignPtr(void *orig) +{ + typedef typename tl::StaticAssert< + tl::FloorLog2::result == tl::CeilingLog2::result + >::result _; + + char *result = (char *) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & -LIFO_ALLOC_ALIGN); + JS_ASSERT(uintptr_t(result) % LIFO_ALLOC_ALIGN == 0); + return result; +} + +/* Header for a chunk of memory wrangled by the LifoAlloc. */ +class BumpChunk +{ + char *bump; + char *limit; + BumpChunk *next_; + size_t bumpSpaceSize; + + char *base() const { return limit - bumpSpaceSize; } + + explicit BumpChunk(size_t bumpSpaceSize) + : bump(reinterpret_cast(this) + sizeof(BumpChunk)), limit(bump + bumpSpaceSize), + next_(NULL), bumpSpaceSize(bumpSpaceSize) { + JS_ASSERT(bump == AlignPtr(bump)); + } + + void clobberUnused() { +#ifdef DEBUG + memset(bump, 0xcd, limit - bump); +#endif + } + + void setBump(void *ptr) { + JS_ASSERT(base() <= ptr); + JS_ASSERT(ptr <= limit); + DebugOnly prevBump = bump; + bump = static_cast(ptr); + if (prevBump < bump) + clobberUnused(); + } + + public: + BumpChunk *next() const { return next_; } + void setNext(BumpChunk *succ) { next_ = succ; } + + size_t used() const { return bump - base(); } + + void resetBump() { + setBump(reinterpret_cast(this) + sizeof(BumpChunk)); + } + + void *mark() const { return bump; } + + void release(void *mark) { + JS_ASSERT(contains(mark)); + JS_ASSERT(mark <= bump); + setBump(mark); + } + + bool contains(void *mark) const { + return base() <= mark && mark <= limit; + } + + bool canAlloc(size_t n) { + return AlignPtr(bump) + n <= limit; + } + + bool canAllocUnaligned(size_t n) { + return bump + n <= limit; + } + + /* Try to perform an allocation of size |n|, return null if not possible. */ + JS_ALWAYS_INLINE + void *tryAlloc(size_t n) { + char *aligned = AlignPtr(bump); + char *newBump = aligned + n; + if (newBump > limit) + return NULL; + + setBump(newBump); + return aligned; + } + + void *tryAllocUnaligned(size_t n); + + void *allocInfallible(size_t n) { + void *result = tryAlloc(n); + JS_ASSERT(result); + return result; + } + + static BumpChunk *new_(size_t chunkSize); + + static void delete_(BumpChunk *chunk) { +#ifdef DEBUG + memset(chunk, 0xcd, sizeof(*chunk) + chunk->bumpSpaceSize); +#endif + js_free(chunk); + } +}; + +} /* namespace detail */ + +/* + * LIFO bump allocator: used for phase-oriented and fast LIFO allocations. + * + * Note: |latest| is not necessary "last". We leave BumpChunks latent in the + * chain after they've been released to avoid thrashing before a GC. + */ +class LifoAlloc +{ + typedef detail::BumpChunk BumpChunk; + + BumpChunk *first; + BumpChunk *latest; + size_t markCount; + size_t defaultChunkSize_; + + void operator=(const LifoAlloc &); + LifoAlloc(const LifoAlloc &); + + /* + * Return a BumpChunk that can perform an allocation of at least size |n| + * and add it to the chain appropriately. + * + * Side effect: if retval is non-null, |first| and |latest| are initialized + * appropriately. + */ + BumpChunk *getOrCreateChunk(size_t n); + + void reset(size_t defaultChunkSize) { + JS_ASSERT(RoundUpPow2(defaultChunkSize) == defaultChunkSize); + first = latest = NULL; + defaultChunkSize_ = defaultChunkSize; + markCount = 0; + } + + public: + explicit LifoAlloc(size_t defaultChunkSize) { reset(defaultChunkSize); } + + /* Steal allocated chunks from |other|. */ + void steal(LifoAlloc *other) { + JS_ASSERT(!other->markCount); + PodCopy((char *) this, (char *) other, sizeof(*this)); + other->reset(defaultChunkSize_); + } + + ~LifoAlloc() { freeAll(); } + + size_t defaultChunkSize() const { return defaultChunkSize_; } + + /* Frees all held memory. */ + void freeAll(); + + /* Should be called on GC in order to release any held chunks. */ + void freeUnused(); + + JS_ALWAYS_INLINE + void *alloc(size_t n) { + void *result; + if (latest && (result = latest->tryAlloc(n))) + return result; + + if (!getOrCreateChunk(n)) + return NULL; + + return latest->allocInfallible(n); + } + + template + T *newArray(size_t count) { + void *mem = alloc(sizeof(T) * count); + if (!mem) + return NULL; + JS_STATIC_ASSERT(tl::IsPodType::result); + return (T *) mem; + } + + /* + * Create an array with uninitialized elements of type |T|. + * The caller is responsible for initialization. + */ + template + T *newArrayUninitialized(size_t count) { + return static_cast(alloc(sizeof(T) * count)); + } + + void *mark() { + markCount++; + + return latest ? latest->mark() : NULL; + } + + void release(void *mark) { + markCount--; + + if (!mark) { + latest = first; + if (latest) + latest->resetBump(); + return; + } + + /* + * Find the chunk that contains |mark|, and make sure we don't pass + * |latest| along the way -- we should be making the chain of active + * chunks shorter, not longer! + */ + BumpChunk *container = first; + while (true) { + if (container->contains(mark)) + break; + JS_ASSERT(container != latest); + container = container->next(); + } + latest = container; + latest->release(mark); + } + + /* Get the total "used" (occupied bytes) count for the arena chunks. */ + size_t used() const { + size_t accum = 0; + BumpChunk *it = first; + while (it) { + accum += it->used(); + it = it->next(); + } + return accum; + } + + /* Doesn't perform construction; useful for lazily-initialized POD types. */ + template + JS_ALWAYS_INLINE + T *newPod() { + return static_cast(alloc(sizeof(T))); + } + + JS_DECLARE_NEW_METHODS(alloc, JS_ALWAYS_INLINE) + + /* Some legacy clients (ab)use LifoAlloc to act like a vector, see bug 688891. */ + + void *allocUnaligned(size_t n); + void *reallocUnaligned(void *origPtr, size_t origSize, size_t incr); +}; + +class LifoAllocScope { + LifoAlloc *lifoAlloc; + void *mark; + bool shouldRelease; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + + public: + explicit LifoAllocScope(LifoAlloc *lifoAlloc + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : lifoAlloc(lifoAlloc), shouldRelease(true) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + mark = lifoAlloc->mark(); + } + + ~LifoAllocScope() { + if (shouldRelease) + lifoAlloc->release(mark); + } + + void releaseEarly() { + JS_ASSERT(shouldRelease); + lifoAlloc->release(mark); + shouldRelease = false; + } +}; + +} /* namespace js */ + +#endif diff --git a/js/src/frontend/ParseMaps.cpp b/js/src/frontend/ParseMaps.cpp index ab2d563afda..edc5623715a 100644 --- a/js/src/frontend/ParseMaps.cpp +++ b/js/src/frontend/ParseMaps.cpp @@ -39,6 +39,7 @@ * ***** END LICENSE BLOCK ***** */ #include "ParseMaps-inl.h" +#include "jscompartment.h" using namespace js; @@ -125,13 +126,12 @@ DumpAtomDefnMap(const AtomDefnMapPtr &map) AtomDeclNode * AtomDecls::allocNode(JSDefinition *defn) { - AtomDeclNode *p; - JS_ARENA_ALLOCATE_TYPE(p, AtomDeclNode, &cx->tempPool); + AtomDeclNode *p = cx->tempLifoAlloc().new_(defn); if (!p) { js_ReportOutOfMemory(cx); return NULL; } - return new (p) AtomDeclNode(defn); + return p; } bool diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index 09e7a838b3e..8da77037679 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -109,8 +109,7 @@ Bytecode::mergeDefines(JSContext *cx, ScriptAnalysis *script, bool initial, * with progressively smaller sets of defined variables. */ if (!owned) { - uint32 *reallocArray = - ArenaArray(cx->compartment->pool, defineCount); + uint32 *reallocArray = cx->typeLifoAlloc().newArray(defineCount); if (!reallocArray) { script->setOOM(cx); return false; @@ -134,12 +133,11 @@ void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc) { printf("#%u:", script->id()); - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAlloc lifoAlloc(1024); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0); js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter); fprintf(stdout, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); } #endif @@ -157,7 +155,7 @@ ScriptAnalysis::addJump(JSContext *cx, unsigned offset, Bytecode *&code = codeArray[offset]; bool initial = (code == NULL); if (initial) { - code = ArenaNew(cx->compartment->pool); + code = cx->typeLifoAlloc().new_(); if (!code) { setOOM(cx); return false; @@ -278,16 +276,16 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) { JS_ASSERT(cx->compartment->activeAnalysis); JS_ASSERT(!ranBytecode()); - JSArenaPool &pool = cx->compartment->pool; + LifoAlloc &tla = cx->typeLifoAlloc(); unsigned length = script->length; unsigned nargs = script->hasFunction ? script->function()->nargs : 0; numSlots = TotalSlots(script); - codeArray = ArenaArray(pool, length); - definedLocals = ArenaArray(pool, script->nfixed); - escapedSlots = ArenaArray(pool, numSlots); + codeArray = tla.newArray(length); + definedLocals = tla.newArray(script->nfixed); + escapedSlots = tla.newArray(numSlots); if (!codeArray || !definedLocals || !escapedSlots) { setOOM(cx); @@ -371,7 +369,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) unsigned forwardCatch = 0; /* Fill in stack depth and definitions at initial bytecode. */ - Bytecode *startcode = ArenaNew(pool); + Bytecode *startcode = tla.new_(); if (!startcode) { setOOM(cx); return; @@ -703,7 +701,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) if (definedLocals[local] == LOCAL_CONDITIONALLY_DEFINED) { if (forwardJump) { /* Add this local to the variables defined after this bytecode. */ - uint32 *newArray = ArenaArray(pool, defineCount + 1); + uint32 *newArray = tla.newArray(defineCount + 1); if (!newArray) { setOOM(cx); return; @@ -795,7 +793,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) bool initial = (nextcode == NULL); if (initial) { - nextcode = ArenaNew(pool); + nextcode = tla.new_(); if (!nextcode) { setOOM(cx); return; @@ -839,9 +837,9 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) return; } - JSArenaPool &pool = cx->compartment->pool; + LifoAlloc &tla = cx->typeLifoAlloc(); - lifetimes = ArenaArray(pool, numSlots); + lifetimes = tla.newArray(numSlots); if (!lifetimes) { setOOM(cx); return; @@ -969,7 +967,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) /* Restore all saved variables. :FIXME: maybe do this precisely. */ for (unsigned i = 0; i < savedCount; i++) { LifetimeVariable &var = *saved[i]; - var.lifetime = ArenaNew(pool, offset, var.savedEnd, var.saved); + var.lifetime = tla.new_(offset, var.savedEnd, var.saved); if (!var.lifetime) { cx->free_(saved); setOOM(cx); @@ -1036,7 +1034,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) if (loop && loop->entry > loop->lastBlock) loop->lastBlock = loop->entry; - LoopAnalysis *nloop = ArenaNew(pool); + LoopAnalysis *nloop = tla.new_(); if (!nloop) { cx->free_(saved); setOOM(cx); @@ -1085,7 +1083,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) * Jumping to a place where this variable is live. Make a new * lifetime segment for the variable. */ - var.lifetime = ArenaNew(pool, offset, var.savedEnd, var.saved); + var.lifetime = tla.new_(offset, var.savedEnd, var.saved); if (!var.lifetime) { cx->free_(saved); setOOM(cx); @@ -1149,7 +1147,7 @@ ScriptAnalysis::addVariable(JSContext *cx, LifetimeVariable &var, unsigned offse } } } - var.lifetime = ArenaNew(cx->compartment->pool, offset, var.savedEnd, var.saved); + var.lifetime = cx->typeLifoAlloc().new_(offset, var.savedEnd, var.saved); if (!var.lifetime) { setOOM(cx); return; @@ -1166,7 +1164,7 @@ ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offs /* Make a point lifetime indicating the write. */ if (!var.saved) saved[savedCount++] = &var; - var.saved = ArenaNew(cx->compartment->pool, offset, var.savedEnd, var.saved); + var.saved = cx->typeLifoAlloc().new_(offset, var.savedEnd, var.saved); if (!var.saved) { setOOM(cx); return; @@ -1252,7 +1250,7 @@ ScriptAnalysis::extendVariable(JSContext *cx, LifetimeVariable &var, } JS_ASSERT(savedEnd <= end); if (savedEnd > segment->end) { - Lifetime *tail = ArenaNew(cx->compartment->pool, savedEnd, 0, segment->next); + Lifetime *tail = cx->typeLifoAlloc().new_(savedEnd, 0, segment->next); if (!tail) { setOOM(cx); return; @@ -1329,7 +1327,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx) return; } - JSArenaPool &pool = cx->compartment->pool; + LifoAlloc &tla = cx->typeLifoAlloc(); unsigned maxDepth = script->nslots - script->nfixed; /* @@ -1490,7 +1488,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx) unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses; if (xuses) { - code->poppedValues = (SSAValue *)ArenaArray(pool, xuses); + code->poppedValues = tla.newArray(xuses); if (!code->poppedValues) { setOOM(cx); return; @@ -1513,7 +1511,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx) } if (xuses) { - SSAUseChain *useChains = ArenaArray(cx->compartment->pool, xuses); + SSAUseChain *useChains = tla.newArray(xuses); if (!useChains) { setOOM(cx); return; @@ -1540,7 +1538,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx) unsigned xdefs = ExtendedDef(pc) ? ndefs + 1 : ndefs; if (xdefs) { - code->pushedUses = ArenaArray(cx->compartment->pool, xdefs); + code->pushedUses = tla.newArray(xdefs); if (!code->pushedUses) { setOOM(cx); return; @@ -1731,8 +1729,8 @@ PhiNodeCapacity(unsigned length) bool ScriptAnalysis::makePhi(JSContext *cx, uint32 slot, uint32 offset, SSAValue *pv) { - SSAPhiNode *node = ArenaNew(cx->compartment->pool); - SSAValue *options = ArenaArray(cx->compartment->pool, PhiNodeCapacity(0)); + SSAPhiNode *node = cx->typeLifoAlloc().new_(); + SSAValue *options = cx->typeLifoAlloc().newArray(PhiNodeCapacity(0)); if (!node || !options) { setOOM(cx); return false; @@ -1764,7 +1762,7 @@ ScriptAnalysis::insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v) if (trackUseChain(v)) { SSAUseChain *&uses = useChain(v); - SSAUseChain *use = ArenaNew(cx->compartment->pool); + SSAUseChain *use = cx->typeLifoAlloc().new_(); if (!use) { setOOM(cx); return; @@ -1782,8 +1780,8 @@ ScriptAnalysis::insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v) return; } - SSAValue *newOptions = ArenaArray(cx->compartment->pool, - PhiNodeCapacity(node->length + 1)); + SSAValue *newOptions = + cx->typeLifoAlloc().newArray(PhiNodeCapacity(node->length + 1)); if (!newOptions) { setOOM(cx); return; @@ -1922,7 +1920,7 @@ ScriptAnalysis::freezeNewValues(JSContext *cx, uint32 offset) return; } - code.newValues = ArenaArray(cx->compartment->pool, count + 1); + code.newValues = cx->typeLifoAlloc().newArray(count + 1); if (!code.newValues) { setOOM(cx); return; diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index 9bbcb09ca90..371bf0c4471 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -41,11 +41,13 @@ #ifndef jsanalyze_h___ #define jsanalyze_h___ -#include "jsarena.h" #include "jscompartment.h" #include "jscntxt.h" #include "jsinfer.h" #include "jsscript.h" +#include "jstl.h" + +#include "ds/LifoAlloc.h" struct JSScript; @@ -83,11 +85,6 @@ namespace analyze { * analyses are independent from type inference. */ -class SSAValue; -struct SSAUseChain; -struct LoopAnalysis; -struct SlotValue; - /* Information about a bytecode instruction. */ class Bytecode { @@ -1372,4 +1369,16 @@ void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc); } /* namespace analyze */ } /* namespace js */ +namespace js { +namespace tl { + +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; + +} /* namespace tl */ +} /* namespace js */ + #endif // jsanalyze_h___ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 51feb91dd36..34f28d602ec 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -48,7 +48,6 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsclist.h" #include "jsdhash.h" @@ -100,6 +99,7 @@ #include "vm/Stack-inl.h" #include "vm/String-inl.h" +#include "ds/LifoAlloc.h" #if ENABLE_YARR_JIT #include "assembler/jit/ExecutableAllocator.h" @@ -2608,10 +2608,6 @@ JS_CompartmentGC(JSContext *cx, JSCompartment *comp) LeaveTrace(cx); - /* Don't nuke active arenas if executing or compiling. */ - if (cx->tempPool.current == &cx->tempPool.first) - JS_FinishArenaPool(&cx->tempPool); - GCREASON(PUBLIC_API); js_GC(cx, comp, GC_NORMAL); } @@ -2628,10 +2624,6 @@ JS_MaybeGC(JSContext *cx) { LeaveTrace(cx); - /* Don't nuke active arenas if executing or compiling. */ - if (cx->tempPool.current == &cx->tempPool.first) - JS_FinishArenaPool(&cx->tempPool); - MaybeGC(cx); } diff --git a/js/src/jsarena.cpp b/js/src/jsarena.cpp deleted file mode 100644 index 9d41f79258e..00000000000 --- a/js/src/jsarena.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Lifetime-based fast allocation, inspired by much prior art, including - * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" - * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). - */ -#include -#include -#include "jsalloc.h" -#include "jstypes.h" -#include "jsstdint.h" -#include "jsbit.h" -#include "jsarena.h" -#include "jsprvtd.h" - -using namespace js; - -/* If JSArena's length is a multiple of 8, that ensures its payload is 8-aligned. */ -JS_STATIC_ASSERT(sizeof(JSArena) % 8 == 0); - -JS_PUBLIC_API(void) -JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) -{ - /* Restricting ourselves to some simple alignments keeps things simple. */ - if (align == 1 || align == 2 || align == 4 || align == 8) { - pool->mask = align - 1; - } else { - /* This shouldn't happen, but set pool->mask reasonably if it does. */ - JS_NOT_REACHED("JS_InitArenaPool: bad align"); - pool->mask = 7; - } - pool->first.next = NULL; - /* pool->first is a zero-sized dummy arena that's never allocated from. */ - pool->first.base = pool->first.avail = pool->first.limit = - JS_ARENA_ALIGN(pool, &pool->first + 1); - pool->current = &pool->first; - pool->arenasize = size; -} - -JS_PUBLIC_API(void *) -JS_ArenaAllocate(JSArenaPool *pool, size_t nb) -{ - /* - * Search pool from current forward till we find or make enough space. - * - * NB: subtract nb from a->limit in the loop condition, instead of adding - * nb to a->avail, to avoid overflow (possible when running a 32-bit - * program on a 64-bit system where the kernel maps the heap up against the - * top of the 32-bit address space, see bug 279273). Note that this - * necessitates a comparison between nb and a->limit that looks like a - * (conceptual) type error but isn't. - */ - JS_ASSERT((nb & pool->mask) == 0); - JSArena *a; - /* - * Comparing nb to a->limit looks like a (conceptual) type error, but it's - * necessary to avoid wrap-around. Yuk. - */ - for (a = pool->current; nb > a->limit || a->avail > a->limit - nb; pool->current = a) { - JSArena **ap = &a->next; - if (!*ap) { - /* Not enough space in pool, so we must malloc. */ - size_t gross = sizeof(JSArena) + JS_MAX(nb, pool->arenasize); - a = (JSArena *) OffTheBooks::malloc_(gross); - if (!a) - return NULL; - - a->next = NULL; - a->base = a->avail = jsuword(a) + sizeof(JSArena); - /* - * Because malloc returns 8-aligned pointers and sizeof(JSArena) is - * a multiple of 8, a->base will always be 8-aligned, which should - * suffice for any valid pool. - */ - JS_ASSERT(a->base == JS_ARENA_ALIGN(pool, a->base)); - a->limit = (jsuword)a + gross; - - *ap = a; - continue; - } - a = *ap; /* move to next arena */ - } - - void* p = (void *)a->avail; - a->avail += nb; - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - return p; -} - -JS_PUBLIC_API(void *) -JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) -{ - /* If we've called JS_ArenaRealloc, the new size must be bigger than pool->arenasize. */ - JS_ASSERT(size + incr > pool->arenasize); - - /* Find the arena containing |p|. */ - JSArena *a; - JSArena **ap = &pool->first.next; - while (true) { - a = *ap; - if (JS_IS_IN_ARENA(a, p)) - break; - JS_ASSERT(a != pool->current); - ap = &a->next; - } - /* If we've called JS_ArenaRealloc, p must be at the start of an arena. */ - JS_ASSERT(a->base == jsuword(p)); - - size_t gross = sizeof(JSArena) + JS_ARENA_ALIGN(pool, size + incr); - a = (JSArena *) OffTheBooks::realloc_(a, gross); - if (!a) - return NULL; - - a->base = jsuword(a) + sizeof(JSArena); - a->avail = a->limit = jsuword(a) + gross; - /* - * Because realloc returns 8-aligned pointers and sizeof(JSArena) is a - * multiple of 8, a->base will always be 8-aligned, which should suffice - * for any valid pool. - */ - JS_ASSERT(a->base == JS_ARENA_ALIGN(pool, a->base)); - - if (a != *ap) { - /* realloc moved the allocation: update other pointers to a. */ - if (pool->current == *ap) - pool->current = a; - *ap = a; - } - - return (void *)a->base; -} - -JS_PUBLIC_API(void *) -JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr) -{ - void *newp; - - /* - * If p points to an oversized allocation, it owns an entire arena, so we - * can simply realloc the arena. - */ - if (size > pool->arenasize) - return JS_ArenaRealloc(pool, p, size, incr); - - JS_ARENA_ALLOCATE(newp, pool, size + incr); - if (newp) - memcpy(newp, p, size); - return newp; -} - -/* - * Free tail arenas linked after head, which may not be the true list head. - * Reset pool->current to point to head in case it pointed at a tail arena. - */ -static void -FreeArenaList(JSArenaPool *pool, JSArena *head) -{ - JSArena **ap, *a; - - ap = &head->next; - a = *ap; - if (!a) - return; - -#ifdef DEBUG - do { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - a->avail = a->base; - JS_CLEAR_UNUSED(a); - } while ((a = a->next) != NULL); - a = *ap; -#endif - - do { - *ap = a->next; - JS_CLEAR_ARENA(a); - UnwantedForeground::free_(a); - } while ((a = *ap) != NULL); - - pool->current = head; -} - -JS_PUBLIC_API(void) -JS_ArenaRelease(JSArenaPool *pool, char *mark) -{ - JSArena *a; - - for (a = &pool->first; a; a = a->next) { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - - if (JS_IS_IN_ARENA(a, mark)) { - a->avail = JS_ARENA_ALIGN(pool, mark); - JS_ASSERT(a->avail <= a->limit); - FreeArenaList(pool, a); - return; - } - } -} - -JS_PUBLIC_API(void) -JS_FreeArenaPool(JSArenaPool *pool) -{ - FreeArenaList(pool, &pool->first); -} - -JS_PUBLIC_API(void) -JS_FinishArenaPool(JSArenaPool *pool) -{ - FreeArenaList(pool, &pool->first); -} - -JS_PUBLIC_API(void) -JS_ArenaFinish() -{ -} - -JS_PUBLIC_API(void) -JS_ArenaShutDown(void) -{ -} diff --git a/js/src/jsarena.h b/js/src/jsarena.h deleted file mode 100644 index e45e8e44b03..00000000000 --- a/js/src/jsarena.h +++ /dev/null @@ -1,317 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsarena_h___ -#define jsarena_h___ -/* - * Lifetime-based fast allocation, inspired by much prior art, including - * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" - * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). - * - * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE). - */ -#include -#include "jstypes.h" -#include "jscompat.h" -#include "jsstaticcheck.h" - -JS_BEGIN_EXTERN_C - -typedef struct JSArena JSArena; -typedef struct JSArenaPool JSArenaPool; - -struct JSArena { - JSArena *next; /* next arena for this lifetime */ - jsuword base; /* aligned base address, follows this header */ - jsuword limit; /* one beyond last byte in arena */ - jsuword avail; /* points to next available byte */ -}; - -struct JSArenaPool { - JSArena first; /* first arena in pool list */ - JSArena *current; /* arena from which to allocate space */ - size_t arenasize; /* net exact size of a new arena */ - jsuword mask; /* alignment mask (power-of-2 - 1) */ -}; - -#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask) - -#define JS_ARENA_ALLOCATE(p, pool, nb) \ - JS_ARENA_ALLOCATE_CAST(p, void *, pool, nb) - -#define JS_ARENA_ALLOCATE_TYPE(p, type, pool) \ - JS_ARENA_ALLOCATE_COMMON(p, type *, pool, sizeof(type), 0) - -#define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb) \ - JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, _nb > _a->limit) - -/* - * NB: In JS_ARENA_ALLOCATE_CAST and JS_ARENA_GROW_CAST, always subtract _nb - * from a->limit rather than adding _nb to _p, to avoid overflow (possible when - * running a 32-bit program on a 64-bit system where the kernel maps the heap - * up against the top of the 32-bit address space, see bug 279273). Note that - * this necessitates a comparison between nb and a->limit that looks like a - * (conceptual) type error but isn't. - */ -#define JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, guard) \ - JS_BEGIN_MACRO \ - JSArena *_a = (pool)->current; \ - size_t _nb = JS_ARENA_ALIGN(pool, nb); \ - jsuword _p = _a->avail; \ - if ((guard) || _p > _a->limit - _nb) \ - _p = (jsuword)JS_ArenaAllocate(pool, _nb); \ - else \ - _a->avail = _p + _nb; \ - p = (type) _p; \ - STATIC_ASSUME(!p || ubound((char *)p) >= nb); \ - JS_END_MACRO - -#define JS_ARENA_GROW(p, pool, size, incr) \ - JS_ARENA_GROW_CAST(p, void *, pool, size, incr) - -#define JS_ARENA_GROW_CAST(p, type, pool, size, incr) \ - JS_BEGIN_MACRO \ - JSArena *_a = (pool)->current; \ - if (_a->avail == (jsuword)(p) + JS_ARENA_ALIGN(pool, size)) { \ - /* p was the last thing allocated in the current arena... */ \ - size_t _nb = (size) + (incr); \ - _nb = JS_ARENA_ALIGN(pool, _nb); \ - if (_a->limit >= _nb && (jsuword)(p) <= _a->limit - _nb) { \ - /* ... and we have space, so just extend p in-place */ \ - _a->avail = (jsuword)(p) + _nb; \ - } else if ((jsuword)(p) == _a->base) { \ - /* ... p is also the 1st thing in this arena */ \ - p = (type) JS_ArenaRealloc(pool, p, size, incr); \ - } else { \ - /* hard case */ \ - p = (type) JS_ArenaGrow(pool, p, size, incr); \ - } \ - } else { \ - /* hard case */ \ - p = (type) JS_ArenaGrow(pool, p, size, incr); \ - } \ - STATIC_ASSUME(!p || ubound((char *)p) >= size + incr); \ - JS_END_MACRO - -#define JS_ARENA_MARK(pool) ((void *) (pool)->current->avail) -#define JS_UPTRDIFF(p,q) ((jsuword)(p) - (jsuword)(q)) - -/* - * Check if the mark is inside arena's allocated area. - */ -#define JS_IS_IN_ARENA(a, mark) \ - (JS_UPTRDIFF(mark, (a)->base) <= JS_UPTRDIFF((a)->avail, (a)->base)) - -#ifdef DEBUG -#define JS_CLEAR_UNUSED(a) (JS_ASSERT((a)->avail <= (a)->limit), \ - memset((void*)(a)->avail, JS_FREE_PATTERN, \ - (a)->limit - (a)->avail)) -#define JS_CLEAR_ARENA(a) memset((void*)(a), JS_FREE_PATTERN, \ - (a)->limit - (jsuword)(a)) -#else -#define JS_CLEAR_UNUSED(a) /* nothing */ -#define JS_CLEAR_ARENA(a) /* nothing */ -#endif - -#define JS_ARENA_RELEASE(pool, mark) \ - JS_BEGIN_MACRO \ - char *_m = (char *)(mark); \ - JSArena *_a = (pool)->current; \ - if (_a != &(pool)->first && JS_IS_IN_ARENA(_a, _m)) { \ - _a->avail = (jsuword)JS_ARENA_ALIGN(pool, _m); \ - JS_ASSERT(_a->avail <= _a->limit); \ - JS_CLEAR_UNUSED(_a); \ - } else { \ - JS_ArenaRelease(pool, _m); \ - } \ - JS_END_MACRO - -#define JS_ARENA_DESTROY(pool, a, pnext) \ - JS_BEGIN_MACRO \ - JS_COUNT_ARENA(pool,--); \ - if ((pool)->current == (a)) (pool)->current = &(pool)->first; \ - *(pnext) = (a)->next; \ - JS_CLEAR_ARENA(a); \ - js::UnwantedForeground::free_(a); \ - (a) = NULL; \ - JS_END_MACRO - -/* - * Initialize an arena pool with a minimum size per arena of |size| bytes. - * |align| must be 1, 2, 4 or 8. - */ -extern JS_PUBLIC_API(void) -JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, - size_t align); - -/* - * Free the arenas in pool. The user may continue to allocate from pool - * after calling this function. There is no need to call JS_InitArenaPool() - * again unless JS_FinishArenaPool(pool) has been called. - */ -extern JS_PUBLIC_API(void) -JS_FreeArenaPool(JSArenaPool *pool); - -/* - * Free the arenas in pool and finish using it altogether. - */ -extern JS_PUBLIC_API(void) -JS_FinishArenaPool(JSArenaPool *pool); - -/* - * Deprecated do-nothing function. - */ -extern JS_PUBLIC_API(void) -JS_ArenaFinish(void); - -/* - * Deprecated do-nothing function. - */ -extern JS_PUBLIC_API(void) -JS_ArenaShutDown(void); - -/* - * Friend functions used by the JS_ARENA_*() macros. - */ -extern JS_PUBLIC_API(void *) -JS_ArenaAllocate(JSArenaPool *pool, size_t nb); - -extern JS_PUBLIC_API(void *) -JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr); - -extern JS_PUBLIC_API(void *) -JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaRelease(JSArenaPool *pool, char *mark); - -JS_END_EXTERN_C - -#ifdef __cplusplus - -namespace js { - -template -inline T * -ArenaArray(JSArenaPool &pool, unsigned count) -{ - void *v; - JS_ARENA_ALLOCATE(v, &pool, count * sizeof(T)); - return (T *) v; -} - -template -inline T * -ArenaNew(JSArenaPool &pool) -{ - void *v; - JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); - return v ? new (v) T() : NULL; -} - -template -inline T * -ArenaNew(JSArenaPool &pool, const A &a) -{ - void *v; - JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); - return v ? new (v) T(a) : NULL; -} - -template -inline T * -ArenaNew(JSArenaPool &pool, const A &a, const B &b) -{ - void *v; - JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); - return v ? new (v) T(a, b) : NULL; -} - -template -inline T * -ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c) -{ - void *v; - JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); - return v ? new (v) T(a, b, c) : NULL; -} - -template -inline T * -ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c, const D &d) -{ - void *v; - JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); - return v ? new (v) T(a, b, c, d) : NULL; -} - -template -inline T * -ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c, const D &d, const E &e) -{ - void *v; - JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); - return v ? new (v) T(a, b, c, d, e) : NULL; -} - -inline uintN -ArenaAllocatedSize(const JSArenaPool &pool) -{ - uintN res = 0; - const JSArena *a = &pool.first; - while (a) { - res += (a->limit - (jsuword)a); - a = a->next; - } - return res; -} - -/* Move the contents of oldPool into newPool, and reset oldPool. */ -inline void -MoveArenaPool(JSArenaPool *oldPool, JSArenaPool *newPool) -{ - *newPool = *oldPool; - JS_InitArenaPool(oldPool, NULL, newPool->arenasize, newPool->mask + 1); -} - -} /* namespace js */ - -#endif /* __cplusplus */ - -#endif /* jsarena_h___ */ diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 52ebdc41f60..1c545b71824 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -57,7 +57,6 @@ #include "jsstdint.h" #include "jstypes.h" -#include "jsarena.h" #include "jsutil.h" #include "jsclist.h" #include "jsprf.h" @@ -112,6 +111,7 @@ ThreadData::ThreadData() maxCodeCacheBytes(DEFAULT_JIT_CACHE_SIZE), #endif waiveGCQuota(false), + tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), dtoaState(NULL), nativeStackBase(GetNativeStackBase()), pendingProxyOperation(NULL), @@ -316,9 +316,6 @@ js_PurgeThreads(JSContext *cx) #endif } -static const size_t ARENA_HEADER_SIZE_HACK = 40; -static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK; - JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize) { @@ -345,9 +342,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT); VOUCH_DOES_NOT_REQUIRE_STACK(); - JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble)); - JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int)); - JS_ASSERT(cx->resolveFlags == 0); if (!cx->busyArrays.init()) { @@ -1373,9 +1367,6 @@ JSContext::~JSContext() if (parseMapPool_) Foreground::delete_(parseMapPool_); - JS_FinishArenaPool(®ExpPool); - JS_FinishArenaPool(&tempPool); - if (lastMessage) Foreground::free_(lastMessage); @@ -1502,25 +1493,9 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) return NULL; } -/* - * Release pool's arenas if the stackPool has existed for longer than the - * limit specified by gcEmptyArenaPoolLifespan. - */ -static void -FreeOldArenas(JSRuntime *rt, JSArenaPool *pool) -{ - JSArena *a = pool->current; - if (a == pool->first.next && a->avail == a->base + sizeof(int64)) { - int64 age = JS_Now() - *(int64 *) a->base; - if (age > int64(rt->gcEmptyArenaPoolLifespan) * 1000) - JS_FreeArenaPool(pool); - } -} - void JSContext::purge() { - FreeOldArenas(runtime, ®ExpPool); if (!activeCompilations) { Foreground::delete_(parseMapPool_); parseMapPool_ = NULL; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 3a8b0be74e9..12c98871c82 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -46,7 +46,6 @@ #include #include "jsprvtd.h" -#include "jsarena.h" #include "jsclist.h" #include "jsatom.h" #include "jsdhash.h" @@ -64,6 +63,7 @@ #include "jsvector.h" #include "prmjtime.h" +#include "ds/LifoAlloc.h" #include "vm/Stack.h" #include "vm/String.h" @@ -190,6 +190,10 @@ struct ThreadData { */ bool waiveGCQuota; + /* Temporary arena pool used while compiling and decompiling. */ + static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; + LifoAlloc tempLifoAlloc; + /* * The GSN cache is per thread since even multi-cx-per-thread embeddings * do not interleave js_GetSrcNote calls. @@ -224,6 +228,7 @@ struct ThreadData { } void purge(JSContext *cx) { + tempLifoAlloc.freeUnused(); gsnCache.purge(); /* FIXME: bug 506341. */ @@ -994,17 +999,11 @@ struct JSContext /* Wrap cx->exception for the current compartment. */ void wrapPendingException(); - /* Temporary arena pool used while compiling and decompiling. */ - JSArenaPool tempPool; - private: /* Lazily initialized pool of maps used during parse/emit. */ js::ParseMapPool *parseMapPool_; public: - /* Temporary arena pool used while evaluate regular expressions. */ - JSArenaPool regExpPool; - /* Top-level object and pointer to top stack frame's scope chain. */ JSObject *globalObject; @@ -1144,6 +1143,9 @@ struct JSContext bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); } bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); } + js::LifoAlloc &tempLifoAlloc() { return JS_THREAD_DATA(this)->tempLifoAlloc; } + inline js::LifoAlloc &typeLifoAlloc(); + #ifdef JS_THREADSAFE private: JSThread *thread_; @@ -1877,28 +1879,6 @@ class AutoKeepAtoms { ~AutoKeepAtoms() { JS_UNKEEP_ATOMS(rt); } }; -class AutoArenaAllocator { - JSArenaPool *pool; - void *mark; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - explicit AutoArenaAllocator(JSArenaPool *pool - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : pool(pool), mark(JS_ARENA_MARK(pool)) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - ~AutoArenaAllocator() { JS_ARENA_RELEASE(pool, mark); } - - template - T *alloc(size_t elems) { - void *ptr; - JS_ARENA_ALLOCATE(ptr, pool, elems * sizeof(T)); - return static_cast(ptr); - } -}; - class AutoReleasePtr { JSContext *cx; void *ptr; diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index cbc21b6a1c4..9395f955dd9 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -407,6 +407,12 @@ inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment() } #endif +inline js::LifoAlloc & +JSContext::typeLifoAlloc() +{ + return compartment->typeLifoAlloc; +} + inline bool JSContext::ensureGeneratorStackSpace() { diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index be8451a6411..d2279d210cb 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -74,6 +74,7 @@ JSCompartment::JSCompartment(JSRuntime *rt) gcTriggerBytes(0), gcLastBytes(0), hold(false), + typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), #ifdef JS_TRACER traceMonitor_(NULL), #endif @@ -132,11 +133,6 @@ JSCompartment::init(JSContext *cx) activeAnalysis = activeInference = false; types.init(cx); - /* Duplicated from jscntxt.cpp. :XXX: bug 675150 fix hack. */ - static const size_t ARENA_HEADER_SIZE_HACK = 40; - - JS_InitArenaPool(&pool, "analysis", 4096 - ARENA_HEADER_SIZE_HACK, 8); - if (!crossCompartmentWrappers.init()) return false; @@ -622,8 +618,8 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) * Clear the analysis pool, but don't release its data yet. While * sweeping types any live data will be allocated into the pool. */ - JSArenaPool oldPool; - MoveArenaPool(&pool, &oldPool); + LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize()); + oldAlloc.steal(&typeLifoAlloc); /* * Sweep analysis information and everything depending on it from the @@ -656,9 +652,6 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) JSScript *script = i.get(); script->clearAnalysis(); } - - /* Reset the analysis pool, releasing all analysis and intermediate type data. */ - JS_FinishArenaPool(&oldPool); } active = false; diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index cfd240ac396..c85d8f99c48 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -408,7 +408,8 @@ struct JS_FRIEND_API(JSCompartment) { * Cleared on every GC, unless the GC happens during analysis (indicated * by activeAnalysis, which is implied by activeInference). */ - JSArenaPool pool; + static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; + js::LifoAlloc typeLifoAlloc; bool activeAnalysis; bool activeInference; diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index ffd1e4e04f6..4191f41e4ef 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -485,10 +485,9 @@ JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp) return NULL; /* Munge data into the API this method implements. Avert your eyes! */ - *markp = JS_ARENA_MARK(&cx->tempPool); + *markp = cx->tempLifoAlloc().mark(); - jsuword *names; - JS_ARENA_ALLOCATE_CAST(names, jsuword *, &cx->tempPool, localNames.length() * sizeof *names); + jsuword *names = cx->tempLifoAlloc().newArray(localNames.length()); if (!names) { js_ReportOutOfMemory(cx); return NULL; @@ -513,7 +512,7 @@ JS_AtomKey(JSAtom *atom) extern JS_PUBLIC_API(void) JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark) { - JS_ARENA_RELEASE(&cx->tempPool, mark); + cx->tempLifoAlloc().release(mark); } JS_PUBLIC_API(JSScript *) @@ -2167,9 +2166,9 @@ JS_PUBLIC_API(void) JS_DumpBytecode(JSContext *cx, JSScript *script) { #if defined(DEBUG) - AutoArenaAllocator mark(&cx->tempPool); + LifoAlloc lifoAlloc(1024); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0); fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno); js_Disassemble(cx, script, true, &sprinter); diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index fb3ff20d365..23eec7d1859 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -48,7 +48,6 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsbit.h" #include "jsprf.h" @@ -75,6 +74,7 @@ #include "jsscriptinlines.h" #include "frontend/ParseMaps-inl.h" +#include "ds/LifoAlloc.h" /* Allocation chunk counts, must be powers of two in general. */ #define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */ @@ -515,8 +515,7 @@ AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp) if (jt) { cg->jtFreeList = jt->kids[JT_LEFT]; } else { - JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool, - sizeof *jt); + jt = args->cx->tempLifoAlloc().new_(); if (!jt) { js_ReportOutOfMemory(args->cx); return 0; @@ -3312,9 +3311,7 @@ EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) static Value * AllocateSwitchConstant(JSContext *cx) { - Value *pv; - JS_ARENA_ALLOCATE_TYPE(pv, Value, &cx->tempPool); - return pv; + return cx->tempLifoAlloc().new_(); } /* @@ -7752,14 +7749,12 @@ static JSBool NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind, uintN stackDepth, size_t start, size_t end) { - JSTryNode *tryNode; - JS_ASSERT((uintN)(uint16)stackDepth == stackDepth); JS_ASSERT(start <= end); JS_ASSERT((size_t)(uint32)start == start); JS_ASSERT((size_t)(uint32)end == end); - JS_ARENA_ALLOCATE_TYPE(tryNode, JSTryNode, &cx->tempPool); + JSTryNode *tryNode = cx->tempLifoAlloc().new_(); if (!tryNode) { js_ReportOutOfMemory(cx); return JS_FALSE; diff --git a/js/src/jsemit.h b/js/src/jsemit.h index dc3cfb16ecd..c5295a538b3 100644 --- a/js/src/jsemit.h +++ b/js/src/jsemit.h @@ -668,11 +668,9 @@ struct JSCodeGenerator : public JSTreeContext } /* - * Release cg->codePool, cg->notePool, and parser->context->tempPool to - * marks set by JSCodeGenerator's ctor. Note that cgs are magic: they own - * the arena pool "tops-of-stack" space above their codeMark, noteMark, and - * tempMark points. This means you cannot alloc from tempPool and save the - * pointer beyond the next JSCodeGenerator destructor call. + * Note that cgs are magic: they own the arena "top-of-stack" space above + * their tempMark points. This means that you cannot alloc from tempPool + * and save the pointer beyond the next JSCodeGenerator destructor call. */ ~JSCodeGenerator(); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index dd33c6db8b1..ca48ca69f16 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2181,8 +2181,8 @@ Function(JSContext *cx, uintN argc, Value *vp) * for a terminating 0. Mark cx->tempPool for later release, to free * collected_args and its tokenstream in one swoop. */ - AutoArenaAllocator aaa(&cx->tempPool); - jschar *cp = aaa.alloc(args_length + 1); + LifoAllocScope las(&cx->tempLifoAlloc()); + jschar *cp = cx->tempLifoAlloc().newArray(args_length + 1); if (!cp) { js_ReportOutOfMemory(cx); return false; diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index e79d8ad5ea9..c1ff7ec927f 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -352,7 +352,7 @@ TypeSet::make(JSContext *cx, const char *name) { JS_ASSERT(cx->compartment->activeInference); - TypeSet *res = ArenaNew(cx->compartment->pool); + TypeSet *res = cx->typeLifoAlloc().new_(); if (!res) { cx->compartment->types.setPendingNukeTypes(cx); return NULL; @@ -492,7 +492,7 @@ public: void TypeSet::addSubset(JSContext *cx, TypeSet *target) { - add(cx, ArenaNew(cx->compartment->pool, target)); + add(cx, cx->typeLifoAlloc().new_(target)); } /* Constraints for reads/writes on object properties. */ @@ -527,14 +527,14 @@ void TypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, jsid id) { - add(cx, ArenaNew(cx->compartment->pool, script, pc, target, id, false)); + add(cx, cx->typeLifoAlloc().new_(script, pc, target, id, false)); } void TypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, jsid id) { - add(cx, ArenaNew(cx->compartment->pool, script, pc, target, id, true)); + add(cx, cx->typeLifoAlloc().new_(script, pc, target, id, true)); } /* @@ -575,7 +575,7 @@ TypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid i if (JSOp(*callpc) == JSOP_NEW) return; - add(cx, ArenaNew(cx->compartment->pool, script, callpc, id)); + add(cx, cx->typeLifoAlloc().new_(script, callpc, id)); } /* @@ -608,8 +608,8 @@ void TypeSet::addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *objectTypes, TypeSet *valueTypes) { - add(cx, ArenaNew(cx->compartment->pool, script, pc, - objectTypes, valueTypes)); + add(cx, cx->typeLifoAlloc().new_(script, pc, objectTypes, + valueTypes)); } /* @@ -633,7 +633,7 @@ public: void TypeSet::addCall(JSContext *cx, TypeCallsite *site) { - add(cx, ArenaNew(cx->compartment->pool, site)); + add(cx, cx->typeLifoAlloc().new_(site)); } /* Constraints for arithmetic operations. */ @@ -658,7 +658,7 @@ public: void TypeSet::addArith(JSContext *cx, TypeSet *target, TypeSet *other) { - add(cx, ArenaNew(cx->compartment->pool, target, other)); + add(cx, cx->typeLifoAlloc().new_(target, other)); } /* Subset constraint which transforms primitive values into appropriate objects. */ @@ -678,7 +678,7 @@ public: void TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target) { - add(cx, ArenaNew(cx->compartment->pool, script, target)); + add(cx, cx->typeLifoAlloc().new_(script, target)); } /* @@ -709,7 +709,7 @@ TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type if (JSOp(*callpc) == JSOP_NEW) return; - add(cx, ArenaNew(cx->compartment->pool, script, callpc, type, types)); + add(cx, cx->typeLifoAlloc().new_(script, callpc, type, types)); } /* Subset constraint which filters out primitive types. */ @@ -752,7 +752,7 @@ public: void TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter) { - add(cx, ArenaNew(cx->compartment->pool, target, filter)); + add(cx, cx->typeLifoAlloc().new_(target, filter)); } /* If id is a normal slotful 'own' property of an object, get its shape. */ @@ -876,7 +876,7 @@ public: void TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target) { - add(cx, ArenaNew(cx->compartment->pool, script, pc, target)); + add(cx, cx->typeLifoAlloc().new_(script, pc, target)); } /* @@ -904,7 +904,7 @@ public: void TypeSet::addLazyArguments(JSContext *cx, TypeSet *target) { - add(cx, ArenaNew(cx->compartment->pool, target)); + add(cx, cx->typeLifoAlloc().new_(target)); } ///////////////////////////////////////////////////////////////////// @@ -1086,9 +1086,8 @@ TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type) if (!types->hasPropagatedProperty()) object->getFromPrototypes(cx, id, types); /* Bypass addPropagateThis, we already have the callpc. */ - types->add(cx, ArenaNew(cx->compartment->pool, - script, callpc, type, - (TypeSet *) NULL)); + types->add(cx, cx->typeLifoAlloc().new_( + script, callpc, type, (TypeSet *) NULL)); } } } @@ -1389,8 +1388,8 @@ public: void TypeSet::addFreeze(JSContext *cx) { - add(cx, ArenaNew(cx->compartment->pool, - cx->compartment->types.compiledScript), false); + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledScript), false); } /* @@ -1473,8 +1472,8 @@ TypeSet::getKnownTypeTag(JSContext *cx) JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN); if (cx->compartment->types.compiledScript && (empty || type != JSVAL_TYPE_UNKNOWN)) { - add(cx, ArenaNew(cx->compartment->pool, - cx->compartment->types.compiledScript), false); + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledScript), false); } return type; @@ -1553,9 +1552,8 @@ public: TypeSet *types = object->getProperty(cx, JSID_EMPTY, false); if (!types) return; - types->add(cx, - ArenaNew(cx->compartment->pool, - script, flags, &marked), false); + types->add(cx, cx->typeLifoAlloc().new_( + script, flags, &marked), false); return; } } else { @@ -1596,8 +1594,8 @@ TypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags) * Watch for new objects of different kind, and re-traverse existing types * in this set to add any needed FreezeArray constraints. */ - add(cx, ArenaNew(cx->compartment->pool, - cx->compartment->types.compiledScript, flags)); + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledScript, flags)); return false; } @@ -1611,9 +1609,8 @@ TypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags TypeSet *types = object->getProperty(cx, JSID_EMPTY, false); if (!types) return true; - types->add(cx, - ArenaNew(cx->compartment->pool, - cx->compartment->types.compiledScript, flags), false); + types->add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledScript, flags), false); return false; } @@ -1694,9 +1691,9 @@ TypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj) * Use a constraint which triggers recompilation when markStateChange is * called, which will set 'force' to true. */ - types->add(cx, ArenaNew(cx->compartment->pool, - cx->compartment->types.compiledScript, - 0)); + types->add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledScript, + 0)); } class TypeConstraintFreezeOwnProperty : public TypeConstraint @@ -1749,7 +1746,7 @@ TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) if (isOwnProperty(configurable)) return true; - add(cx, ArenaNew(cx->compartment->pool, + add(cx, cx->typeLifoAlloc().new_( cx->compartment->types.compiledScript, configurable), false); return false; @@ -1761,8 +1758,8 @@ TypeSet::knownNonEmpty(JSContext *cx) if (baseFlags() != 0 || baseObjectCount() != 0) return true; - add(cx, ArenaNew(cx->compartment->pool, - cx->compartment->types.compiledScript), false); + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledScript), false); return false; } @@ -1821,7 +1818,7 @@ TypeSet::getSingleton(JSContext *cx, bool freeze) return NULL; if (freeze) { - add(cx, ArenaNew(cx->compartment->pool, + add(cx, cx->typeLifoAlloc().new_( cx->compartment->types.compiledScript), false); } @@ -1879,9 +1876,8 @@ TypeSet::hasGlobalObject(JSContext *cx, JSObject *global) return false; } - add(cx, ArenaNew(cx->compartment->pool, - cx->compartment->types.compiledScript, - global), false); + add(cx, cx->typeLifoAlloc().new_( + cx->compartment->types.compiledScript, global), false); return true; } @@ -2309,8 +2305,7 @@ ScriptAnalysis::addTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *tar InferSpewColor(target), target, InferSpewColorReset(), TypeString(type)); - barrier = ArenaNew(cx->compartment->pool, target, type, - (JSObject *) NULL, JSID_VOID); + barrier = cx->typeLifoAlloc().new_(target, type, (JSObject *) NULL, JSID_VOID); barrier->next = code.typeBarriers; code.typeBarriers = barrier; @@ -2335,8 +2330,7 @@ ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, Typ InferSpewColor(target), target, InferSpewColorReset(), (void *) singleton, TypeIdString(singletonId)); - TypeBarrier *barrier = - ArenaNew(cx->compartment->pool, target, Type::UndefinedType(), + TypeBarrier *barrier = cx->typeLifoAlloc().new_(target, Type::UndefinedType(), singleton, singletonId); barrier->next = code.typeBarriers; @@ -2701,7 +2695,7 @@ bool TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop) { JS_ASSERT(!*pprop); - Property *base = ArenaNew(cx->compartment->pool, id); + Property *base = cx->typeLifoAlloc().new_(id); if (!base) { cx->compartment->types.setPendingNukeTypes(cx); return false; @@ -3251,7 +3245,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, if (ExtendedDef(pc)) defCount++; - TypeSet *pushed = ArenaArray(cx->compartment->pool, defCount); + TypeSet *pushed = cx->typeLifoAlloc().newArrayUninitialized(defCount); if (!pushed) return false; PodZero(pushed, defCount); @@ -3774,7 +3768,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, /* Construct the base call information about this site. */ unsigned argCount = GetUseCount(script, offset) - 2; - TypeCallsite *callsite = ArenaNew(cx->compartment->pool, + TypeCallsite *callsite = cx->typeLifoAlloc().new_( cx, script, pc, op == JSOP_NEW, argCount); if (!callsite || (argCount && !callsite->argumentTypes)) { cx->compartment->types.setPendingNukeTypes(cx); @@ -4496,8 +4490,7 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO if (!parentTypes || parentTypes->unknown()) return false; parentObject->getFromPrototypes(cx, id, parentTypes); - parentTypes->add(cx, - ArenaNew(cx->compartment->pool, type)); + parentTypes->add(cx, cx->typeLifoAlloc().new_(type)); } else if (op == JSOP_FUNCALL && uses->u.which == GET_ARGC(pc) - 1) { /* * Passed as the first parameter to Function.call. Follow control @@ -4546,9 +4539,9 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO * should the Function.call or callee itself change in the future. */ analysis->pushedTypes(calleev.pushedOffset(), 0)->add(cx, - ArenaNew(cx->compartment->pool, type)); + cx->typeLifoAlloc().new_(type)); analysis->pushedTypes(calleev.pushedOffset(), 1)->add(cx, - ArenaNew(cx->compartment->pool, type)); + cx->typeLifoAlloc().new_(type)); TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, uses->offset); if (!initializerList->append(pushframe)) { @@ -5424,7 +5417,7 @@ JSScript::makeAnalysis(JSContext *cx) AutoEnterAnalysis enter(cx); - types->analysis = ArenaNew(cx->compartment->pool, this); + types->analysis = cx->typeLifoAlloc().new_(this); if (!types->analysis) return false; @@ -5815,7 +5808,7 @@ TypeObject::sweep(JSContext *cx) for (unsigned i = 0; i < oldCapacity; i++) { Property *prop = oldArray[i]; if (prop && prop->types.isOwnProperty(false)) { - Property *newProp = ArenaNew(compartment->pool, *prop); + Property *newProp = compartment->typeLifoAlloc.new_(*prop); if (newProp) { Property **pentry = HashSetInsert @@ -5835,7 +5828,7 @@ TypeObject::sweep(JSContext *cx) } else if (propertyCount == 1) { Property *prop = (Property *) propertySet; if (prop->types.isOwnProperty(false)) { - Property *newProp = ArenaNew(compartment->pool, *prop); + Property *newProp = compartment->typeLifoAlloc.new_(*prop); if (newProp) { propertySet = (Property **) newProp; newProp->types.sweep(cx, compartment); @@ -6109,7 +6102,7 @@ JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment, * by being copied to the replacement pool. This memory will be counted too * and deducted from the amount of temporary data. */ - stats->temporary += ArenaAllocatedSize(compartment->pool); + stats->temporary += compartment->typeLifoAlloc.used(); /* Pending arrays are cleared on GC along with the analysis pool. */ stats->temporary += sizeof(TypeCompartment::PendingWork) * compartment->types.pendingCapacity; diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index f36d3795a76..dc1a731c297 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -43,29 +43,16 @@ #define jsinfer_h___ #include "jsalloc.h" -#include "jsarena.h" #include "jscell.h" #include "jstl.h" #include "jsprvtd.h" #include "jshashtable.h" -namespace js { - class CallArgs; - namespace analyze { - class ScriptAnalysis; - } - class GlobalObject; -} +#include "ds/LifoAlloc.h" namespace js { namespace types { -/* Forward declarations. */ -class TypeSet; -struct TypeCallsite; -struct TypeObject; -struct TypeCompartment; - /* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */ struct TypeObjectKey { static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; } diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 8253f6ebcac..a45bc87adb8 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -820,7 +820,7 @@ HashSetInsertTry(JSCompartment *compartment, U **&values, unsigned &count, T key return &values[insertpos]; } - U **newValues = ArenaArray(compartment->pool, newCapacity); + U **newValues = compartment->typeLifoAlloc.newArray(newCapacity); if (!newValues) return NULL; PodZero(newValues, newCapacity); @@ -861,7 +861,7 @@ HashSetInsert(JSCompartment *compartment, U **&values, unsigned &count, T key) if (KEY::getKey(oldData) == key) return (U **) &values; - values = ArenaArray(compartment->pool, SET_ARRAY_SIZE); + values = compartment->typeLifoAlloc.newArray(SET_ARRAY_SIZE); if (!values) { values = (U **) oldData; return NULL; @@ -1097,7 +1097,7 @@ TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc, thisTypes(NULL), returnTypes(NULL) { /* Caller must check for failure. */ - argumentTypes = ArenaArray(cx->compartment->pool, argumentCount); + argumentTypes = cx->typeLifoAlloc().newArray(argumentCount); } ///////////////////////////////////////////////////////////////////// diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 5ba1fa86ac9..57ec91c0238 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -46,7 +46,6 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jsapi.h" diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index cddaae5021f..822cae80ca8 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -45,7 +45,6 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" -#include "jsarena.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 0b7c1df0e57..a7b0594edaa 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -45,7 +45,6 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" #include "jsbit.h" #include "jsutil.h" #include "jshash.h" diff --git a/js/src/json.cpp b/js/src/json.cpp index 84b3d916601..3816c03faaa 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -41,7 +41,6 @@ #include #include "jsapi.h" -#include "jsarena.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 18a4975fe5b..0bbd462797d 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -50,7 +50,6 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jsapi.h" @@ -319,9 +318,7 @@ js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc else SprintCString(sp, " "); } - len = js_Disassemble1(cx, script, next, - next - script->code, - lines, sp); + len = js_Disassemble1(cx, script, next, next - script->code, lines, sp); if (!len) return JS_FALSE; next += len; @@ -338,24 +335,22 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp) JS_FRIEND_API(JSBool) js_DumpPC(JSContext *cx) { - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, &sprinter); fprintf(stdout, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); return ok; } JSBool js_DumpScript(JSContext *cx, JSScript *script) { - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); JSBool ok = js_Disassemble(cx, script, true, &sprinter); fprintf(stdout, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); return ok; } @@ -367,13 +362,13 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) { if (JSVAL_IS_STRING(v)) { Sprinter sprinter; - void *mark = JS_ARENA_MARK(&cx->tempPool); - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + LifoAlloc &tla = cx->tempLifoAlloc(); + LifoAllocScope las(&tla); + INIT_SPRINTER(cx, &sprinter, &tla, 0); char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"'); if (!nbytes) return false; nbytes = JS_sprintf_append(NULL, "%s", nbytes); - JS_ARENA_RELEASE(&cx->tempPool, mark); if (!nbytes) return false; bytes->initBytes(nbytes); @@ -687,11 +682,10 @@ SprintEnsureBuffer(Sprinter *sp, size_t len) if (nb < 0) return JS_TRUE; base = sp->base; - if (!base) { - JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb); - } else { - JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb); - } + if (!base) + base = static_cast(sp->pool->allocUnaligned(nb)); + else + base = static_cast(sp->pool->reallocUnaligned(base, sp->size, nb)); if (!base) { js_ReportOutOfMemory(sp->context); return JS_FALSE; @@ -876,16 +870,11 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) JSString * js_QuoteString(JSContext *cx, JSString *str, jschar quote) { - void *mark; + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; - char *bytes; - JSString *escstr; - - mark = JS_ARENA_MARK(&cx->tempPool); - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - bytes = QuoteString(&sprinter, str, quote); - escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; - JS_ARENA_RELEASE(&cx->tempPool, mark); + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + char *bytes = QuoteString(&sprinter, str, quote); + JSString *escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; return escstr; } @@ -893,7 +882,7 @@ js_QuoteString(JSContext *cx, JSString *str, jschar quote) struct JSPrinter { Sprinter sprinter; /* base class state */ - JSArenaPool pool; /* string allocation pool */ + LifoAlloc pool; /* string allocation pool */ uintN indent; /* indentation in spaces */ bool pretty; /* pretty-print: indent, use newlines */ bool grouped; /* in parenthesized expression context */ @@ -909,13 +898,11 @@ JSPrinter * js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, uintN indent, JSBool pretty, JSBool grouped, JSBool strict) { - JSPrinter *jp; - - jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter)); + JSPrinter *jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter)); if (!jp) return NULL; INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); - JS_InitArenaPool(&jp->pool, name, 256, 1); + new (&jp->pool) LifoAlloc(1024); jp->indent = indent; jp->pretty = !!pretty; jp->grouped = !!grouped; @@ -938,7 +925,7 @@ js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, void js_DestroyPrinter(JSPrinter *jp) { - JS_FinishArenaPool(&jp->pool); + jp->pool.freeAll(); Foreground::delete_(jp->localNames); jp->sprinter.context->free_(jp); } @@ -955,7 +942,7 @@ js_GetPrinterOutput(JSPrinter *jp) str = JS_NewStringCopyZ(cx, jp->sprinter.base); if (!str) return NULL; - JS_FreeArenaPool(&jp->pool); + jp->pool.freeAll(); INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); return str; } @@ -1906,15 +1893,12 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, static JSBool InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) { - size_t offsetsz, opcodesz; - void *space; - - INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP); + INIT_SPRINTER(cx, &ss->sprinter, &cx->tempLifoAlloc(), PAREN_SLOP); /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ - offsetsz = depth * sizeof(ptrdiff_t); - opcodesz = depth * sizeof(jsbytecode); - JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); + size_t offsetsz = depth * sizeof(ptrdiff_t); + size_t opcodesz = depth * sizeof(jsbytecode); + void *space = cx->tempLifoAlloc().alloc(offsetsz + opcodesz); if (!space) { js_ReportOutOfMemory(cx); return JS_FALSE; @@ -4064,7 +4048,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) #if JS_HAS_GENERATOR_EXPRS sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_GENEXP) { - void *mark; Vector *innerLocalNames; Vector *outerLocalNames; JSScript *inner, *outer; @@ -4079,7 +4062,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * Therefore after InitSprintStack succeeds, we must * release to mark before returning. */ - mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); if (fun->script()->bindings.hasLocalNames()) { innerLocalNames = cx->new_ >(cx); if (!innerLocalNames || @@ -4091,10 +4074,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) innerLocalNames = NULL; } inner = fun->script(); - if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) { - JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) return NULL; - } ss2.inGenExp = JS_TRUE; /* @@ -4122,10 +4103,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) jp->script = outer; jp->fun = outerfun; jp->localNames = outerLocalNames; - if (!ok) { - JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!ok) return NULL; - } /* * Advance over this op and its global |this| push, and @@ -4195,7 +4174,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * from cx's tempPool. */ rval = JS_strdup(cx, PopStr(&ss2, op)); - JS_ARENA_RELEASE(&cx->tempPool, mark); + las.releaseEarly(); if (!rval) return NULL; todo = SprintCString(&ss->sprinter, rval); @@ -4833,8 +4812,6 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, uintN depth, i; SprintStack ss; JSContext *cx; - void *mark; - JSBool ok; JSScript *oldscript; char *last; @@ -4845,10 +4822,9 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, AutoScriptUntrapper untrapper(cx, script, &pc); /* Initialize a sprinter for use with the offset stack. */ - mark = JS_ARENA_MARK(&cx->tempPool); - ok = InitSprintStack(cx, &ss, jp, depth); - if (!ok) - goto out; + LifoAllocScope las(&cx->tempLifoAlloc()); + if (!InitSprintStack(cx, &ss, jp, depth)) + return false; /* * If we are called from js_DecompileValueGenerator with a portion of @@ -4872,7 +4848,7 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, /* Call recursive subroutine to do the hard work. */ oldscript = jp->script; jp->script = script; - ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL; + bool ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL; jp->script = oldscript; /* If the given code didn't empty the stack, do it now. */ @@ -4883,9 +4859,6 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, js_printf(jp, "%s", last); } -out: - /* Free all temporary stuff allocated under this call. */ - JS_ARENA_RELEASE(&cx->tempPool, mark); return ok; } @@ -4998,7 +4971,6 @@ js_DecompileFunction(JSPrinter *jp) JSScript *script = fun->script(); #if JS_HAS_DESTRUCTURING SprintStack ss; - void *mark; #endif /* Print the parameters. */ @@ -5010,7 +4982,7 @@ js_DecompileFunction(JSPrinter *jp) #if JS_HAS_DESTRUCTURING ss.printer = NULL; jp->script = script; - mark = JS_ARENA_MARK(&jp->sprinter.context->tempPool); + LifoAllocScope las(&jp->sprinter.context->tempLifoAlloc()); #endif for (i = 0; i < fun->nargs; i++) { @@ -5061,7 +5033,7 @@ js_DecompileFunction(JSPrinter *jp) #if JS_HAS_DESTRUCTURING jp->script = NULL; - JS_ARENA_RELEASE(&jp->sprinter.context->tempPool, mark); + las.releaseEarly(); #endif if (!ok) return JS_FALSE; diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 89107d97dbe..67a4b92606e 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -46,7 +46,6 @@ #include "jsprvtd.h" #include "jspubtd.h" #include "jsutil.h" -#include "jsarena.h" JS_BEGIN_EXTERN_C @@ -505,7 +504,7 @@ DecompileValueGenerator(JSContext *cx, intN spindex, const Value &v, */ struct Sprinter { JSContext *context; /* context executing the decompiler */ - JSArenaPool *pool; /* string allocation pool */ + LifoAlloc *pool; /* string allocation pool */ char *base; /* base address of buffer in pool */ size_t size; /* size of buffer allocated at base */ ptrdiff_t offset; /* offset of next free char in buffer */ diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 7b5616fb7e4..dd741e026ac 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -56,7 +56,6 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsapi.h" #include "jsarray.h" @@ -209,9 +208,9 @@ Parser::init(const jschar *base, size_t length, const char *filename, uintN line JSContext *cx = context; if (!cx->ensureParseMapPool()) return false; - tempPoolMark = JS_ARENA_MARK(&cx->tempPool); + tempPoolMark = cx->tempLifoAlloc().mark(); if (!tokenStream.init(base, length, filename, lineno, version)) { - JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark); + cx->tempLifoAlloc().release(tempPoolMark); return false; } return true; @@ -223,7 +222,7 @@ Parser::~Parser() if (principals) JSPRINCIPALS_DROP(cx, principals); - JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark); + cx->tempLifoAlloc().release(tempPoolMark); cx->activeCompilations--; } @@ -247,8 +246,7 @@ Parser::newObjectBox(JSObject *obj) * containing the entries must be alive until we are done with scanning, * parsing and code generation for the whole script or top-level function. */ - JSObjectBox *objbox; - JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool); + JSObjectBox *objbox = context->tempLifoAlloc().new_(); if (!objbox) { js_ReportOutOfMemory(context); return NULL; @@ -273,8 +271,7 @@ Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc) * containing the entries must be alive until we are done with scanning, * parsing and code generation for the whole script or top-level function. */ - JSFunctionBox *funbox; - JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool); + JSFunctionBox *funbox = context->tempLifoAlloc().newPod(); if (!funbox) { js_ReportOutOfMemory(context); return NULL; @@ -677,8 +674,7 @@ NewOrRecycledNode(JSTreeContext *tc) pn = tc->parser->nodeList; if (!pn) { JSContext *cx = tc->parser->context; - - JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); + pn = cx->tempLifoAlloc().new_(); if (!pn) js_ReportOutOfMemory(cx); } else { diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 705cdae0592..57e1226e54b 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -159,6 +159,8 @@ class UpvarCookie; class TempAllocPolicy; class RuntimeAllocPolicy; +class GlobalObject; + template @@ -183,6 +185,8 @@ template class InlineMap; +class LifoAlloc; + class PropertyCache; struct PropertyCacheEntry; @@ -210,6 +214,26 @@ typedef JSPropertyOp PropertyOp; typedef JSStrictPropertyOp StrictPropertyOp; typedef JSPropertyDescriptor PropertyDescriptor; +namespace analyze { + +struct LifetimeVariable; +class LoopAnalysis; +class ScriptAnalysis; +class SlotValue; +class SSAValue; +class SSAUseChain; + +} /* namespace analyze */ + +namespace types { + +class TypeSet; +struct TypeCallsite; +struct TypeObject; +struct TypeCompartment; + +} /* namespace types */ + } /* namespace js */ } /* export "C++" */ diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index d2dbf8d344e..4b3ad5f0987 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -149,7 +149,6 @@ class RegExp void reportPCREError(JSContext *cx, int error); #endif void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error); - static inline bool initArena(JSContext *cx); static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount); static JSObject *createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount); inline bool executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, @@ -255,27 +254,6 @@ class RegExpMatchBuilder /* RegExp inlines. */ -inline bool -RegExp::initArena(JSContext *cx) -{ - if (cx->regExpPool.first.next) - return true; - - /* - * The regular expression arena pool is special... we want to hang on to it - * until a GC is performed so rapid subsequent regexp executions don't - * thrash malloc/freeing arena chunks. - * - * Stick a timestamp at the base of that pool. - */ - int64 *timestamp; - JS_ARENA_ALLOCATE_CAST(timestamp, int64 *, &cx->regExpPool, sizeof *timestamp); - if (!timestamp) - return false; - *timestamp = JS_Now(); - return true; -} - inline void RegExp::checkMatchPairs(JSString *input, int *buf, size_t matchItemCount) { @@ -339,11 +317,8 @@ RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inputstr, const size_t bufCount = pairCount * 3; /* Should be x2, but PCRE has... needs. */ const size_t matchItemCount = pairCount * 2; - if (!initArena(cx)) - return false; - - AutoArenaAllocator aaa(&cx->regExpPool); - int *buf = aaa.alloc(bufCount); + LifoAllocScope las(&cx->tempLifoAlloc()); + int *buf = cx->tempLifoAlloc().newArray(bufCount); if (!buf) return false; diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 338147b8098..54f3e9e6300 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -54,7 +54,6 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" #include "jsbit.h" #include "jsutil.h" #include "jsprf.h" diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 00586eeeb7e..d8fdfcec992 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -46,7 +46,6 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" #include "jsbit.h" #include "jsclist.h" #include "jsdhash.h" diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index d51ea6e3fbf..083714c9a45 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -384,14 +384,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) Bindings bindings(cx); uint32 nameCount = nargs + nvars + nupvars; if (nameCount > 0) { - struct AutoMark { - JSArenaPool * const pool; - void * const mark; - AutoMark(JSArenaPool *pool) : pool(pool), mark(JS_ARENA_MARK(pool)) { } - ~AutoMark() { - JS_ARENA_RELEASE(pool, mark); - } - } automark(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); /* * To xdr the names we prefix the names with a bitmap descriptor and @@ -402,9 +395,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) * name is declared as const, not as ordinary var. * */ uintN bitmapLength = JS_HOWMANY(nameCount, JS_BITS_PER_UINT32); - uint32 *bitmap; - JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &cx->tempPool, - bitmapLength * sizeof *bitmap); + uint32 *bitmap = cx->tempLifoAlloc().newArray(bitmapLength); if (!bitmap) { js_ReportOutOfMemory(cx); return false; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 595cc3e06cb..7df2fcb8d95 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -146,8 +146,6 @@ typedef struct JSConstArray { uint32 length; } JSConstArray; -struct JSArenaPool; - namespace js { struct GlobalSlotArray { diff --git a/js/src/jstl.h b/js/src/jstl.h index 81cbc62a0be..d62b45d6392 100644 --- a/js/src/jstl.h +++ b/js/src/jstl.h @@ -40,7 +40,7 @@ #ifndef jstl_h_ #define jstl_h_ -#include "jspubtd.h" +#include "jsprvtd.h" #include "jsbit.h" #include "jsstaticcheck.h" #include "jsstdint.h" @@ -167,6 +167,7 @@ template <> struct IsPodType { static const bool result = template <> struct IsPodType { static const bool result = true; }; template <> struct IsPodType { static const bool result = true; }; template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; template struct IsPodType { static const bool result = true; }; /* Return the size/end of an array without using macros. */ diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 5d128f5bc2e..5fe8323f5bd 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -7390,9 +7390,9 @@ TraceRecorder::monitorRecording(JSOp op) debug_only_stmt( if (LogController.lcbits & LC_TMRecorder) { - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); debug_only_print0(LC_TMRecorder, "\n"); js_Disassemble1(cx, cx->fp()->script(), cx->regs().pc, @@ -7401,7 +7401,6 @@ TraceRecorder::monitorRecording(JSOp op) !cx->fp()->hasImacropc(), &sprinter); fprintf(stdout, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); } ) @@ -10415,14 +10414,13 @@ TraceRecorder::record_EnterFrame() callDepth); debug_only_stmt( if (LogController.lcbits & LC_TMRecorder) { - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); js_Disassemble(cx, cx->fp()->script(), JS_TRUE, &sprinter); debug_only_printf(LC_TMTracer, "%s", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); debug_only_print0(LC_TMTracer, "----\n"); } ) diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 68de3a24b5a..473f9add429 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -70,6 +70,8 @@ using namespace mozilla; JS_BEGIN_EXTERN_C +#define JS_UPTRDIFF(a_, b_) (uintptr_t(a_) - uintptr_t(b_)) + #define JS_CRASH_UNLESS(__cond) \ JS_BEGIN_MACRO \ if (!(__cond)) { \ diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index cff6d4e3fa2..25a8ef41083 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -1462,13 +1462,12 @@ public: JS_BEGIN_MACRO \ if (IsJaegerSpewChannelActive(JSpew_JSOps)) { \ JaegerSpew(JSpew_JSOps, " %2d ", frame.stackDepth()); \ - void *mark = JS_ARENA_MARK(&cx->tempPool); \ + LifoAllocScope las(&cx->tempLifoAlloc()); \ Sprinter sprinter; \ - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); \ + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); \ js_Disassemble1(cx, script, PC, PC - script->code, \ JS_TRUE, &sprinter); \ fprintf(stdout, "%s", sprinter.base); \ - JS_ARENA_RELEASE(&cx->tempPool, mark); \ } \ JS_END_MACRO; #else @@ -6683,7 +6682,7 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slow, bool *tramp if (cx->typeInferenceEnabled()) { RegisterAllocation *&alloc = analysis->getAllocation(target); if (!alloc) { - alloc = ArenaNew(cx->compartment->pool, false); + alloc = cx->typeLifoAlloc().new_(false); if (!alloc) return false; } diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 6373a3101ae..c0bd80da60d 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -575,7 +575,7 @@ RegisterAllocation * FrameState::computeAllocation(jsbytecode *target) { JS_ASSERT(cx->typeInferenceEnabled()); - RegisterAllocation *alloc = ArenaNew(cx->compartment->pool, false); + RegisterAllocation *alloc = cx->typeLifoAlloc().new_(false); if (!alloc) return NULL; @@ -853,7 +853,7 @@ FrameState::discardForJoin(RegisterAllocation *&alloc, uint32 stackDepth) * This shows up for loop entries which are not reachable from the * loop head, and for exception, switch target and trap safe points. */ - alloc = ArenaNew(cx->compartment->pool, false); + alloc = cx->typeLifoAlloc().new_(false); if (!alloc) return false; } diff --git a/js/src/methodjit/LoopState.cpp b/js/src/methodjit/LoopState.cpp index e04d4abf513..4777446f2d7 100644 --- a/js/src/methodjit/LoopState.cpp +++ b/js/src/methodjit/LoopState.cpp @@ -157,7 +157,7 @@ LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget) RegisterAllocation *&alloc = outerAnalysis->getAllocation(head); JS_ASSERT(!alloc); - alloc = ArenaNew(cx->compartment->pool, true); + alloc = cx->typeLifoAlloc().new_(true); if (!alloc) return false; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index bab2454c7eb..56c478c6354 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -50,7 +50,6 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jswrapper.h" @@ -1955,16 +1954,13 @@ SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp) static JSBool Notes(JSContext *cx, uintN argc, jsval *vp) { - uintN i; - JSScript *script; - - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); jsval *argv = JS_ARGV(cx, vp); - for (i = 0; i < argc; i++) { - script = ValueToScript(cx, argv[i]); + for (uintN i = 0; i < argc; i++) { + JSScript *script = ValueToScript(cx, argv[i]); if (!script) continue; @@ -1972,7 +1968,6 @@ Notes(JSContext *cx, uintN argc, jsval *vp) } JSString *str = JS_NewStringCopyZ(cx, sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); if (!str) return JS_FALSE; JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); @@ -2113,9 +2108,9 @@ DisassembleToString(JSContext *cx, uintN argc, jsval *vp) if (!p.parse(cx)) return false; - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); Sprinter *sp = &sprinter; bool ok = true; @@ -2139,7 +2134,6 @@ DisassembleToString(JSContext *cx, uintN argc, jsval *vp) } JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.base) : NULL; - JS_ARENA_RELEASE(&cx->tempPool, mark); if (!str) return false; JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); @@ -2153,9 +2147,9 @@ Disassemble(JSContext *cx, uintN argc, jsval *vp) if (!p.parse(cx)) return false; - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); Sprinter *sp = &sprinter; bool ok = true; @@ -2180,7 +2174,6 @@ Disassemble(JSContext *cx, uintN argc, jsval *vp) if (ok) fprintf(stdout, "%s\n", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); JS_SET_RVAL(cx, vp, JSVAL_VOID); return ok; } @@ -2216,13 +2209,12 @@ DisassFile(JSContext *cx, uintN argc, jsval *vp) if (!script) return false; - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); bool ok = DisassembleScript(cx, script, NULL, p.lines, p.recursive, &sprinter); if (ok) fprintf(stdout, "%s\n", sprinter.base); - JS_ARENA_RELEASE(&cx->tempPool, mark); if (!ok) return false; @@ -2266,18 +2258,17 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp) pc = script->code; end = pc + script->length; - void *mark = JS_ARENA_MARK(&cx->tempPool); + LifoAllocScope las(&cx->tempLifoAlloc()); Sprinter sprinter; Sprinter *sp = &sprinter; - INIT_SPRINTER(cx, sp, &cx->tempPool, 0); + INIT_SPRINTER(cx, sp, &cx->tempLifoAlloc(), 0); /* burn the leading lines */ line2 = JS_PCToLineNumber(cx, script, pc); for (line1 = 0; line1 < line2 - 1; line1++) { char *tmp = fgets(linebuf, LINE_BUF_LEN, file); if (!tmp) { - JS_ReportError(cx, "failed to read %s fully", - script->filename); + JS_ReportError(cx, "failed to read %s fully", script->filename); ok = JS_FALSE; goto bail; } @@ -2318,7 +2309,6 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp) } bail: - JS_ARENA_RELEASE(&cx->tempPool, mark); fclose(file); } JS_SET_RVAL(cx, vp, JSVAL_VOID); diff --git a/mfbt/Util.h b/mfbt/Util.h index 8a343b2baae..65b2e2c8ee9 100644 --- a/mfbt/Util.h +++ b/mfbt/Util.h @@ -183,6 +183,7 @@ struct DebugOnly DebugOnly& operator=(const T&) { return *this; } void operator++(int) {} void operator--(int) {} + bool operator<(const T&) { return false; } #endif /*