Bug 684039: rewrite JS LIFO allocator, avoids thrashing. (r=luke)
authorChris Leary <cdleary@mozilla.com>
Thu, 22 Sep 2011 13:22:30 -0700
changeset 79111 4d10127fd1068193bec6fa49cd5d082dfc8db40c
parent 79110 d709c25c0e1fa916629f1366c51e6ae02f26c4cf
child 79112 01f5a405be12b53edf7e31fba3d5ab5d7f35ba89
push idunknown
push userunknown
push dateunknown
reviewersluke
bugs684039
milestone10.0a1
Bug 684039: rewrite JS LIFO allocator, avoids thrashing. (r=luke)
caps/src/nsScriptSecurityManager.cpp
js/src/Makefile.in
js/src/ds/LifoAlloc.cpp
js/src/ds/LifoAlloc.h
js/src/frontend/ParseMaps.cpp
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi.cpp
js/src/jsarena.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/json.cpp
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jsparse.cpp
js/src/jsprvtd.h
js/src/jsregexpinlines.h
js/src/jsscan.cpp
js/src/jsscope.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jstl.h
js/src/jstracer.cpp
js/src/jsutil.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/FrameState.cpp
js/src/methodjit/LoopState.cpp
js/src/shell/js.cpp
mfbt/Util.h
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -54,17 +54,16 @@
 #include "nsNullPrincipal.h"
 #include "nsXPIDLString.h"
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
 #include "nsIJSContextStack.h"
 #include "nsDOMError.h"
 #include "nsDOMCID.h"
 #include "jsdbgapi.h"
-#include "jsarena.h"
 #include "jsfun.h"
 #include "jsobj.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCSecurityManager.h"
 #include "nsTextFormatter.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
 #include "nsIProperties.h"
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -96,17 +96,16 @@ FORCE_STATIC_LIB = 1
 DIST_INSTALL = 1
 
 VPATH		= $(srcdir)
 
 CPPSRCS		= \
 		jsalloc.cpp \
 		jsanalyze.cpp \
 		jsapi.cpp \
-		jsarena.cpp \
 		jsarray.cpp \
 		jsatom.cpp \
 		jsbool.cpp \
 		jsclone.cpp \
 		jscntxt.cpp \
 		jscompartment.cpp \
 		jsdate.cpp \
 		jsdbgapi.cpp \
@@ -156,30 +155,30 @@ CPPSRCS		= \
 		prmjtime.cpp \
 		sharkctl.cpp \
 		CallObject.cpp \
 		Debugger.cpp \
 		GlobalObject.cpp \
 		Stack.cpp \
 		String.cpp \
 		ParseMaps.cpp \
+		LifoAlloc.cpp \
 		Unicode.cpp \
 		$(NULL)
 
 # Changes to internal header files, used externally, massively slow down
 # browser builds.  Don't add new files here unless you know what you're
 # doing!
 INSTALLED_HEADERS = \
 		js-config.h \
 		jsautocfg.h \
 		$(CURDIR)/jsautokw.h \
 		js.msg \
 		jsalloc.h \
 		jsapi.h \
-		jsarena.h \
 		jsatom.h \
 		jsbit.h \
 		jsclass.h \
 		jsclist.h \
 		jsclone.h \
 		jscntxt.h \
 		jscompat.h \
 		jscrashreport.h \
@@ -236,30 +235,34 @@ INSTALLED_HEADERS = \
 		$(NULL)
 
 ######################################################
 # BEGIN include sources for the engine subdirectories
 #
 VPATH		+= \
 		$(srcdir)/vm \
 		$(srcdir)/frontend \
+		$(srcdir)/ds \
 		$(NULL)
 
-EXPORTS_NAMESPACES = vm
+EXPORTS_NAMESPACES = vm ds
 
 EXPORTS_vm = \
 		ArgumentsObject.h \
 		CallObject.h \
 		GlobalObject.h \
 		Stack.h \
 		String.h \
 		StringObject.h \
 		Unicode.h  \
 		$(NULL)
 
+EXPORTS_ds = \
+		LifoAlloc.h
+
 ###############################################
 # BEGIN include sources for low-level code shared with Gecko
 #
 VPATH		+= \
 		$(srcdir)/../../mfbt \
 		$(NULL)
 
 EXPORTS_NAMESPACES += mozilla
new file mode 100644
--- /dev/null
+++ b/js/src/ds/LifoAlloc.cpp
@@ -0,0 +1,149 @@
+#include "LifoAlloc.h"
+
+#include <new>
+
+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;
+}
rename from js/src/jsarena.h
rename to js/src/ds/LifoAlloc.h
--- a/js/src/jsarena.h
+++ b/js/src/ds/LifoAlloc.h
@@ -1,317 +1,335 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- 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 Communicator client code, released
- * March 31, 1998.
+ * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
+ * June 12, 2009.
  *
  * 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.
+ *   the Mozilla Corporation.
  *
  * Contributor(s):
+ *   Chris Leary <cdleary@mozilla.com>
  *
  * 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 <stdlib.h>
-#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)
+#ifndef LifoAlloc_h__
+#define LifoAlloc_h__
 
 /*
- * 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.
+ * 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.
  */
-#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
+#include "jsutil.h"
+#include "jstl.h"
 
 namespace js {
 
-template <typename T>
-inline T *
-ArenaArray(JSArenaPool &pool, unsigned count)
-{
-    void *v;
-    JS_ARENA_ALLOCATE(v, &pool, count * sizeof(T));
-    return (T *) v;
-}
+namespace detail {
+
+static const size_t LIFO_ALLOC_ALIGN = 8;
 
-template <typename T>
-inline T *
-ArenaNew(JSArenaPool &pool)
+JS_ALWAYS_INLINE
+char *
+AlignPtr(void *orig)
 {
-    void *v;
-    JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
-    return v ? new (v) T() : NULL;
-}
+    typedef tl::StaticAssert<
+        tl::FloorLog2<LIFO_ALLOC_ALIGN>::result == tl::CeilingLog2<LIFO_ALLOC_ALIGN>::result
+    >::result _;
 
-template <typename T, typename A>
-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 <typename T, typename A, typename B>
-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;
+    char *result = (char *) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & -LIFO_ALLOC_ALIGN);
+    JS_ASSERT(uintptr_t(result) % LIFO_ALLOC_ALIGN == 0);
+    return result;
 }
 
-template <typename T, typename A, typename B, typename C>
-inline T *
-ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c)
+/* Header for a chunk of memory wrangled by the LifoAlloc. */
+class BumpChunk
 {
-    void *v;
-    JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
-    return v ? new (v) T(a, b, c) : NULL;
-}
+    char        *bump;
+    char        *limit;
+    BumpChunk   *next_;
+    size_t      bumpSpaceSize;
+
+    char *base() const { return limit - bumpSpaceSize; }
+
+    explicit BumpChunk(size_t bumpSpaceSize)
+      : bump(reinterpret_cast<char *>(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<char *> prevBump = bump;
+        bump = static_cast<char *>(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<char *>(this) + sizeof(BumpChunk));
+    }
+
+    void *mark() const { return bump; }
+
+    void release(void *mark) {
+        JS_ASSERT(contains(mark));
+        JS_ASSERT(mark <= bump);
+        setBump(mark);
+    }
 
-template <typename T, typename A, typename B, typename C, typename D>
-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;
-}
+    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);
 
-template <typename T, typename A, typename B, typename C, typename D, typename E>
-inline T *
-ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c, const D &d, const E &e)
+    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
 {
-    void *v;
-    JS_ARENA_ALLOCATE(v, &pool, sizeof(T));
-    return v ? new (v) T(a, b, c, d, e) : NULL;
-}
+    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 <typename T>
+    T *newArray(size_t count) {
+        void *mem = alloc(sizeof(T) * count);
+        if (!mem)
+            return NULL;
+        JS_STATIC_ASSERT(tl::IsPodType<T>::result);
+        return (T *) mem;
+    }
+
+    /*
+     * Create an array with uninitialized elements of type |T|.
+     * The caller is responsible for initialization.
+     */
+    template <typename T>
+    T *newArrayUninitialized(size_t count) {
+        return static_cast<T *>(alloc(sizeof(T) * count));
+    }
+
+    void *mark() {
+        markCount++;
 
-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 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;
     }
-    return res;
-}
+
+    /* Doesn't perform construction; useful for lazily-initialized POD types. */
+    template <typename T>
+    JS_ALWAYS_INLINE
+    T *newPod() {
+        return static_cast<T *>(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);
+};
 
-/* 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);
-}
+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 /* __cplusplus */
-
-#endif /* jsarena_h___ */
+#endif
--- a/js/src/frontend/ParseMaps.cpp
+++ b/js/src/frontend/ParseMaps.cpp
@@ -34,16 +34,17 @@
  * 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 ***** */
 
 #include "ParseMaps-inl.h"
+#include "jscompartment.h"
 
 using namespace js;
 
 void
 ParseMapPool::checkInvariants()
 {
     /*
      * Having all values be of the same size permits us to easily reuse the
@@ -120,23 +121,22 @@ DumpAtomDefnMap(const AtomDefnMapPtr &ma
         fprintf(stderr, "defn: %p\n", (void *) r.front().value());
     }
 }
 #endif
 
 AtomDeclNode *
 AtomDecls::allocNode(JSDefinition *defn)
 {
-    AtomDeclNode *p;
-    JS_ARENA_ALLOCATE_TYPE(p, AtomDeclNode, &cx->tempPool);
+    AtomDeclNode *p = cx->tempLifoAlloc().new_<AtomDeclNode>(defn);
     if (!p) {
         js_ReportOutOfMemory(cx);
         return NULL;
     }
-    return new (p) AtomDeclNode(defn);
+    return p;
 }
 
 bool
 AtomDecls::addShadow(JSAtom *atom, JSDefinition *defn)
 {
     AtomDeclNode *node = allocNode(defn);
     if (!node)
         return false;
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -104,18 +104,17 @@ Bytecode::mergeDefines(JSContext *cx, Sc
             }
             if (!found) {
                 /*
                  * Get a mutable copy of the defines.  This can end up making
                  * several copies for a bytecode if it has many incoming edges
                  * with progressively smaller sets of defined variables.
                  */
                 if (!owned) {
-                    uint32 *reallocArray =
-                        ArenaArray<uint32>(cx->compartment->pool, defineCount);
+                    uint32 *reallocArray = cx->typeLifoAlloc().newArray<uint32>(defineCount);
                     if (!reallocArray) {
                         script->setOOM(cx);
                         return false;
                     }
                     memcpy(reallocArray, defineArray, defineCount * sizeof(uint32));
                     defineArray = reallocArray;
                     owned = true;
                 }
@@ -129,22 +128,21 @@ Bytecode::mergeDefines(JSContext *cx, Sc
     return true;
 }
 
 #ifdef DEBUG
 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
 
 /////////////////////////////////////////////////////////////////////
 // Bytecode Analysis
 /////////////////////////////////////////////////////////////////////
 
 inline bool
@@ -152,17 +150,17 @@ ScriptAnalysis::addJump(JSContext *cx, u
                         unsigned *currentOffset, unsigned *forwardJump,
                         unsigned stackDepth, uint32 *defineArray, unsigned defineCount)
 {
     JS_ASSERT(offset < script->length);
 
     Bytecode *&code = codeArray[offset];
     bool initial = (code == NULL);
     if (initial) {
-        code = ArenaNew<Bytecode>(cx->compartment->pool);
+        code = cx->typeLifoAlloc().new_<Bytecode>();
         if (!code) {
             setOOM(cx);
             return false;
         }
     }
 
     if (!code->mergeDefines(cx, this, initial, stackDepth, defineArray, defineCount))
         return false;
@@ -273,26 +271,26 @@ BytecodeNoFallThrough(JSOp op)
     }
 }
 
 void
 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<Bytecode*>(pool, length);
-    definedLocals = ArenaArray<uint32>(pool, script->nfixed);
-    escapedSlots = ArenaArray<JSPackedBool>(pool, numSlots);
+    codeArray = tla.newArray<Bytecode*>(length);
+    definedLocals = tla.newArray<uint32>(script->nfixed);
+    escapedSlots = tla.newArray<JSPackedBool>(numSlots);
 
     if (!codeArray || !definedLocals || !escapedSlots) {
         setOOM(cx);
         return;
     }
 
     PodZero(codeArray, length);
 
@@ -366,17 +364,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
 
     /*
      * If we are in the middle of a try block, the offset of the highest
      * catch/finally/enditer.
      */
     unsigned forwardCatch = 0;
 
     /* Fill in stack depth and definitions at initial bytecode. */
-    Bytecode *startcode = ArenaNew<Bytecode>(pool);
+    Bytecode *startcode = tla.new_<Bytecode>();
     if (!startcode) {
         setOOM(cx);
         return;
     }
 
     startcode->stackDepth = 0;
     codeArray[0] = startcode;
 
@@ -698,17 +696,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
              * defined at a later point in the script, if that definition was in the
              * condition for a loop which then jumped back here.  In such cases we
              * will not treat the variable as ever being defined in the loop body
              * (see setLocal).
              */
             if (definedLocals[local] == LOCAL_CONDITIONALLY_DEFINED) {
                 if (forwardJump) {
                     /* Add this local to the variables defined after this bytecode. */
-                    uint32 *newArray = ArenaArray<uint32>(pool, defineCount + 1);
+                    uint32 *newArray = tla.newArray<uint32>(defineCount + 1);
                     if (!newArray) {
                         setOOM(cx);
                         return;
                     }
                     if (defineCount)
                         memcpy(newArray, defineArray, defineCount * sizeof(uint32));
                     defineArray = newArray;
                     defineArray[defineCount++] = local;
@@ -790,17 +788,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
         /* Handle any fallthrough from this opcode. */
         if (!BytecodeNoFallThrough(op)) {
             JS_ASSERT(successorOffset < script->length);
 
             Bytecode *&nextcode = codeArray[successorOffset];
             bool initial = (nextcode == NULL);
 
             if (initial) {
-                nextcode = ArenaNew<Bytecode>(pool);
+                nextcode = tla.new_<Bytecode>();
                 if (!nextcode) {
                     setOOM(cx);
                     return;
                 }
             }
 
             if (type == JOF_JUMP || type == JOF_JUMPX)
                 nextcode->jumpFallthrough = true;
@@ -834,19 +832,19 @@ ScriptAnalysis::analyzeLifetimes(JSConte
     JS_ASSERT(cx->compartment->activeAnalysis && !ranLifetimes() && !failed());
 
     if (!ranBytecode()) {
         analyzeBytecode(cx);
         if (failed())
             return;
     }
 
-    JSArenaPool &pool = cx->compartment->pool;
+    LifoAlloc &tla = cx->typeLifoAlloc();
 
-    lifetimes = ArenaArray<LifetimeVariable>(pool, numSlots);
+    lifetimes = tla.newArray<LifetimeVariable>(numSlots);
     if (!lifetimes) {
         setOOM(cx);
         return;
     }
     PodZero(lifetimes, numSlots);
 
     /*
      * Variables which are currently dead. On forward branches to locations
@@ -964,17 +962,17 @@ ScriptAnalysis::analyzeLifetimes(JSConte
 
           case JSOP_LOOKUPSWITCH:
           case JSOP_LOOKUPSWITCHX:
           case JSOP_TABLESWITCH:
           case JSOP_TABLESWITCHX:
             /* Restore all saved variables. :FIXME: maybe do this precisely. */
             for (unsigned i = 0; i < savedCount; i++) {
                 LifetimeVariable &var = *saved[i];
-                var.lifetime = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
+                var.lifetime = tla.new_<Lifetime>(offset, var.savedEnd, var.saved);
                 if (!var.lifetime) {
                     cx->free_(saved);
                     setOOM(cx);
                     return;
                 }
                 var.saved = NULL;
                 saved[i--] = saved[--savedCount];
             }
@@ -1031,17 +1029,17 @@ ScriptAnalysis::analyzeLifetimes(JSConte
                 /*
                  * If we already have a loop, it is an outer loop and we
                  * need to prune the last block in the loop --- we do not
                  * track 'continue' statements for outer loops.
                  */
                 if (loop && loop->entry > loop->lastBlock)
                     loop->lastBlock = loop->entry;
 
-                LoopAnalysis *nloop = ArenaNew<LoopAnalysis>(pool);
+                LoopAnalysis *nloop = tla.new_<LoopAnalysis>();
                 if (!nloop) {
                     cx->free_(saved);
                     setOOM(cx);
                     return;
                 }
                 PodZero(nloop);
 
                 if (loop)
@@ -1080,17 +1078,17 @@ ScriptAnalysis::analyzeLifetimes(JSConte
                 for (unsigned i = 0; i < savedCount; i++) {
                     LifetimeVariable &var = *saved[i];
                     JS_ASSERT(!var.lifetime && var.saved);
                     if (var.live(targetOffset)) {
                         /*
                          * Jumping to a place where this variable is live. Make a new
                          * lifetime segment for the variable.
                          */
-                        var.lifetime = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
+                        var.lifetime = tla.new_<Lifetime>(offset, var.savedEnd, var.saved);
                         if (!var.lifetime) {
                             cx->free_(saved);
                             setOOM(cx);
                             return;
                         }
                         var.saved = NULL;
                         saved[i--] = saved[--savedCount];
                     } else if (loop && !var.savedEnd) {
@@ -1144,34 +1142,34 @@ ScriptAnalysis::addVariable(JSContext *c
             for (unsigned i = 0; i < savedCount; i++) {
                 if (saved[i] == &var) {
                     JS_ASSERT(savedCount);
                     saved[i--] = saved[--savedCount];
                     break;
                 }
             }
         }
-        var.lifetime = ArenaNew<Lifetime>(cx->compartment->pool, offset, var.savedEnd, var.saved);
+        var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
         if (!var.lifetime) {
             setOOM(cx);
             return;
         }
         var.saved = NULL;
     }
 }
 
 inline void
 ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
                              LifetimeVariable **&saved, unsigned &savedCount)
 {
     if (!var.lifetime) {
         /* Make a point lifetime indicating the write. */
         if (!var.saved)
             saved[savedCount++] = &var;
-        var.saved = ArenaNew<Lifetime>(cx->compartment->pool, offset, var.savedEnd, var.saved);
+        var.saved = cx->typeLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
         if (!var.saved) {
             setOOM(cx);
             return;
         }
         var.saved->write = true;
         var.savedEnd = 0;
         return;
     }
@@ -1247,17 +1245,17 @@ ScriptAnalysis::extendVariable(JSContext
             if (segment->end >= end) {
                 /* Variable known to be live after the loop finishes. */
                 break;
             }
             savedEnd = end;
         }
         JS_ASSERT(savedEnd <= end);
         if (savedEnd > segment->end) {
-            Lifetime *tail = ArenaNew<Lifetime>(cx->compartment->pool, savedEnd, 0, segment->next);
+            Lifetime *tail = cx->typeLifoAlloc().new_<Lifetime>(savedEnd, 0, segment->next);
             if (!tail) {
                 setOOM(cx);
                 return;
             }
             tail->start = segment->end;
             tail->loopTail = true;
 
             /*
@@ -1324,17 +1322,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
     JS_ASSERT(cx->compartment->activeAnalysis && !ranSSA() && !failed());
 
     if (!ranLifetimes()) {
         analyzeLifetimes(cx);
         if (failed())
             return;
     }
 
-    JSArenaPool &pool = cx->compartment->pool;
+    LifoAlloc &tla = cx->typeLifoAlloc();
     unsigned maxDepth = script->nslots - script->nfixed;
 
     /*
      * Current value of each variable and stack value. Empty for missing or
      * untracked entries, i.e. escaping locals and arguments.
      */
     SSAValue *values = (SSAValue *)
         cx->calloc_((numSlots + maxDepth) * sizeof(SSAValue));
@@ -1485,17 +1483,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
 
         unsigned nuses = GetUseCount(script, offset);
         unsigned ndefs = GetDefCount(script, offset);
         JS_ASSERT(stackDepth >= nuses);
 
         unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
 
         if (xuses) {
-            code->poppedValues = (SSAValue *)ArenaArray<SSAValue>(pool, xuses);
+            code->poppedValues = tla.newArray<SSAValue>(xuses);
             if (!code->poppedValues) {
                 setOOM(cx);
                 return;
             }
             for (unsigned i = 0; i < nuses; i++) {
                 SSAValue &v = stack[stackDepth - 1 - i];
                 code->poppedValues[i] = v;
                 v.clear();
@@ -1508,17 +1506,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
                 uint32 slot = GetBytecodeSlot(script, pc);
                 if (trackSlot(slot))
                     code->poppedValues[nuses] = values[slot];
                 else
                     code->poppedValues[nuses].clear();
             }
 
             if (xuses) {
-                SSAUseChain *useChains = ArenaArray<SSAUseChain>(cx->compartment->pool, xuses);
+                SSAUseChain *useChains = tla.newArray<SSAUseChain>(xuses);
                 if (!useChains) {
                     setOOM(cx);
                     return;
                 }
                 PodZero(useChains, xuses);
                 for (unsigned i = 0; i < xuses; i++) {
                     const SSAValue &v = code->poppedValues[i];
                     if (trackUseChain(v)) {
@@ -1535,17 +1533,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
 
         stackDepth -= nuses;
 
         for (unsigned i = 0; i < ndefs; i++)
             stack[stackDepth + i].initPushed(offset, i);
 
         unsigned xdefs = ExtendedDef(pc) ? ndefs + 1 : ndefs;
         if (xdefs) {
-            code->pushedUses = ArenaArray<SSAUseChain *>(cx->compartment->pool, xdefs);
+            code->pushedUses = tla.newArray<SSAUseChain *>(xdefs);
             if (!code->pushedUses) {
                 setOOM(cx);
                 return;
             }
             PodZero(code->pushedUses, xdefs);
         }
 
         stackDepth += ndefs;
@@ -1726,18 +1724,18 @@ PhiNodeCapacity(unsigned length)
     unsigned log2;
     JS_FLOOR_LOG2(log2, length - 1);
     return 1 << (log2 + 1);
 }
 
 bool
 ScriptAnalysis::makePhi(JSContext *cx, uint32 slot, uint32 offset, SSAValue *pv)
 {
-    SSAPhiNode *node = ArenaNew<SSAPhiNode>(cx->compartment->pool);
-    SSAValue *options = ArenaArray<SSAValue>(cx->compartment->pool, PhiNodeCapacity(0));
+    SSAPhiNode *node = cx->typeLifoAlloc().new_<SSAPhiNode>();
+    SSAValue *options = cx->typeLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(0));
     if (!node || !options) {
         setOOM(cx);
         return false;
     }
     node->slot = slot;
     node->options = options;
     pv->initPhi(offset, node);
     return true;
@@ -1759,17 +1757,17 @@ ScriptAnalysis::insertPhi(JSContext *cx,
             if (v.equals(node->options[i]))
                 return;
         }
     }
 
     if (trackUseChain(v)) {
         SSAUseChain *&uses = useChain(v);
 
-        SSAUseChain *use = ArenaNew<SSAUseChain>(cx->compartment->pool);
+        SSAUseChain *use = cx->typeLifoAlloc().new_<SSAUseChain>();
         if (!use) {
             setOOM(cx);
             return;
         }
 
         use->popped = false;
         use->offset = phi.phiOffset();
         use->u.phi = node;
@@ -1777,18 +1775,18 @@ ScriptAnalysis::insertPhi(JSContext *cx,
         uses = use;
     }
 
     if (node->length < PhiNodeCapacity(node->length)) {
         node->options[node->length++] = v;
         return;
     }
 
-    SSAValue *newOptions = ArenaArray<SSAValue>(cx->compartment->pool,
-                                                PhiNodeCapacity(node->length + 1));
+    SSAValue *newOptions =
+        cx->typeLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(node->length + 1));
     if (!newOptions) {
         setOOM(cx);
         return;
     }
 
     PodCopy(newOptions, node->options, node->length);
     node->options = newOptions;
     node->options[node->length++] = v;
@@ -1917,17 +1915,17 @@ ScriptAnalysis::freezeNewValues(JSContex
     code.pendingValues = NULL;
 
     unsigned count = pending->length();
     if (count == 0) {
         cx->delete_(pending);
         return;
     }
 
-    code.newValues = ArenaArray<SlotValue>(cx->compartment->pool, count + 1);
+    code.newValues = cx->typeLifoAlloc().newArray<SlotValue>(count + 1);
     if (!code.newValues) {
         setOOM(cx);
         return;
     }
 
     for (unsigned i = 0; i < count; i++)
         code.newValues[i] = (*pending)[i];
     code.newValues[count].slot = 0;
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -36,21 +36,23 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* Definitions for javascript analysis. */
 
 #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;
 
 /* Forward declaration of downstream register allocations computed for join points. */
 namespace js { namespace mjit { struct RegisterAllocation; } }
 
 namespace js {
 namespace analyze {
@@ -78,21 +80,16 @@ namespace analyze {
  * - SSA analysis of the script's variables and stack values. For each stack
  * value popped and non-escaping local variable or argument read, determines
  * which push(es) or write(s) produced that value.
  *
  * Intermediate type inference results are additionally stored here. The above
  * analyses are independent from type inference.
  */
 
-class SSAValue;
-struct SSAUseChain;
-struct LoopAnalysis;
-struct SlotValue;
-
 /* Information about a bytecode instruction. */
 class Bytecode
 {
     friend class ScriptAnalysis;
 
   public:
     Bytecode() { PodZero(this); }
 
@@ -1367,9 +1364,21 @@ class CrossScriptSSA
 
 #ifdef DEBUG
 void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc);
 #endif
 
 } /* namespace analyze */
 } /* namespace js */
 
+namespace js {
+namespace tl {
+
+template <> struct IsPodType<js::analyze::LifetimeVariable> { static const bool result = true; };
+template <> struct IsPodType<js::analyze::LoopAnalysis>     { static const bool result = true; };
+template <> struct IsPodType<js::analyze::SlotValue>        { static const bool result = true; };
+template <> struct IsPodType<js::analyze::SSAValue>         { static const bool result = true; };
+template <> struct IsPodType<js::analyze::SSAUseChain>      { static const bool result = true; };
+
+} /* namespace tl */
+} /* namespace js */
+
 #endif // jsanalyze_h___
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -43,17 +43,16 @@
  */
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include "jstypes.h"
 #include "jsstdint.h"
-#include "jsarena.h"
 #include "jsutil.h"
 #include "jsclist.h"
 #include "jsdhash.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
@@ -95,16 +94,17 @@
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsregexpinlines.h"
 #include "jsscriptinlines.h"
 #include "assembler/wtf/Platform.h"
 
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
+#include "ds/LifoAlloc.h"
 
 #if ENABLE_YARR_JIT
 #include "assembler/jit/ExecutableAllocator.h"
 #include "methodjit/Logging.h"
 #endif
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
@@ -2603,40 +2603,32 @@ JS_IsGCMarkingTracer(JSTracer *trc)
 JS_PUBLIC_API(void)
 JS_CompartmentGC(JSContext *cx, JSCompartment *comp)
 {
     /* We cannot GC the atoms compartment alone; use a full GC instead. */
     JS_ASSERT(comp != cx->runtime->atomsCompartment);
 
     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);
 }
 
 JS_PUBLIC_API(void)
 JS_GC(JSContext *cx)
 {
     GCREASON(PUBLIC_API);
     JS_CompartmentGC(cx, NULL);
 }
 
 JS_PUBLIC_API(void)
 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);
 }
 
 JS_PUBLIC_API(JSGCCallback)
 JS_SetGCCallback(JSContext *cx, JSGCCallback cb)
 {
     CHECK_REQUEST(cx);
     return JS_SetGCCallbackRT(cx->runtime, cb);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -52,17 +52,16 @@
 # include <android/log.h>
 # include <fstream>
 # include <string>
 #endif  // ANDROID
 
 #include "jsstdint.h"
 
 #include "jstypes.h"
-#include "jsarena.h"
 #include "jsutil.h"
 #include "jsclist.h"
 #include "jsprf.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsdtoa.h"
@@ -107,16 +106,17 @@ ThreadData::ThreadData()
 #endif
 #ifdef JS_TRACER
     onTraceCompartment(NULL),
     recordingCompartment(NULL),
     profilingCompartment(NULL),
     maxCodeCacheBytes(DEFAULT_JIT_CACHE_SIZE),
 #endif
     waiveGCQuota(false),
+    tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     dtoaState(NULL),
     nativeStackBase(GetNativeStackBase()),
     pendingProxyOperation(NULL),
     interpreterFrames(NULL)
 {
 #ifdef DEBUG
     noGCOrAllocationCheck = 0;
 #endif
@@ -311,19 +311,16 @@ js_PurgeThreads(JSContext *cx)
             thread->data.purge(cx);
         }
     }
 #else
     cx->runtime->threadData.purge(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)
 {
     JSContext *cx;
     JSBool first;
     JSContextCallback cxCallback;
 
     /*
@@ -340,19 +337,16 @@ js_NewContext(JSRuntime *rt, size_t stac
 #if JS_STACK_GROWTH_DIRECTION > 0
     cx->stackLimit = (jsuword) -1;
 #endif
     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
     JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
     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()) {
         Foreground::delete_(cx);
         return NULL;
     }
 
 #ifdef JS_THREADSAFE
@@ -1368,19 +1362,16 @@ JSContext::~JSContext()
     JS_ASSERT(!thread_);
 #endif
 
     /* Free the stuff hanging off of cx. */
     VOUCH_DOES_NOT_REQUIRE_STACK();
     if (parseMapPool_)
         Foreground::delete_<ParseMapPool>(parseMapPool_);
 
-    JS_FinishArenaPool(&regExpPool);
-    JS_FinishArenaPool(&tempPool);
-
     if (lastMessage)
         Foreground::free_(lastMessage);
 
     /* Remove any argument formatters. */
     JSArgumentFormatMap *map = argumentFormatMap;
     while (map) {
         JSArgumentFormatMap *temp = map;
         map = map->next;
@@ -1497,35 +1488,19 @@ JSRuntime::onOutOfMemory(void *p, size_t
     if (p)
         return p;
 #endif
     if (cx)
         js_ReportOutOfMemory(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, &regExpPool);
     if (!activeCompilations) {
         Foreground::delete_<ParseMapPool>(parseMapPool_);
         parseMapPool_ = NULL;
     }
 }
 
 #if defined(JS_TRACER) || defined(JS_METHODJIT)
 static bool
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -41,17 +41,16 @@
 #ifndef jscntxt_h___
 #define jscntxt_h___
 /*
  * JS execution context.
  */
 #include <string.h>
 
 #include "jsprvtd.h"
-#include "jsarena.h"
 #include "jsclist.h"
 #include "jsatom.h"
 #include "jsdhash.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcchunk.h"
 #include "jshashtable.h"
 #include "jsinfer.h"
@@ -59,16 +58,17 @@
 #include "jsobj.h"
 #include "jspropertycache.h"
 #include "jspropertytree.h"
 #include "jsstaticcheck.h"
 #include "jsutil.h"
 #include "jsvector.h"
 #include "prmjtime.h"
 
+#include "ds/LifoAlloc.h"
 #include "vm/Stack.h"
 #include "vm/String.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #pragma warning(push)
 #pragma warning(disable:4355) /* Silence warning about "this" used in base member initializer list */
@@ -185,16 +185,20 @@ struct ThreadData {
     StackSpace          stackSpace;
 
     /*
      * Flag indicating that we are waiving any soft limits on the GC heap
      * because we want allocations to be infallible (except when we hit OOM).
      */
     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.
      */
     GSNCache            gsnCache;
 
     /* Property cache for faster call/get/set invocation. */
     PropertyCache       propertyCache;
@@ -219,16 +223,17 @@ struct ThreadData {
 
     bool init();
 
     void mark(JSTracer *trc) {
         stackSpace.mark(trc);
     }
 
     void purge(JSContext *cx) {
+        tempLifoAlloc.freeUnused();
         gsnCache.purge();
 
         /* FIXME: bug 506341. */
         propertyCache.purge(cx);
     }
 
     /* This must be called with the GC lock held. */
     void triggerOperationCallback(JSRuntime *rt);
@@ -989,27 +994,21 @@ struct JSContext
     js::FrameRegs* maybeRegs() const  { return stack.maybeRegs(); }
 
     /* Set cx->compartment based on the current scope chain. */
     void resetCompartment();
 
     /* 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;
 
     /* State for object and array toSource conversion. */
     JSSharpObjectMap    sharpObjectMap;
     js::BusyArraysSet   busyArrays;
 
     /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */
@@ -1139,16 +1138,19 @@ struct JSContext
         JS_ASSERT((ropt & JSRUNOPTION_MASK) == ropt);
         return !!(runOptions & ropt);
     }
 
     bool hasStrictOption() const { return hasRunOption(JSOPTION_STRICT); }
     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_;
   public:
     JSThread *thread() const { return thread_; }
 
     void setThread(JSThread *thread);
     static const size_t threadOffset() { return offsetof(JSContext, thread_); }
@@ -1872,38 +1874,16 @@ class AutoKeepAtoms {
       : rt(rt)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         JS_KEEP_ATOMS(rt);
     }
     ~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 <typename T>
-    T *alloc(size_t elems) {
-        void *ptr;
-        JS_ARENA_ALLOCATE(ptr, pool, elems * sizeof(T));
-        return static_cast<T *>(ptr);
-    }
-};
-
 class AutoReleasePtr {
     JSContext   *cx;
     void        *ptr;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     AutoReleasePtr operator=(const AutoReleasePtr &other);
 
   public:
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -402,16 +402,22 @@ LeaveTraceIfArgumentsObject(JSContext *c
 
 #ifdef JS_METHODJIT
 inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment()
 {
     return compartment->jaegerCompartment();
 }
 #endif
 
+inline js::LifoAlloc &
+JSContext::typeLifoAlloc()
+{
+    return compartment->typeLifoAlloc;
+}
+
 inline bool
 JSContext::ensureGeneratorStackSpace()
 {
     bool ok = genStack.reserve(genStack.length() + 1);
     if (!ok)
         js_ReportOutOfMemory(this);
     return ok;
 }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -69,16 +69,17 @@ using namespace js::gc;
 
 JSCompartment::JSCompartment(JSRuntime *rt)
   : rt(rt),
     principals(NULL),
     gcBytes(0),
     gcTriggerBytes(0),
     gcLastBytes(0),
     hold(false),
+    typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
 #ifdef JS_TRACER
     traceMonitor_(NULL),
 #endif
     data(NULL),
     active(false),
     hasDebugModeCodeToDrop(false),
 #ifdef JS_METHODJIT
     jaegerCompartment_(NULL),
@@ -127,21 +128,16 @@ JSCompartment::~JSCompartment()
 }
 
 bool
 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;
 
     if (!scriptFilenameTable.init())
         return false;
 
     regExpAllocator = rt->new_<WTF::BumpPointerAllocator>();
     if (!regExpAllocator)
@@ -617,18 +613,18 @@ JSCompartment::sweep(JSContext *cx, uint
             }
         }
 #endif
     } else {
         /*
          * 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
          * compartment, including all remaining mjit code if inference is
          * enabled in the compartment.
          */
         if (types.inferenceEnabled) {
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
@@ -651,19 +647,16 @@ JSCompartment::sweep(JSContext *cx, uint
         }
 
         types.sweep(cx);
 
         for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             script->clearAnalysis();
         }
-
-        /* Reset the analysis pool, releasing all analysis and intermediate type data. */
-        JS_FinishArenaPool(&oldPool);
     }
 
     active = false;
 }
 
 void
 JSCompartment::purge(JSContext *cx)
 {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -403,17 +403,18 @@ struct JS_FRIEND_API(JSCompartment) {
     bool                         hold;
     bool                         isSystemCompartment;
 
     /*
      * Pool for analysis and intermediate type information in this compartment.
      * 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;
 
     /* Type information about the scripts and objects in this compartment. */
     js::types::TypeCompartment   types;
 
 #ifdef JS_TRACER
   private:
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -480,20 +480,19 @@ JS_FunctionHasLocalNames(JSContext *cx, 
 extern JS_PUBLIC_API(jsuword *)
 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
 {
     Vector<JSAtom *> localNames(cx);
     if (!fun->script()->bindings.getLocalNameArray(cx, &localNames))
         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<jsuword>(localNames.length());
     if (!names) {
         js_ReportOutOfMemory(cx);
         return NULL;
     }
 
     memcpy(names, localNames.begin(), localNames.length() * sizeof(jsuword));
     return names;
 }
@@ -508,17 +507,17 @@ extern JS_PUBLIC_API(JSString *)
 JS_AtomKey(JSAtom *atom)
 {
     return 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 *)
 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
 {
     return fun->maybeScript();
 }
 
@@ -2162,19 +2161,19 @@ JS_GetFunctionCallback(JSContext *cx)
 }
 
 #endif /* MOZ_TRACE_JSCALLS */
 
 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);
     fputs(sprinter.base, stdout);
     fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno);
 #endif
 }
 
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -43,17 +43,16 @@
  */
 #ifdef HAVE_MEMORY_H
 #include <memory.h>
 #endif
 #include <new>
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
-#include "jsarena.h"
 #include "jsutil.h"
 #include "jsbit.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsversion.h"
@@ -70,16 +69,17 @@
 #include "jsstaticcheck.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #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 */
 #define SRCNOTE_CHUNK_LENGTH   1024    /* initial srcnote chunk length */
 
 /* Macros to compute byte sizes from typed element counts. */
 #define BYTECODE_SIZE(n)        ((n) * sizeof(jsbytecode))
 #define SRCNOTE_SIZE(n)         ((n) * sizeof(jssrcnote))
@@ -510,18 +510,17 @@ AddJumpTarget(AddJumpTargetArgs *args, J
     jt = *jtp;
     if (!jt) {
         JSCodeGenerator *cg = args->cg;
 
         jt = cg->jtFreeList;
         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_<JSJumpTarget>();
             if (!jt) {
                 js_ReportOutOfMemory(args->cx);
                 return 0;
             }
         }
         jt->offset = args->offset;
         jt->balance = 0;
         jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL;
@@ -3307,19 +3306,17 @@ EmitNumberOp(JSContext *cx, jsdouble dva
  * are not currently recycled (like parse nodes) and the temp pool is only
  * flushed at the end of compiling a script, so these values are technically
  * leaked. This would only be a problem for scripts containing a large number
  * of large switches, which seems unlikely.
  */
 static Value *
 AllocateSwitchConstant(JSContext *cx)
 {
-    Value *pv;
-    JS_ARENA_ALLOCATE_TYPE(pv, Value, &cx->tempPool);
-    return pv;
+    return cx->tempLifoAlloc().new_<Value>();
 }
 
 /*
  * Sometimes, let-slots are pushed to the JS stack before we logically enter
  * the let scope. For example,
  *     let (x = EXPR) BODY
  * compiles to roughly {enterblock; EXPR; setlocal x; BODY; leaveblock} even
  * though EXPR is evaluated in the enclosing scope; it does not see x.
@@ -7787,24 +7784,22 @@ js_FinishTakingSrcNotes(JSContext *cx, J
 #endif
     return JS_TRUE;
 }
 
 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_<JSTryNode>();
     if (!tryNode) {
         js_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
 
     tryNode->note.kind = kind;
     tryNode->note.stackDepth = (uint16)stackDepth;
     tryNode->note.start = (uint32)start;
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -663,21 +663,19 @@ struct JSCodeGenerator : public JSTreeCo
     JSCodeGenerator(js::Parser *parser, uintN lineno);
     bool init(JSContext *cx, JSTreeContext::InitBehavior ib = USED_AS_CODE_GENERATOR);
 
     JSContext *context() {
         return parser->context;
     }
 
     /*
-     * 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();
 
     /*
      * Adds a use of a variable that is statically known to exist on the
      * global object.
      *
      * The actual slot of the variable on the global object is not known
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2177,18 +2177,18 @@ Function(JSContext *cx, uintN argc, Valu
             return false;
         }
 
         /*
          * Allocate a string to hold the concatenated arguments, including room
          * 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<jschar>(args_length + 1);
+        LifoAllocScope las(&cx->tempLifoAlloc());
+        jschar *cp = cx->tempLifoAlloc().newArray<jschar>(args_length + 1);
         if (!cp) {
             js_ReportOutOfMemory(cx);
             return false;
         }
         jschar *collected_args = cp;
 
         /*
          * Concatenate the arguments into the new string, separated by commas.
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -347,17 +347,17 @@ types::TypeFailure(JSContext *cx, const 
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
 TypeSet *
 TypeSet::make(JSContext *cx, const char *name)
 {
     JS_ASSERT(cx->compartment->activeInference);
 
-    TypeSet *res = ArenaNew<TypeSet>(cx->compartment->pool);
+    TypeSet *res = cx->typeLifoAlloc().new_<TypeSet>();
     if (!res) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return NULL;
     }
 
     InferSpew(ISpewOps, "typeSet: %sT%p%s intermediate %s",
               InferSpewColor(res), res, InferSpewColorReset(),
               name);
@@ -487,17 +487,17 @@ public:
         /* Basic subset constraint, move all types to the target. */
         target->addType(cx, type);
     }
 };
 
 void
 TypeSet::addSubset(JSContext *cx, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintSubset>(cx->compartment->pool, target));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubset>(target));
 }
 
 /* Constraints for reads/writes on object properties. */
 class TypeConstraintProp : public TypeConstraint
 {
 public:
     JSScript *script;
     jsbytecode *pc;
@@ -522,24 +522,24 @@ public:
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id)
 {
-    add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->pool, script, pc, target, id, false));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintProp>(script, pc, target, id, false));
 }
 
 void
 TypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id)
 {
-    add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->pool, script, pc, target, id, true));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintProp>(script, pc, target, id, true));
 }
 
 /*
  * Constraints for updating the 'this' types of callees on CALLPROP/CALLELEM.
  * These are derived from the types on the properties themselves, rather than
  * those pushed in the 'this' slot at the call site, which allows us to retain
  * correlations between the type of the 'this' object and the associated
  * callee scripts at polymorphic call sites.
@@ -570,17 +570,17 @@ TypeSet::addCallProperty(JSContext *cx, 
      * modify the 'this' types of callees. The initial 'this' value will be
      * outright ignored.
      */
     jsbytecode *callpc = script->analysis()->getCallPC(pc);
     UntrapOpcode untrap(cx, script, callpc);
     if (JSOp(*callpc) == JSOP_NEW)
         return;
 
-    add(cx, ArenaNew<TypeConstraintCallProp>(cx->compartment->pool, script, callpc, id));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintCallProp>(script, callpc, id));
 }
 
 /*
  * Constraints for generating 'set' property constraints on a SETELEM only if
  * the element type may be a number. For SETELEM we only account for integer
  * indexes, and if the element cannot be an integer (e.g. it must be a string)
  * then we lose precision by treating it like one.
  */
@@ -603,18 +603,18 @@ public:
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
                        TypeSet *objectTypes, TypeSet *valueTypes)
 {
-    add(cx, ArenaNew<TypeConstraintSetElement>(cx->compartment->pool, script, pc,
-                                               objectTypes, valueTypes));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintSetElement>(script, pc, objectTypes,
+                                                               valueTypes));
 }
 
 /*
  * Constraints for watching call edges as they are discovered and invoking native
  * function handlers, adding constraints for arguments, receiver objects and the
  * return value, and updating script foundOffsets.
  */
 class TypeConstraintCall : public TypeConstraint
@@ -628,17 +628,17 @@ public:
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addCall(JSContext *cx, TypeCallsite *site)
 {
-    add(cx, ArenaNew<TypeConstraintCall>(cx->compartment->pool, site));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintCall>(site));
 }
 
 /* Constraints for arithmetic operations. */
 class TypeConstraintArith : public TypeConstraint
 {
 public:
     /* Type set receiving the result of the arithmetic. */
     TypeSet *target;
@@ -653,17 +653,17 @@ public:
     }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addArith(JSContext *cx, TypeSet *target, TypeSet *other)
 {
-    add(cx, ArenaNew<TypeConstraintArith>(cx->compartment->pool, target, other));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintArith>(target, other));
 }
 
 /* Subset constraint which transforms primitive values into appropriate objects. */
 class TypeConstraintTransformThis : public TypeConstraint
 {
 public:
     JSScript *script;
     TypeSet *target;
@@ -673,17 +673,17 @@ public:
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintTransformThis>(cx->compartment->pool, script, target));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintTransformThis>(script, target));
 }
 
 /*
  * Constraint which adds a particular type to the 'this' types of all
  * discovered scripted functions.
  */
 class TypeConstraintPropagateThis : public TypeConstraint
 {
@@ -704,17 +704,17 @@ void
 TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type, TypeSet *types)
 {
     /* Don't add constraints when the call will be 'new' (see addCallProperty). */
     jsbytecode *callpc = script->analysis()->getCallPC(pc);
     UntrapOpcode untrap(cx, script, callpc);
     if (JSOp(*callpc) == JSOP_NEW)
         return;
 
-    add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type, types));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintPropagateThis>(script, callpc, type, types));
 }
 
 /* Subset constraint which filters out primitive types. */
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
 public:
     TypeSet *target;
     TypeSet::FilterKind filter;
@@ -747,17 +747,17 @@ public:
 
         target->addType(cx, type);
     }
 };
 
 void
 TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter)
 {
-    add(cx, ArenaNew<TypeConstraintFilterPrimitive>(cx->compartment->pool, target, filter));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFilterPrimitive>(target, filter));
 }
 
 /* If id is a normal slotful 'own' property of an object, get its shape. */
 static inline const Shape *
 GetSingletonShape(JSContext *cx, JSObject *obj, jsid id)
 {
     const Shape *shape = obj->nativeLookup(cx, id);
     if (shape && shape->hasDefaultGetterOrIsMethod() && shape->slot != SHAPE_INVALID_SLOT)
@@ -871,17 +871,17 @@ public:
 
         target->addType(cx, type);
     }
 };
 
 void
 TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintSubsetBarrier>(cx->compartment->pool, script, pc, target));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubsetBarrier>(script, pc, target));
 }
 
 /*
  * Constraint which marks a pushed ARGUMENTS value as unknown if the script has
  * an arguments object created in the future.
  */
 class TypeConstraintLazyArguments : public TypeConstraint
 {
@@ -899,17 +899,17 @@ public:
         if (object->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS))
             target->addType(cx, Type::UnknownType());
     }
 };
 
 void
 TypeSet::addLazyArguments(JSContext *cx, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintLazyArguments>(cx->compartment->pool, target));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintLazyArguments>(target));
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeConstraint
 /////////////////////////////////////////////////////////////////////
 
 /* Get the object to use for a property access on type. */
 static inline TypeObject *
@@ -1081,19 +1081,18 @@ TypeConstraintCallProp::newType(JSContex
             cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         } else {
             TypeSet *types = object->getProperty(cx, id, false);
             if (!types)
                 return;
             if (!types->hasPropagatedProperty())
                 object->getFromPrototypes(cx, id, types);
             /* Bypass addPropagateThis, we already have the callpc. */
-            types->add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool,
-                                                                 script, callpc, type,
-                                                                 (TypeSet *) NULL));
+            types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintPropagateThis>(
+                            script, callpc, type, (TypeSet *) NULL));
         }
     }
 }
 
 void
 TypeConstraintSetElement::newType(JSContext *cx, TypeSet *source, Type type)
 {
     if (type.isUnknown() ||
@@ -1384,18 +1383,18 @@ public:
         typeAdded = true;
         cx->compartment->types.addPendingRecompile(cx, script);
     }
 };
 
 void
 TypeSet::addFreeze(JSContext *cx)
 {
-    add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
-                                           cx->compartment->types.compiledScript), false);
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
+                cx->compartment->types.compiledScript), false);
 }
 
 /*
  * Constraint which triggers recompilation of a script if a possible new JSValueType
  * tag is realized for a type set.
  */
 class TypeConstraintFreezeTypeTag : public TypeConstraint
 {
@@ -1468,18 +1467,18 @@ TypeSet::getKnownTypeTag(JSContext *cx)
      * it a definite type tag. This is not needed if there are enough types
      * that the exact tag is unknown, as it will stay unknown as more types are
      * added to the set.
      */
     bool empty = flags == 0 && baseObjectCount() == 0;
     JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
 
     if (cx->compartment->types.compiledScript && (empty || type != JSVAL_TYPE_UNKNOWN)) {
-        add(cx, ArenaNew<TypeConstraintFreezeTypeTag>(cx->compartment->pool,
-                                                      cx->compartment->types.compiledScript), false);
+        add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeTypeTag>(
+                  cx->compartment->types.compiledScript), false);
     }
 
     return type;
 }
 
 /* Constraint which triggers recompilation if an object acquires particular flags. */
 class TypeConstraintFreezeObjectFlags : public TypeConstraint
 {
@@ -1548,19 +1547,18 @@ public:
             if (!object->hasAnyFlags(flags)) {
                 /*
                  * Add a constraint on the the object to pick up changes in the
                  * object's properties.
                  */
                 TypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
                 if (!types)
                     return;
-                types->add(cx,
-                    ArenaNew<TypeConstraintFreezeObjectFlags>(cx->compartment->pool,
-                                                              script, flags, &marked), false);
+                types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
+                                  script, flags, &marked), false);
                 return;
             }
         } else {
             return;
         }
 
         marked = true;
         cx->compartment->types.addPendingRecompile(cx, script);
@@ -1591,34 +1589,33 @@ TypeSet::hasObjectFlags(JSContext *cx, T
         if (object && object->hasAnyFlags(flags))
             return true;
     }
 
     /*
      * Watch for new objects of different kind, and re-traverse existing types
      * in this set to add any needed FreezeArray constraints.
      */
-    add(cx, ArenaNew<TypeConstraintFreezeObjectFlagsSet>(cx->compartment->pool,
-                                                         cx->compartment->types.compiledScript, flags));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlagsSet>(
+                 cx->compartment->types.compiledScript, flags));
 
     return false;
 }
 
 bool
 TypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags)
 {
     if (object->hasAnyFlags(flags))
         return true;
 
     TypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
     if (!types)
         return true;
-    types->add(cx,
-        ArenaNew<TypeConstraintFreezeObjectFlags>(cx->compartment->pool,
-                                                  cx->compartment->types.compiledScript, flags), false);
+    types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
+                      cx->compartment->types.compiledScript, flags), false);
     return false;
 }
 
 void
 types::MarkArgumentsCreated(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(!script->createdArgs);
 
@@ -1689,19 +1686,19 @@ TypeSet::WatchObjectStateChange(JSContex
     TypeSet *types = obj->getProperty(cx, JSID_EMPTY, false);
     if (!types)
         return;
 
     /*
      * Use a constraint which triggers recompilation when markStateChange is
      * called, which will set 'force' to true.
      */
-    types->add(cx, ArenaNew<TypeConstraintFreezeObjectFlags>(cx->compartment->pool,
-                                                             cx->compartment->types.compiledScript,
-                                                             0));
+    types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
+                     cx->compartment->types.compiledScript,
+                     0));
 }
 
 class TypeConstraintFreezeOwnProperty : public TypeConstraint
 {
 public:
     JSScript *script;
 
     bool updated;
@@ -1744,30 +1741,30 @@ TypeSet::isOwnProperty(JSContext *cx, Ty
             JS_ASSERT(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED);
             object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
         }
     }
 
     if (isOwnProperty(configurable))
         return true;
 
-    add(cx, ArenaNew<TypeConstraintFreezeOwnProperty>(cx->compartment->pool,
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeOwnProperty>(
                                                       cx->compartment->types.compiledScript,
                                                       configurable), false);
     return false;
 }
 
 bool
 TypeSet::knownNonEmpty(JSContext *cx)
 {
     if (baseFlags() != 0 || baseObjectCount() != 0)
         return true;
 
-    add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
-                                           cx->compartment->types.compiledScript), false);
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
+                cx->compartment->types.compiledScript), false);
 
     return false;
 }
 
 int
 TypeSet::getTypedArrayType(JSContext *cx)
 {
     int arrayType = TypedArray::TYPE_MAX;
@@ -1816,17 +1813,17 @@ TypeSet::getSingleton(JSContext *cx, boo
     if (baseFlags() != 0 || baseObjectCount() != 1)
         return NULL;
 
     JSObject *obj = getSingleObject(0);
     if (!obj)
         return NULL;
 
     if (freeze) {
-        add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
+        add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
                                                cx->compartment->types.compiledScript), false);
     }
 
     return obj;
 }
 
 static inline bool
 TypeHasGlobal(Type type, JSObject *global)
@@ -1874,19 +1871,18 @@ TypeSet::hasGlobalObject(JSContext *cx, 
 
     unsigned count = getObjectCount();
     for (unsigned i = 0; i < count; i++) {
         TypeObjectKey *object = getObject(i);
         if (object && !TypeHasGlobal(Type::ObjectType(object), global))
             return false;
     }
 
-    add(cx, ArenaNew<TypeConstraintFreezeGlobal>(cx->compartment->pool,
-                                                 cx->compartment->types.compiledScript,
-                                                 global), false);
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeGlobal>(
+              cx->compartment->types.compiledScript, global), false);
 
     return true;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
@@ -2304,18 +2300,17 @@ ScriptAnalysis::addTypeBarrier(JSContext
         barrier = barrier->next;
     }
 
     InferSpew(ISpewOps, "typeBarrier: #%u:%05u: %sT%p%s %s",
               script->id(), pc - script->code,
               InferSpewColor(target), target, InferSpewColorReset(),
               TypeString(type));
 
-    barrier = ArenaNew<TypeBarrier>(cx->compartment->pool, target, type,
-                                    (JSObject *) NULL, JSID_VOID);
+    barrier = cx->typeLifoAlloc().new_<TypeBarrier>(target, type, (JSObject *) NULL, JSID_VOID);
 
     barrier->next = code.typeBarriers;
     code.typeBarriers = barrier;
 }
 
 void
 ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, JSObject *singleton, jsid singletonId)
 {
@@ -2330,18 +2325,17 @@ ScriptAnalysis::addSingletonTypeBarrier(
             ObjectStateChange(cx, script->function()->type(), false, true);
     }
 
     InferSpew(ISpewOps, "singletonTypeBarrier: #%u:%05u: %sT%p%s %p %s",
               script->id(), pc - script->code,
               InferSpewColor(target), target, InferSpewColorReset(),
               (void *) singleton, TypeIdString(singletonId));
 
-    TypeBarrier *barrier =
-        ArenaNew<TypeBarrier>(cx->compartment->pool, target, Type::UndefinedType(),
+    TypeBarrier *barrier = cx->typeLifoAlloc().new_<TypeBarrier>(target, Type::UndefinedType(),
                               singleton, singletonId);
 
     barrier->next = code.typeBarriers;
     code.typeBarriers = barrier;
 }
 
 void
 TypeCompartment::print(JSContext *cx, bool force)
@@ -2696,17 +2690,17 @@ UpdatePropertyType(JSContext *cx, TypeSe
         types->addType(cx, type);
     }
 }
 
 bool
 TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop)
 {
     JS_ASSERT(!*pprop);
-    Property *base = ArenaNew<Property>(cx->compartment->pool, id);
+    Property *base = cx->typeLifoAlloc().new_<Property>(id);
     if (!base) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return false;
     }
 
     if (singleton) {
         /*
          * Fill the property in with any type the object already has in an
@@ -3246,17 +3240,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
     JS_ASSERT(!code.pushedTypes);
 
     InferSpew(ISpewOps, "analyze: #%u:%05u", script->id(), offset);
 
     unsigned defCount = GetDefCount(script, offset);
     if (ExtendedDef(pc))
         defCount++;
 
-    TypeSet *pushed = ArenaArray<TypeSet>(cx->compartment->pool, defCount);
+    TypeSet *pushed = cx->typeLifoAlloc().newArrayUninitialized<TypeSet>(defCount);
     if (!pushed)
         return false;
     PodZero(pushed, defCount);
     code.pushedTypes = pushed;
 
     /*
      * Add phi nodes introduced at this point to the list of all phi nodes in
      * the script. Types for these are not generated until after the script has
@@ -3769,17 +3763,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
       case JSOP_NEW: {
         TypeSet *seen = script->analysis()->bytecodeTypes(pc);
         seen->addSubset(cx, &pushed[0]);
 
         /* Construct the base call information about this site. */
         unsigned argCount = GetUseCount(script, offset) - 2;
-        TypeCallsite *callsite = ArenaNew<TypeCallsite>(cx->compartment->pool,
+        TypeCallsite *callsite = cx->typeLifoAlloc().new_<TypeCallsite>(
                                                         cx, script, pc, op == JSOP_NEW, argCount);
         if (!callsite || (argCount && !callsite->argumentTypes)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             break;
         }
         callsite->thisTypes = poppedTypes(pc, argCount);
         callsite->returnTypes = seen;
 
@@ -4491,18 +4485,17 @@ AnalyzeNewScriptProperties(JSContext *cx
              */
             TypeObject *parentObject = type->proto->getType(cx);
             if (parentObject->unknownProperties())
                 return false;
             TypeSet *parentTypes = parentObject->getProperty(cx, id, false);
             if (!parentTypes || parentTypes->unknown())
                 return false;
             parentObject->getFromPrototypes(cx, id, parentTypes);
-            parentTypes->add(cx,
-                ArenaNew<TypeConstraintClearDefiniteSetter>(cx->compartment->pool, type));
+            parentTypes->add(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSetter>(type));
         } else if (op == JSOP_FUNCALL && uses->u.which == GET_ARGC(pc) - 1) {
             /*
              * Passed as the first parameter to Function.call. Follow control
              * into the callee, and add any definite properties it assigns to
              * the object as well. :TODO: This is narrow pattern matching on
              * the inheritance patterns seen in the v8-deltablue benchmark, and
              * needs robustness against other ways initialization can cross
              * script boundaries.
@@ -4541,19 +4534,19 @@ AnalyzeNewScriptProperties(JSContext *cx
             JSFunction *function = scriptObj->getFunctionPrivate();
             JS_ASSERT(!function->script()->isInnerFunction);
 
             /*
              * Generate constraints to clear definite properties from the type
              * should the Function.call or callee itself change in the future.
              */
             analysis->pushedTypes(calleev.pushedOffset(), 0)->add(cx,
-                ArenaNew<TypeConstraintClearDefiniteSingle>(cx->compartment->pool, type));
+                cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
             analysis->pushedTypes(calleev.pushedOffset(), 1)->add(cx,
-                ArenaNew<TypeConstraintClearDefiniteSingle>(cx->compartment->pool, type));
+                cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
 
             TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, uses->offset);
             if (!initializerList->append(pushframe)) {
                 cx->compartment->types.setPendingNukeTypes(cx);
                 *pbaseobj = NULL;
                 return false;
             }
 
@@ -5419,17 +5412,17 @@ JSScript::makeTypes(JSContext *cx, JSFun
 
 bool
 JSScript::makeAnalysis(JSContext *cx)
 {
     JS_ASSERT(types && !types->analysis);
 
     AutoEnterAnalysis enter(cx);
 
-    types->analysis = ArenaNew<ScriptAnalysis>(cx->compartment->pool, this);
+    types->analysis = cx->typeLifoAlloc().new_<ScriptAnalysis>(this);
 
     if (!types->analysis)
         return false;
 
     types->analysis->analyzeBytecode(cx);
 
     if (types->analysis->OOM()) {
         types->analysis = NULL;
@@ -5810,17 +5803,17 @@ TypeObject::sweep(JSContext *cx)
         unsigned oldCapacity = HashSetCapacity(propertyCount);
         Property **oldArray = propertySet;
 
         clearProperties();
         propertyCount = 0;
         for (unsigned i = 0; i < oldCapacity; i++) {
             Property *prop = oldArray[i];
             if (prop && prop->types.isOwnProperty(false)) {
-                Property *newProp = ArenaNew<Property>(compartment->pool, *prop);
+                Property *newProp = compartment->typeLifoAlloc.new_<Property>(*prop);
                 if (newProp) {
                     Property **pentry =
                         HashSetInsert<jsid,Property,Property>
                             (compartment, propertySet, propertyCount, prop->id);
                     if (pentry) {
                         *pentry = newProp;
                         newProp->types.sweep(cx, compartment);
                     } else {
@@ -5830,17 +5823,17 @@ TypeObject::sweep(JSContext *cx)
                     compartment->types.setPendingNukeTypes(cx);
                 }
             }
         }
         setBasePropertyCount(propertyCount);
     } else if (propertyCount == 1) {
         Property *prop = (Property *) propertySet;
         if (prop->types.isOwnProperty(false)) {
-            Property *newProp = ArenaNew<Property>(compartment->pool, *prop);
+            Property *newProp = compartment->typeLifoAlloc.new_<Property>(*prop);
             if (newProp) {
                 propertySet = (Property **) newProp;
                 newProp->types.sweep(cx, compartment);
             } else {
                 compartment->types.setPendingNukeTypes(cx);
             }
         } else {
             propertySet = NULL;
@@ -6104,17 +6097,17 @@ JS_FRIEND_API(void)
 JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment,
                                TypeInferenceMemoryStats *stats)
 {
     /*
      * Note: not all data in the pool is temporary, and some will survive GCs
      * 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;
 
     for (gc::CellIter i(cx, compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next())
         GetScriptMemoryStats(i.get<JSScript>(), stats);
 
     if (compartment->types.allocationSiteTable)
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -38,39 +38,26 @@
  * ***** END LICENSE BLOCK ***** */
 
 /* Definitions related to javascript type inference. */
 
 #ifndef jsinfer_h___
 #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; }
     static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; }
 };
 
 /*
  * Information about a single concrete type. We pack this into a single word,
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -815,17 +815,17 @@ HashSetInsertTry(JSCompartment *compartm
     count++;
     unsigned newCapacity = HashSetCapacity(count);
 
     if (newCapacity == capacity) {
         JS_ASSERT(!converting);
         return &values[insertpos];
     }
 
-    U **newValues = ArenaArray<U*>(compartment->pool, newCapacity);
+    U **newValues = compartment->typeLifoAlloc.newArray<U*>(newCapacity);
     if (!newValues)
         return NULL;
     PodZero(newValues, newCapacity);
 
     for (unsigned i = 0; i < capacity; i++) {
         if (values[i]) {
             unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
             while (newValues[pos] != NULL)
@@ -856,17 +856,17 @@ HashSetInsert(JSCompartment *compartment
         return (U **) &values;
     }
 
     if (count == 1) {
         U *oldData = (U*) values;
         if (KEY::getKey(oldData) == key)
             return (U **) &values;
 
-        values = ArenaArray<U*>(compartment->pool, SET_ARRAY_SIZE);
+        values = compartment->typeLifoAlloc.newArray<U*>(SET_ARRAY_SIZE);
         if (!values) {
             values = (U **) oldData;
             return NULL;
         }
         PodZero(values, SET_ARRAY_SIZE);
         count++;
 
         values[0] = oldData;
@@ -1092,17 +1092,17 @@ TypeSet::getTypeObject(unsigned i)
 
 inline
 TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
                            bool isNew, unsigned argumentCount)
     : script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
       thisTypes(NULL), returnTypes(NULL)
 {
     /* Caller must check for failure. */
-    argumentTypes = ArenaArray<TypeSet*>(cx->compartment->pool, argumentCount);
+    argumentTypes = cx->typeLifoAlloc().newArray<TypeSet*>(argumentCount);
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
 {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -41,17 +41,16 @@
 /*
  * JavaScript bytecode interpreter.
  */
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
 #include "jstypes.h"
 #include "jsstdint.h"
-#include "jsarena.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsdate.h"
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -40,17 +40,16 @@
 
 /*
  * JavaScript iterators.
  */
 #include <string.h>     /* for memcpy */
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h"
-#include "jsarena.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsexn.h"
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -40,17 +40,16 @@
 
 /*
  * JS object implementation.
  */
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
-#include "jsarena.h"
 #include "jsbit.h"
 #include "jsutil.h"
 #include "jshash.h"
 #include "jsdhash.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -36,17 +36,16 @@
  * 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 ***** */
 
 #include <string.h>
 #include "jsapi.h"
-#include "jsarena.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jsnum.h"
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -45,17 +45,16 @@
 #include <memory.h>
 #endif
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
-#include "jsarena.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsemit.h"
@@ -314,71 +313,67 @@ js_DisassembleAtPC(JSContext *cx, JSScri
         if (next == script->main())
             SprintCString(sp, "main:\n");
         if (pc != NULL) {
             if (pc == next)
                 SprintCString(sp, "--> ");
             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;
     }
     return JS_TRUE;
 }
 
 JS_FRIEND_API(JSBool)
 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp)
 {
     return js_DisassembleAtPC(cx, script, lines, NULL, 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;
 }
 
 static char *
 QuoteString(Sprinter *sp, JSString *str, uint32 quote);
 
 static bool
 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);
         return true;
     }
 
     if (cx->runtime->gcRunning || JS_THREAD_DATA(cx)->noGCOrAllocationCheck) {
         char *source = JS_sprintf_append(NULL, "<value>");
@@ -682,21 +677,20 @@ SprintEnsureBuffer(Sprinter *sp, size_t 
 {
     ptrdiff_t nb;
     char *base;
 
     nb = (sp->offset + len + 1) - sp->size;
     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<char *>(sp->pool->allocUnaligned(nb));
+    else
+        base = static_cast<char *>(sp->pool->reallocUnaligned(base, sp->size, nb));
     if (!base) {
         js_ReportOutOfMemory(sp->context);
         return JS_FALSE;
     }
     sp->base = base;
     sp->size += nb;
     return JS_TRUE;
 }
@@ -871,56 +865,49 @@ QuoteString(Sprinter *sp, JSString *str,
     if (off == sp->offset && Sprint(sp, "") < 0)
         return NULL;
     return OFF2STR(sp, off);
 }
 
 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;
 }
 
 /************************************************************************/
 
 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 */
     bool            strict;         /* in code marked strict */
     JSScript        *script;        /* script being printed */
     jsbytecode      *dvgfence;      /* DecompileExpression fencepost */
     jsbytecode      **pcstack;      /* DecompileExpression modeled stack */
     JSFunction      *fun;           /* interpreted function */
     Vector<JSAtom *> *localNames;   /* argument and variable names */
 };
 
 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;
     jp->strict = !!strict;
     jp->script = NULL;
     jp->dvgfence = NULL;
     jp->pcstack = NULL;
     jp->fun = fun;
@@ -933,34 +920,34 @@ js_NewPrinter(JSContext *cx, const char 
         }
     }
     return jp;
 }
 
 void
 js_DestroyPrinter(JSPrinter *jp)
 {
-    JS_FinishArenaPool(&jp->pool);
+    jp->pool.freeAll();
     Foreground::delete_(jp->localNames);
     jp->sprinter.context->free_(jp);
 }
 
 JSString *
 js_GetPrinterOutput(JSPrinter *jp)
 {
     JSContext *cx;
     JSString *str;
 
     cx = jp->sprinter.context;
     if (!jp->sprinter.base)
         return cx->runtime->emptyString;
     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;
 }
 
 /*
  * NB: Indexed by SRC_DECL_* defines from jsemit.h.
  */
 static const char * const var_prefix[] = {"var ", "const ", "let "};
@@ -1901,25 +1888,22 @@ DecompileGroupAssignment(SprintStack *ss
 #undef LOCAL_ASSERT
 #undef LOAD_OP_DATA
 
 #endif /* JS_HAS_DESTRUCTURING */
 
 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;
     }
     ss->offsets = (ptrdiff_t *) space;
     ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
 
     ss->top = ss->inArrayInit = 0;
@@ -4059,47 +4043,44 @@ Decompile(SprintStack *ss, jsbytecode *p
                 todo = STR2OFF(&ss->sprinter, rval);
                 break;
 
               case JSOP_LAMBDA:
               case JSOP_LAMBDA_FC:
 #if JS_HAS_GENERATOR_EXPRS
                 sn = js_GetSrcNote(jp->script, pc);
                 if (sn && SN_TYPE(sn) == SRC_GENEXP) {
-                    void *mark;
                     Vector<JSAtom *> *innerLocalNames;
                     Vector<JSAtom *> *outerLocalNames;
                     JSScript *inner, *outer;
                     SprintStack ss2;
                     JSFunction *outerfun;
 
                     LOAD_FUNCTION(0);
 
                     /*
                      * All allocation when decompiling is LIFO, using malloc
                      * or, more commonly, arena-allocating from cx->tempPool.
                      * 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_<Vector<JSAtom *> >(cx);
                         if (!innerLocalNames ||
                             fun->script()->bindings.getLocalNameArray(cx, innerLocalNames))
                         {
                             return NULL;
                         }
                     } else {
                         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;
 
                     /*
                      * Recursively decompile this generator function as an
                      * un-parenthesized generator expression. The ss->inGenExp
                      * special case of JSOP_YIELD shares array comprehension
                      * decompilation code that leaves the result as the single
                      * string pushed on ss2.
@@ -4117,20 +4098,18 @@ Decompile(SprintStack *ss, jsbytecode *p
                      * new prolog ops that have stack effects.
                      */
                     ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset,
                                    JSOP_NOP)
                          != NULL;
                     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
                      * arrange to advance over the call to this lambda.
                      */
                     pc += len;
                     if (*pc == JSOP_BLOCKCHAIN) {
                         pc += JSOP_BLOCKCHAIN_LENGTH;
@@ -4190,17 +4169,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     }
 
                     /*
                      * Alas, we have to malloc a copy of the result left on
                      * the top of ss2 because both ss and ss2 arena-allocate
                      * 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);
                     cx->free_((void *)rval);
                     break;
                 }
 #endif /* JS_HAS_GENERATOR_EXPRS */
                 /* FALL THROUGH */
@@ -4828,32 +4807,29 @@ Decompile(SprintStack *ss, jsbytecode *p
 
 static JSBool
 DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
               uintN pcdepth)
 {
     uintN depth, i;
     SprintStack ss;
     JSContext *cx;
-    void *mark;
-    JSBool ok;
     JSScript *oldscript;
     char *last;
 
     depth = StackDepth(script);
     JS_ASSERT(pcdepth <= depth);
     cx = jp->sprinter.context;
 
     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
      * script's bytecode that starts with a non-zero model stack depth given
      * by pcdepth, attempt to initialize the missing string offsets in ss to
      * |spindex| negative indexes from fp->sp for the activation fp in which
      * the error arose.
      *
@@ -4867,30 +4843,27 @@ DecompileCode(JSPrinter *jp, JSScript *s
             ss.offsets[i] = -2 - (ptrdiff_t)i;
             ss.opcodes[i] = *jp->pcstack[i];
         }
     }
 
     /* 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. */
     if (ok && ss.top) {
         do {
             last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP));
         } while (ss.top > pcdepth);
         js_printf(jp, "%s", last);
     }
 
-out:
-    /* Free all temporary stuff allocated under this call. */
-    JS_ARENA_RELEASE(&cx->tempPool, mark);
     return ok;
 }
 
 /*
  * Decompile a function body, expression closure expression, or complete
  * script. Start at |pc|; go to the end of |script|. Include a directive
  * prologue, if appropriate.
  */
@@ -4993,29 +4966,28 @@ js_DecompileFunction(JSPrinter *jp)
         jp->indent += 4;
         js_printf(jp, native_code_str);
         jp->indent -= 4;
         js_printf(jp, "\t}");
     } else {
         JSScript *script = fun->script();
 #if JS_HAS_DESTRUCTURING
         SprintStack ss;
-        void *mark;
 #endif
 
         /* Print the parameters. */
         pc = script->main();
         AutoScriptUntrapper untrapper(jp->sprinter.context, script, &pc);
         endpc = pc + script->length;
         ok = JS_TRUE;
 
 #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++) {
             if (i > 0)
                 js_puts(jp, ", ");
 
             param = GetArgOrVarAtom(jp, i);
 
@@ -5056,17 +5028,17 @@ js_DecompileFunction(JSPrinter *jp)
             if (!QuoteString(&jp->sprinter, param, 0)) {
                 ok = JS_FALSE;
                 break;
             }
         }
 
 #if JS_HAS_DESTRUCTURING
         jp->script = NULL;
-        JS_ARENA_RELEASE(&jp->sprinter.context->tempPool, mark);
+        las.releaseEarly();
 #endif
         if (!ok)
             return JS_FALSE;
         js_printf(jp, ") ");
         if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
             js_printf(jp, "{\n");
             jp->indent += 4;
         }
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -41,17 +41,16 @@
 #define jsopcode_h___
 /*
  * JS bytecode definitions.
  */
 #include <stddef.h>
 #include "jsprvtd.h"
 #include "jspubtd.h"
 #include "jsutil.h"
-#include "jsarena.h"
 
 JS_BEGIN_EXTERN_C
 
 /*
  * JS operation bytecodes.
  */
 typedef enum JSOp {
 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
@@ -500,17 +499,17 @@ DecompileValueGenerator(JSContext *cx, i
     return js_DecompileValueGenerator(cx, spindex, v, fallback);
 }
 
 /*
  * Sprintf, but with unlimited and automatically allocated buffering.
  */
 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 */
 };
 
 #define INIT_SPRINTER(cx, sp, ap, off) \
     ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0,  \
      (sp)->offset = off)
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -51,17 +51,16 @@
  *
  * This parser attempts no error recovery.
  */
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
 #include "jstypes.h"
 #include "jsstdint.h"
-#include "jsarena.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsemit.h"
 #include "jsfun.h"
@@ -204,31 +203,31 @@ Parser::Parser(JSContext *cx, JSPrincipa
 
 bool
 Parser::init(const jschar *base, size_t length, const char *filename, uintN lineno,
              JSVersion version)
 {
     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;
 }
 
 Parser::~Parser()
 {
     JSContext *cx = context;
 
     if (principals)
         JSPRINCIPALS_DROP(cx, principals);
-    JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
+    cx->tempLifoAlloc().release(tempPoolMark);
     cx->activeCompilations--;
 }
 
 void
 Parser::setPrincipals(JSPrincipals *prin)
 {
     JS_ASSERT(!principals);
     if (prin)
@@ -242,18 +241,17 @@ Parser::newObjectBox(JSObject *obj)
     JS_ASSERT(obj);
 
     /*
      * We use JSContext.tempPool to allocate parsed objects and place them on
      * a list in this Parser to ensure GC safety. Thus the tempPool arenas
      * 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_<JSObjectBox>();
     if (!objbox) {
         js_ReportOutOfMemory(context);
         return NULL;
     }
     objbox->traceLink = traceListHead;
     traceListHead = objbox;
     objbox->emitLink = NULL;
     objbox->object = obj;
@@ -268,18 +266,17 @@ Parser::newFunctionBox(JSObject *obj, JS
     JS_ASSERT(obj->isFunction());
 
     /*
      * We use JSContext.tempPool to allocate parsed objects and place them on
      * a list in this Parser to ensure GC safety. Thus the tempPool arenas
      * 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<JSFunctionBox>();
     if (!funbox) {
         js_ReportOutOfMemory(context);
         return NULL;
     }
     funbox->traceLink = traceListHead;
     traceListHead = funbox;
     funbox->emitLink = NULL;
     funbox->object = obj;
@@ -672,18 +669,17 @@ RecycleTree(JSParseNode *pn, JSTreeConte
 static JSParseNode *
 NewOrRecycledNode(JSTreeContext *tc)
 {
     JSParseNode *pn;
 
     pn = tc->parser->nodeList;
     if (!pn) {
         JSContext *cx = tc->parser->context;
-
-        JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
+        pn = cx->tempLifoAlloc().new_<JSParseNode>();
         if (!pn)
             js_ReportOutOfMemory(cx);
     } else {
         tc->parser->nodeList = pn->pn_next;
     }
 
     if (pn) {
         pn->setUsed(false);
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -159,16 +159,18 @@ class UpvarCookie;
 class Proxy;
 class ProxyHandler;
 class Wrapper;
 class CrossCompartmentWrapper;
 
 class TempAllocPolicy;
 class RuntimeAllocPolicy;
 
+class GlobalObject;
+
 template <class T,
           size_t MinInlineCapacity = 0,
           class AllocPolicy = TempAllocPolicy>
 class Vector;
 
 template <class>
 struct DefaultHasher;
 
@@ -183,16 +185,18 @@ template <class T,
           class AllocPolicy = TempAllocPolicy>
 class HashSet;
 
 template <typename K,
           typename V,
           size_t InlineElems>
 class InlineMap;
 
+class LifoAlloc;
+
 class PropertyCache;
 struct PropertyCacheEntry;
 
 struct Shape;
 struct EmptyShape;
 class Bindings;
 
 class MultiDeclRange;
@@ -210,16 +214,36 @@ typedef HashMap<jsbytecode *, Breakpoint
 class Debugger;
 class WatchpointMap;
 
 typedef JSNative             Native;
 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++" */
 
 #else
 
 typedef struct JSAtom JSAtom;
 
--- a/js/src/jsregexpinlines.h
+++ b/js/src/jsregexpinlines.h
@@ -144,17 +144,16 @@ class RegExp
 
     bool compileHelper(JSContext *cx, JSLinearString &pattern, TokenStream *ts);
     bool compile(JSContext *cx, TokenStream *ts);
     static const uint32 allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_MULTILINE | JSREG_STICKY;
 #if !ENABLE_YARR_JIT
     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,
                                 size_t *lastIndex, bool test, Value *rval);
 
   public:
     static inline bool isMetaChar(jschar c);
     static inline bool hasMetaChars(const jschar *chars, size_t length);
@@ -250,37 +249,16 @@ class RegExpMatchBuilder
     bool setInput(JSString *str) {
         JS_ASSERT(str);
         return setProperty(cx->runtime->atomState.inputAtom, StringValue(str));
     }
 };
 
 /* 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)
 {
 #if DEBUG
     size_t inputLength = input->length();
     for (size_t i = 0; i < matchItemCount; i += 2) {
         int start = buf[i];
         int limit = buf[i + 1];
@@ -334,21 +312,18 @@ RegExp::createResult(JSContext *cx, JSSt
 inline bool
 RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inputstr,
                         size_t *lastIndex, bool test, Value *rval)
 {
     const size_t pairCount = parenCount + 1;
     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<int>(bufCount);
+    LifoAllocScope las(&cx->tempLifoAlloc());
+    int *buf = cx->tempLifoAlloc().newArray<int>(bufCount);
     if (!buf)
         return false;
 
     /*
      * The JIT regexp procedure doesn't always initialize matchPair values.
      * Maybe we can make this faster by ensuring it does?
      */
     for (int *it = buf; it != buf + matchItemCount; ++it)
--- a/js/src/jsscan.cpp
+++ b/js/src/jsscan.cpp
@@ -49,17 +49,16 @@
 #ifdef HAVE_MEMORY_H
 #include <memory.h>
 #endif
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
-#include "jsarena.h"
 #include "jsbit.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsemit.h"
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -41,17 +41,16 @@
 /*
  * JS symbol tables.
  */
 #include <new>
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
-#include "jsarena.h"
 #include "jsbit.h"
 #include "jsclist.h"
 #include "jsdhash.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsdbgapi.h"
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -379,37 +379,28 @@ js_XDRScript(JSXDRState *xdr, JSScript *
     }
     JS_ASSERT(nargs != Bindings::BINDING_COUNT_LIMIT);
     JS_ASSERT(nvars != Bindings::BINDING_COUNT_LIMIT);
     JS_ASSERT(nupvars != Bindings::BINDING_COUNT_LIMIT);
 
     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
          * then xdr the names as strings. For argument names (indexes below
          * nargs) the corresponding bit in the bitmap is unset when the name
          * is null. Such null names are not encoded or decoded. For variable
          * names (indexes starting from nargs) bitmap's bit is set when the
          * 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<uint32>(bitmapLength);
         if (!bitmap) {
             js_ReportOutOfMemory(cx);
             return false;
         }
 
         Vector<JSAtom *> names(cx);
         if (xdr->mode == JSXDR_ENCODE) {
             if (!script->bindings.getLocalNameArray(cx, &names))
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -141,18 +141,16 @@ typedef struct JSUpvarArray {
     uint32          length;     /* count of indexed upvar cookies */
 } JSUpvarArray;
 
 typedef struct JSConstArray {
     js::Value       *vector;    /* array of indexed constant values */
     uint32          length;
 } JSConstArray;
 
-struct JSArenaPool;
-
 namespace js {
 
 struct GlobalSlotArray {
     struct Entry {
         uint32      atomIndex;  /* index into atom table */
         uint32      slot;       /* global obj slot number */
     };
     Entry           *vector;
--- a/js/src/jstl.h
+++ b/js/src/jstl.h
@@ -35,17 +35,17 @@
  * 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 jstl_h_
 #define jstl_h_
 
-#include "jspubtd.h"
+#include "jsprvtd.h"
 #include "jsbit.h"
 #include "jsstaticcheck.h"
 #include "jsstdint.h"
 
 #include <new>
 #include <string.h>
 
 namespace js {
@@ -162,16 +162,17 @@ template <> struct IsPodType<unsigned sh
 template <> struct IsPodType<int>                   { static const bool result = true; };
 template <> struct IsPodType<unsigned int>          { static const bool result = true; };
 template <> struct IsPodType<long>                  { static const bool result = true; };
 template <> struct IsPodType<unsigned long>         { static const bool result = true; };
 template <> struct IsPodType<long long>             { static const bool result = true; };
 template <> struct IsPodType<unsigned long long>    { static const bool result = true; };
 template <> struct IsPodType<float>                 { static const bool result = true; };
 template <> struct IsPodType<double>                { static const bool result = true; };
+template <> struct IsPodType<wchar_t>               { static const bool result = true; };
 template <typename T> struct IsPodType<T *>         { static const bool result = true; };
 
 /* Return the size/end of an array without using macros. */
 template <class T, size_t N> inline T *ArraySize(T (&)[N]) { return N; }
 template <class T, size_t N> inline T *ArrayEnd(T (&arr)[N]) { return arr + N; }
 
 template <bool cond, typename T, T v1, T v2> struct If        { static const T result = v1; };
 template <typename T, T v1, T v2> struct If<false, T, v1, v2> { static const T result = v2; };
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -7385,28 +7385,27 @@ TraceRecorder::monitorRecording(JSOp op)
                                         AnyAddress(val_ins->oprnd1(), val_ins->disp()),
                                         snapshot(BRANCH_EXIT));
         set(pendingUnboxSlot, unboxed_ins);
         pendingUnboxSlot = 0;
     }
 
     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,
                             cx->fp()->hasImacropc()
                                 ? 0 : cx->regs().pc - cx->fp()->script()->code,
                             !cx->fp()->hasImacropc(), &sprinter);
 
             fprintf(stdout, "%s", sprinter.base);
-            JS_ARENA_RELEASE(&cx->tempPool, mark);
         }
     )
 
     /*
      * If op is not a break or a return from a loop, continue recording and
      * follow the trace. We check for imacro-calling bytecodes inside each
      * switch case to resolve the if (JSOP_IS_IMACOP(x)) conditions at compile
      * time.
@@ -10410,24 +10409,23 @@ TraceRecorder::record_EnterFrame()
     debug_only_stmt(JSAutoByteString funBytes);
     debug_only_printf(LC_TMTracer, "EnterFrame %s, callDepth=%d\n",
                       cx->fp()->fun()->atom ?
                         js_AtomToPrintableString(cx, cx->fp()->fun()->atom, &funBytes) :
                         "<anonymous>",
                       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");
         }
     )
     LIns* void_ins = w.immiUndefined();
 
     // Before we enter this frame, we need to clear out any dangling insns left
     // in the tracer. While we also clear when returning from a function, it is
     // possible to have the following sequence of stack usage:
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -65,16 +65,18 @@ using namespace JS;
 using namespace mozilla;
 
 }  /* namespace js */
 
 #endif  /* defined __cplusplus */
 
 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)) {                                                        \
             *(int *)(uintptr_t)0xccadbeef = 0;                                  \
             ((void(*)())0)(); /* More reliable, but doesn't say CCADBEEF */     \
         }                                                                       \
     JS_END_MACRO
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1457,23 +1457,22 @@ public:
     }
 };
 
 #ifdef DEBUG
 #define SPEW_OPCODE()                                                         \
     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
 #define SPEW_OPCODE()
 #endif /* DEBUG */
 
 #define BEGIN_CASE(name)        case name:
 #define END_CASE(name)                      \
@@ -6678,17 +6677,17 @@ mjit::Compiler::jumpAndTrace(Jump j, jsb
      * Unless we are coming from a branch which synced everything, syncForBranch
      * must have been called and ensured an allocation at the target.
      */
     RegisterAllocation *lvtarget = NULL;
     bool consistent = true;
     if (cx->typeInferenceEnabled()) {
         RegisterAllocation *&alloc = analysis->getAllocation(target);
         if (!alloc) {
-            alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, false);
+            alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(false);
             if (!alloc)
                 return false;
         }
         lvtarget = alloc;
         consistent = frame.consistentRegisters(target);
     }
 
     if (!addTraceHints || target >= PC ||
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -570,17 +570,17 @@ FrameState::dumpAllocation(RegisterAlloc
     printf("\n");
 }
 #endif
 
 RegisterAllocation *
 FrameState::computeAllocation(jsbytecode *target)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
-    RegisterAllocation *alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, false);
+    RegisterAllocation *alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(false);
     if (!alloc)
         return NULL;
 
     if (a->analysis->getCode(target).exceptionEntry || a->analysis->getCode(target).switchTarget ||
         JSOp(*target) == JSOP_TRAP) {
         /* State must be synced at exception and switch targets, and at traps. */
 #ifdef DEBUG
         if (IsJaegerSpewChannelActive(JSpew_Regalloc)) {
@@ -848,17 +848,17 @@ FrameState::discardForJoin(RegisterAlloc
         return true;
     }
 
     if (!alloc) {
         /*
          * 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<RegisterAllocation>(cx->compartment->pool, false);
+        alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(false);
         if (!alloc)
             return false;
     }
 
     resetInternalState();
     PodArrayZero(regstate_);
 
     Registers regs(Registers::AvailAnyRegs);
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -152,17 +152,17 @@ LoopState::init(jsbytecode *head, Jump e
         JaegerSpew(JSpew_Analysis, "loop modified property at %u: %s %s\n", lifetime->head,
                    types::TypeString(types::Type::ObjectType(modifiedProperties[i].object)),
                    TypeIdString(modifiedProperties[i].id));
     }
 
     RegisterAllocation *&alloc = outerAnalysis->getAllocation(head);
     JS_ASSERT(!alloc);
 
-    alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, true);
+    alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(true);
     if (!alloc)
         return false;
 
     this->alloc = alloc;
     this->loopRegs = Registers::AvailAnyRegs;
 
     /*
      * Don't hoist bounds checks or loop invariant code in scripts that have
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -45,17 +45,16 @@
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <signal.h>
 #include <locale.h>
 #include "jstypes.h"
 #include "jsstdint.h"
-#include "jsarena.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbuiltins.h"
 #include "jscntxt.h"
@@ -1947,34 +1946,30 @@ SrcNotes(JSContext *cx, JSScript *script
         }
         Sprint(sp, "\n");
     }
 }
 
 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;
 
         SrcNotes(cx, script, &sprinter);
     }
 
     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));
     return JS_TRUE;
 }
 
 JS_STATIC_ASSERT(JSTRY_CATCH == 0);
 JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
@@ -2105,19 +2100,19 @@ struct DisassembleOptionParser {
 
 static JSBool
 DisassembleToString(JSContext *cx, uintN argc, jsval *vp)
 {
     DisassembleOptionParser p(argc, JS_ARGV(cx, 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;
     if (p.argc == 0) {
         /* Without arguments, disassemble the current script. */
         if (JSStackFrame *frame = JS_GetScriptedCaller(cx, NULL)) {
             JSScript *script = JS_GetFrameScript(cx, frame);
             if (js_Disassemble(cx, script, p.lines, sp)) {
@@ -2131,33 +2126,32 @@ DisassembleToString(JSContext *cx, uintN
         for (uintN i = 0; i < p.argc; i++) {
             JSFunction *fun;
             JSScript *script = ValueToScript(cx, p.argv[i], &fun);
             ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, sp);
         }
     }
 
     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));
     return true;
 }
 
 static JSBool
 Disassemble(JSContext *cx, uintN argc, jsval *vp)
 {
     DisassembleOptionParser p(argc, JS_ARGV(cx, 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;
     if (p.argc == 0) {
         /* Without arguments, disassemble the current script. */
         if (JSStackFrame *frame = JS_GetScriptedCaller(cx, NULL)) {
             JSScript *script = JS_GetFrameScript(cx, frame);
             if (js_Disassemble(cx, script, p.lines, sp)) {
@@ -2172,17 +2166,16 @@ Disassemble(JSContext *cx, uintN argc, j
             JSFunction *fun;
             JSScript *script = ValueToScript(cx, p.argv[i], &fun);
             ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, sp);
         }
     }
 
     if (ok)
         fprintf(stdout, "%s\n", sprinter.base);
-    JS_ARENA_RELEASE(&cx->tempPool, mark);
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return ok;
 }
 
 static JSBool
 DisassFile(JSContext *cx, uintN argc, jsval *vp)
 {
     /* Support extra options at the start, just like Dissassemble. */
@@ -2208,23 +2201,22 @@ DisassFile(JSContext *cx, uintN argc, js
 
     uint32 oldopts = JS_GetOptions(cx);
     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
     JSScript *script = JS_CompileFile(cx, thisobj, filename.ptr());
     JS_SetOptions(cx, oldopts);
     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;
     
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
 static JSBool
@@ -2258,28 +2250,27 @@ DisassWithSrc(JSContext *cx, uintN argc,
                                  JSSMSG_CANT_OPEN, script->filename,
                                  strerror(errno));
             return JS_FALSE;
         }
 
         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;
             }
         }
 
         bupline = 0;
         while (pc < end) {
             line2 = JS_PCToLineNumber(cx, script, pc);
@@ -2310,17 +2301,16 @@ DisassWithSrc(JSContext *cx, uintN argc,
             if (!len) {
                 ok = JS_FALSE;
                 goto bail;
             }
             pc += len;
         }
 
       bail:
-        JS_ARENA_RELEASE(&cx->tempPool, mark);
         fclose(file);
     }
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return ok;
 #undef LINE_BUF_LEN
 }
 
 static void
--- a/mfbt/Util.h
+++ b/mfbt/Util.h
@@ -172,22 +172,25 @@ struct DebugOnly
         value--;
     }
 
     operator T&() { return value; }
     operator const T&() const { return value; }
 
     T& operator->() { return value; }
 
+    bool operator<(const T& other) { return value < other; }
+
 #else
     DebugOnly() {}
     DebugOnly(const T&) {}
     DebugOnly& operator=(const T&) { return *this; }
     void operator++(int) {}
     void operator--(int) {}
+    bool operator<(const T&) { return false; }
 #endif
 
     /*
      * DebugOnly must always have a destructor or else it will
      * generate "unused variable" warnings, exactly what it's intended
      * to avoid!
      */
     ~DebugOnly() {}