Bug 584860 - Move TraceMonitor into compartment (r=gal,a=blocker)
authorBill McCloskey <wmccloskey@mozilla.com>
Wed, 29 Dec 2010 17:53:58 -0800
changeset 59733 c9682df3daf8f31c3f0d1f8fb4a525d8af9de30b
parent 59732 37068ce988b98c3fbe7196c7e887cc8e21835852
child 59734 f66eefcf11ff7f0849bf1912a807314ee549f714
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersgal, blocker
bugs584860
milestone2.0b9pre
Bug 584860 - Move TraceMonitor into compartment (r=gal,a=blocker)
js/src/jsapi.cpp
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/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2657,17 +2657,17 @@ JS_SetGCParameterForThread(JSContext *cx
 #endif
 }
 
 JS_PUBLIC_API(uint32)
 JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key)
 {
     JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES);
 #ifdef JS_TRACER
-    return JS_THREAD_DATA(cx)->traceMonitor.maxCodeCacheBytes;
+    return JS_THREAD_DATA(cx)->maxCodeCacheBytes;
 #else
     return 0;
 #endif
 }
 
 JS_PUBLIC_API(void)
 JS_FlushCaches(JSContext *cx)
 {
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -489,57 +489,47 @@ JSThreadData::init()
 {
 #ifdef DEBUG
     /* The data must be already zeroed. */
     for (size_t i = 0; i != sizeof(*this); ++i)
         JS_ASSERT(reinterpret_cast<uint8*>(this)[i] == 0);
 #endif
     if (!stackSpace.init())
         return false;
-#ifdef JS_TRACER
-    if (!InitJIT(&traceMonitor)) {
-        finish();
-        return false;
-    }
-#endif
     dtoaState = js_NewDtoaState();
     if (!dtoaState) {
         finish();
         return false;
     }
     nativeStackBase = GetNativeStackBase();
+
+    /* Set the default size for the code cache to 16MB. */
+    maxCodeCacheBytes = 16 * 1024 * 1024;
+    
     return true;
 }
 
 MathCache *
 JSThreadData::allocMathCache(JSContext *cx)
 {
     JS_ASSERT(!mathCache);
     mathCache = new MathCache;
     if (!mathCache)
         js_ReportOutOfMemory(cx);
     return mathCache;
 }
 
 void
 JSThreadData::finish()
 {
-#ifdef DEBUG
-    for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
-        JS_ASSERT(!scriptsToGC[i]);
-#endif
-
     if (dtoaState)
         js_DestroyDtoaState(dtoaState);
 
     js_FinishGSNCache(&gsnCache);
     propertyCache.~PropertyCache();
-#if defined JS_TRACER
-    FinishJIT(&traceMonitor);
-#endif
     stackSpace.finish();
     delete mathCache;
 }
 
 void
 JSThreadData::mark(JSTracer *trc)
 {
     stackSpace.mark(trc);
@@ -548,28 +538,16 @@ JSThreadData::mark(JSTracer *trc)
 void
 JSThreadData::purge(JSContext *cx)
 {
     js_PurgeGSNCache(&gsnCache);
 
     /* FIXME: bug 506341. */
     propertyCache.purge(cx);
 
-#ifdef JS_TRACER
-    /*
-     * If we are about to regenerate shapes, we have to flush the JIT cache,
-     * which will eventually abort any current recording.
-     */
-    if (cx->runtime->gcRegenShapes)
-        traceMonitor.needFlush = JS_TRUE;
-#endif
-
-    /* Destroy eval'ed scripts. */
-    js_DestroyScriptsToGC(cx, this);
-
     /* Purge cached native iterators. */
     memset(cachedNativeIterators, 0, sizeof(cachedNativeIterators));
     lastNativeIterator = NULL;
 
     dtoaCache.s = NULL;
 }
 
 #ifdef JS_THREADSAFE
@@ -731,17 +709,16 @@ js_PurgeThreads(JSContext *cx)
 #ifdef JS_THREADSAFE
     for (JSThread::Map::Enum e(cx->runtime->threads);
          !e.empty();
          e.popFront()) {
         JSThread *thread = e.front().value;
 
         if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
             JS_ASSERT(cx->thread != thread);
-            js_DestroyScriptsToGC(cx, &thread->data);
 
             DestroyThread(thread);
             e.removeFront();
         } else {
             thread->data.purge(cx);
         }
     }
 #else
@@ -916,17 +893,17 @@ DumpEvalCacheMeter(JSContext *cx)
         struct {
             const char *name;
             ptrdiff_t  offset;
         } table[] = {
 #define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
             EVAL_CACHE_METER_LIST(frob)
 #undef frob
         };
-        JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
+        JSEvalCacheMeter *ecm = &cx->compartment->evalCacheMeter;
 
         static JSAutoFile fp;
         if (!fp && !fp.open(filename, "w"))
             return;
 
         fprintf(fp, "eval cache meter (%p):\n",
 #ifdef JS_THREADSAFE
                 (void *) cx->thread
@@ -2311,16 +2288,25 @@ JSContext::updateJITEnabled()
 #ifdef JS_TRACER
     profilingEnabled = (options & JSOPTION_PROFILING) && traceJitEnabled && methodJitEnabled;
 #endif
 #endif
 }
 
 namespace js {
 
+JS_FORCES_STACK JS_FRIEND_API(void)
+LeaveTrace(JSContext *cx)
+{
+#ifdef JS_TRACER
+    if (JS_ON_TRACE(cx))
+        DeepBail(cx);
+#endif
+}
+
 void
 SetPendingException(JSContext *cx, const Value &v)
 {
     cx->throwing = JS_TRUE;
     cx->exception = v;
 }
 
 } /* namespace js */
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -879,187 +879,29 @@ public:
 
     JSStackFrame *fp() const { return curfp; }
 
 private:
     StackSegment *curcs;
     JSStackFrame *curfp;
 };
 
-/* Holds the number of recording attemps for an address. */
-typedef HashMap<jsbytecode*,
-                size_t,
-                DefaultHasher<jsbytecode*>,
-                SystemAllocPolicy> RecordAttemptMap;
-
-/* Holds the profile data for loops. */
-typedef HashMap<jsbytecode*,
-                LoopProfile*,
-                DefaultHasher<jsbytecode*>,
-                SystemAllocPolicy> LoopProfileMap;
-
-class Oracle;
-
-typedef HashSet<JSScript *,
-                DefaultHasher<JSScript *>,
-                SystemAllocPolicy> TracedScriptSet;
-
-/*
- * Trace monitor. Every JSThread (if JS_THREADSAFE) or JSRuntime (if not
- * JS_THREADSAFE) has an associated trace monitor that keeps track of loop
- * frequencies for all JavaScript code loaded into that runtime.
- */
-struct TraceMonitor {
-    /*
-     * The context currently executing JIT-compiled code on this thread, or
-     * NULL if none. Among other things, this can in certain cases prevent
-     * last-ditch GC and suppress calls to JS_ReportOutOfMemory.
-     *
-     * !tracecx && !recorder: not on trace
-     * !tracecx && recorder: recording
-     * tracecx && !recorder: executing a trace
-     * tracecx && recorder: executing inner loop, recording outer loop
-     */
-    JSContext               *tracecx;
-
-    /*
-     * Cached storage to use when executing on trace. While we may enter nested
-     * traces, we always reuse the outer trace's storage, so never need more
-     * than of these.
-     */
-    TraceNativeStorage      *storage;
-
-    /*
-     * There are 4 allocators here.  This might seem like overkill, but they
-     * have different lifecycles, and by keeping them separate we keep the
-     * amount of retained memory down significantly.  They are flushed (ie.
-     * all the allocated memory is freed) periodically.
-     *
-     * - dataAlloc has the lifecycle of the monitor.  It's flushed only when
-     *   the monitor is flushed.  It's used for fragments.
-     *
-     * - traceAlloc has the same flush lifecycle as the dataAlloc, but it is
-     *   also *marked* when a recording starts and rewinds to the mark point
-     *   if recording aborts.  So you can put things in it that are only
-     *   reachable on a successful record/compile cycle like GuardRecords and
-     *   SideExits.
-     *
-     * - tempAlloc is flushed after each recording, successful or not.  It's
-     *   used to store LIR code and for all other elements in the LIR
-     *   pipeline.
-     *
-     * - codeAlloc has the same lifetime as dataAlloc, but its API is
-     *   different (CodeAlloc vs. VMAllocator).  It's used for native code.
-     *   It's also a good idea to keep code and data separate to avoid I-cache
-     *   vs. D-cache issues.
-     */
-    VMAllocator*            dataAlloc;
-    VMAllocator*            traceAlloc;
-    VMAllocator*            tempAlloc;
-    nanojit::CodeAlloc*     codeAlloc;
-    nanojit::Assembler*     assembler;
-    FrameInfoCache*         frameCache;
-
-    /* This gets incremented every time the monitor is flushed. */
-    uintN                   flushEpoch;
-
-    Oracle*                 oracle;
-    TraceRecorder*          recorder;
-
-    /* If we are profiling a loop, this tracks the current profile. Otherwise NULL. */
-    LoopProfile*            profile;
-
-    GlobalState             globalStates[MONITOR_N_GLOBAL_STATES];
-    TreeFragment*           vmfragments[FRAGMENT_TABLE_SIZE];
-    RecordAttemptMap*       recordAttempts;
-
-    /* A hashtable mapping PC values to loop profiles for those loops. */
-    LoopProfileMap*         loopProfiles;
-
-    /*
-     * Maximum size of the code cache before we start flushing. 1/16 of this
-     * size is used as threshold for the regular expression code cache.
-     */
-    uint32                  maxCodeCacheBytes;
-
-    /*
-     * If nonzero, do not flush the JIT cache after a deep bail. That would
-     * free JITted code pages that we will later return to. Instead, set the
-     * needFlush flag so that it can be flushed later.
-     */
-    JSBool                  needFlush;
-
-    /*
-     * Fragment map for the regular expression compiler.
-     */
-    REHashMap*              reFragments;
-
-    // Cached temporary typemap to avoid realloc'ing every time we create one.
-    // This must be used in only one place at a given time. It must be cleared
-    // before use.
-    TypeMap*                cachedTempTypeMap;
-
-    /* Scripts with recorded fragments. */
-    TracedScriptSet         tracedScripts;
-
-#ifdef DEBUG
-    /* Fields needed for fragment/guard profiling. */
-    nanojit::Seq<nanojit::Fragment*>* branches;
-    uint32                  lastFragID;
-    /*
-     * profAlloc has a lifetime which spans exactly from js_InitJIT to
-     * js_FinishJIT.
-     */
-    VMAllocator*            profAlloc;
-    FragStatsMap*           profTab;
-#endif
-
-    bool ontrace() const {
-        return !!tracecx;
-    }
-
-    /* Flush the JIT cache. */
-    void flush();
-
-    /* Sweep any cache entry pointing to dead GC things. */
-    void sweep();
-
-    bool outOfMemory() const;
-};
-
 } /* namespace js */
 
 /*
  * N.B. JS_ON_TRACE(cx) is true if JIT code is on the stack in the current
  * thread, regardless of whether cx is the context in which that trace is
  * executing.  cx must be a context on the current thread.
  */
 #ifdef JS_TRACER
 # define JS_ON_TRACE(cx)            (JS_TRACE_MONITOR(cx).ontrace())
 #else
 # define JS_ON_TRACE(cx)            false
 #endif
 
-/* Number of potentially reusable scriptsToGC to search for the eval cache. */
-#ifndef JS_EVAL_CACHE_SHIFT
-# define JS_EVAL_CACHE_SHIFT        6
-#endif
-#define JS_EVAL_CACHE_SIZE          JS_BIT(JS_EVAL_CACHE_SHIFT)
-
-#ifdef DEBUG
-# define EVAL_CACHE_METER_LIST(_)   _(probe), _(hit), _(step), _(noscope)
-# define identity(x)                x
-
-struct JSEvalCacheMeter {
-    uint64 EVAL_CACHE_METER_LIST(identity);
-};
-
-# undef identity
-#endif
-
 #ifdef DEBUG
 # define FUNCTION_KIND_METER_LIST(_)                                          \
                         _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar),  \
                         _(display), _(flat), _(setupvar), _(badfunarg),       \
                         _(joinedsetmethod), _(joinedinitmethod),              \
                         _(joinedreplace), _(joinedsort), _(joinedmodulepat),  \
                         _(mreadbarrier), _(mwritebarrier), _(mwslotbarrier),  \
                         _(unjoined)
@@ -1113,28 +955,21 @@ struct JSThreadData {
      * do not interleave js_GetSrcNote calls.
      */
     JSGSNCache          gsnCache;
 
     /* Property cache for faster call/get/set invocation. */
     js::PropertyCache   propertyCache;
 
 #ifdef JS_TRACER
-    /* Trace-tree JIT recorder/interpreter state. */
-    js::TraceMonitor    traceMonitor;
-
     /* Counts the number of iterations run by a trace. */
     unsigned            iterationCounter;
-#endif
-
-    /* Lock-free hashed lists of scripts created by eval to garbage-collect. */
-    JSScript            *scriptsToGC[JS_EVAL_CACHE_SIZE];
-
-#ifdef DEBUG
-    JSEvalCacheMeter    evalCacheMeter;
+
+    /* Maximum size of the tracer's code cache before we start flushing. */
+    uint32                  maxCodeCacheBytes;
 #endif
 
     /* State used by dtoa.c. */
     DtoaState           *dtoaState;
 
     /*
      * A single-entry cache for some base-10 double-to-string conversions.
      * This helps date-format-xparb.js.  It also avoids skewing the results
@@ -1720,24 +1555,16 @@ struct JSRuntime {
      * The function must be called outside the GC lock.
      */
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
 };
 
 /* Common macros to access thread-local caches in JSThread or JSRuntime. */
 #define JS_GSN_CACHE(cx)        (JS_THREAD_DATA(cx)->gsnCache)
 #define JS_PROPERTY_CACHE(cx)   (JS_THREAD_DATA(cx)->propertyCache)
-#define JS_TRACE_MONITOR(cx)    (JS_THREAD_DATA(cx)->traceMonitor)
-#define JS_SCRIPTS_TO_GC(cx)    (JS_THREAD_DATA(cx)->scriptsToGC)
-
-#ifdef DEBUG
-# define EVAL_CACHE_METER(x)    (JS_THREAD_DATA(cx)->evalCacheMeter.x++)
-#else
-# define EVAL_CACHE_METER(x)    ((void) 0)
-#endif
 
 #ifdef DEBUG
 # define JS_RUNTIME_METER(rt, which)    JS_ATOMIC_INCREMENT(&(rt)->which)
 # define JS_RUNTIME_UNMETER(rt, which)  JS_ATOMIC_DECREMENT(&(rt)->which)
 #else
 # define JS_RUNTIME_METER(rt, which)    /* nothing */
 # define JS_RUNTIME_UNMETER(rt, which)  /* nothing */
 #endif
@@ -3233,60 +3060,24 @@ js_GetScriptedCaller(JSContext *cx, JSSt
 extern jsbytecode*
 js_GetCurrentBytecodePC(JSContext* cx);
 
 extern bool
 js_CurrentPCIsInImacro(JSContext *cx);
 
 namespace js {
 
-#ifdef JS_TRACER
-/*
- * Reconstruct the JS stack and clear cx->tracecx. We must be currently in a
- * _FAIL builtin from trace on cx or another context on the same thread. The
- * machine code for the trace remains on the C stack when js_DeepBail returns.
- *
- * Implemented in jstracer.cpp.
- */
-JS_FORCES_STACK JS_FRIEND_API(void)
-DeepBail(JSContext *cx);
-#endif
-
-static JS_FORCES_STACK JS_INLINE void
-LeaveTrace(JSContext *cx)
-{
-#ifdef JS_TRACER
-    if (JS_ON_TRACE(cx))
-        DeepBail(cx);
-#endif
-}
-
-static JS_INLINE void
-LeaveTraceIfGlobalObject(JSContext *cx, JSObject *obj)
-{
-    if (!obj->parent)
-        LeaveTrace(cx);
-}
-
-static JS_INLINE JSBool
-CanLeaveTrace(JSContext *cx)
-{
-    JS_ASSERT(JS_ON_TRACE(cx));
-#ifdef JS_TRACER
-    return cx->bailExit != NULL;
-#else
-    return JS_FALSE;
-#endif
-}
-
 extern void
 SetPendingException(JSContext *cx, const Value &v);
 
 class RegExpStatics;
 
+extern JS_FORCES_STACK JS_FRIEND_API(void)
+LeaveTrace(JSContext *cx);
+
 } /* namespace js */
 
 /*
  * Get the current frame, first lazily instantiating stack frames if needed.
  * (Do not access cx->fp() directly except in JS_REQUIRES_STACK code.)
  *
  * Defined in jstracer.cpp if JS_TRACER is defined.
  */
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -37,16 +37,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jscntxtinlines_h___
 #define jscntxtinlines_h___
 
 #include "jscntxt.h"
+#include "jscompartment.h"
 #include "jsparse.h"
 #include "jsstaticcheck.h"
 #include "jsxml.h"
 #include "jsregexp.h"
 #include "jsgc.h"
 #include "jscompartment.h"
 
 namespace js {
@@ -748,11 +749,41 @@ CallSetter(JSContext *cx, JSObject *obj,
     if (attrs & JSPROP_GETTER)
         return js_ReportGetterOnlyAssignment(cx);
 
     if (attrs & JSPROP_SHORTID)
         id = INT_TO_JSID(shortid);
     return CallJSPropertyOpSetter(cx, op, obj, id, vp);
 }
 
+#ifdef JS_TRACER
+/*
+ * Reconstruct the JS stack and clear cx->tracecx. We must be currently in a
+ * _FAIL builtin from trace on cx or another context on the same thread. The
+ * machine code for the trace remains on the C stack when js_DeepBail returns.
+ *
+ * Implemented in jstracer.cpp.
+ */
+JS_FORCES_STACK JS_FRIEND_API(void)
+DeepBail(JSContext *cx);
+#endif
+
+static JS_INLINE void
+LeaveTraceIfGlobalObject(JSContext *cx, JSObject *obj)
+{
+    if (!obj->parent)
+        LeaveTrace(cx);
+}
+
+static JS_INLINE JSBool
+CanLeaveTrace(JSContext *cx)
+{
+    JS_ASSERT(JS_ON_TRACE(cx));
+#ifdef JS_TRACER
+    return cx->bailExit != NULL;
+#else
+    return JS_FALSE;
+#endif
+}
+
 }  /* namespace js */
 
 #endif /* jscntxtinlines_h___ */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -39,56 +39,77 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jsproxy.h"
 #include "jsscope.h"
+#include "jstracer.h"
 #include "methodjit/MethodJIT.h"
 #include "methodjit/PolyIC.h"
 #include "methodjit/MonoIC.h"
 
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 JSCompartment::JSCompartment(JSRuntime *rt)
   : rt(rt), principals(NULL), data(NULL), marked(false), debugMode(rt->debugMode),
     anynameObject(NULL), functionNamespaceObject(NULL)
 {
     JS_INIT_CLIST(&scripts);
+
+    memset(scriptsToGC, 0, sizeof(scriptsToGC));
 }
 
 JSCompartment::~JSCompartment()
 {
+#if defined JS_TRACER
+    FinishJIT(&traceMonitor);
+#endif
 #ifdef JS_METHODJIT
     delete jaegerCompartment;
 #endif
+
+#ifdef DEBUG
+    for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
+        JS_ASSERT(!scriptsToGC[i]);
+#endif
 }
 
 bool
 JSCompartment::init()
 {
     chunk = NULL;
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         arenas[i].init();
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         freeLists.finalizables[i] = NULL;
 #ifdef JS_GCMETER
     memset(&compartmentStats, 0, sizeof(JSGCArenaStats) * FINALIZE_LIMIT);
 #endif
     if (!crossCompartmentWrappers.init())
         return false;
 
+#ifdef JS_TRACER
+    if (!InitJIT(&traceMonitor)) {
+        return false;
+    }
+#endif
+
 #ifdef JS_METHODJIT
-    if (!(jaegerCompartment = new mjit::JaegerCompartment))
+    if (!(jaegerCompartment = new mjit::JaegerCompartment)) {
+#ifdef JS_TRACER
+        FinishJIT(&traceMonitor);
+#endif
         return false;
+    }
     return jaegerCompartment->Initialize();
 #else
     return true;
 #endif
 }
 
 bool
 JSCompartment::arenaListsAreEmpty()
@@ -352,16 +373,28 @@ JSCompartment::sweep(JSContext *cx)
 #endif
 }
 
 void
 JSCompartment::purge(JSContext *cx)
 {
     freeLists.purge();
 
+    /* Destroy eval'ed scripts. */
+    js_DestroyScriptsToGC(cx, this);
+
+#ifdef JS_TRACER
+    /*
+     * If we are about to regenerate shapes, we have to flush the JIT cache,
+     * which will eventually abort any current recording.
+     */
+    if (cx->runtime->gcRegenShapes)
+        traceMonitor.needFlush = JS_TRUE;
+#endif
+
 #ifdef JS_METHODJIT
     for (JSScript *script = (JSScript *)scripts.next;
          &script->links != &scripts;
          script = (JSScript *)script->links.next) {
         if (script->hasJITCode()) {
 # if defined JS_POLYIC
             mjit::ic::PurgePICs(cx, script);
 # endif
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -50,33 +50,204 @@
 #include "jsxml.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4251) /* Silence warning about JS_FRIEND_API and data members. */
 #endif
 
 namespace js {
+
+/* Holds the number of recording attemps for an address. */
+typedef HashMap<jsbytecode*,
+                size_t,
+                DefaultHasher<jsbytecode*>,
+                SystemAllocPolicy> RecordAttemptMap;
+
+/* Holds the profile data for loops. */
+typedef HashMap<jsbytecode*,
+                LoopProfile*,
+                DefaultHasher<jsbytecode*>,
+                SystemAllocPolicy> LoopProfileMap;
+
+class Oracle;
+
+typedef HashSet<JSScript *,
+                DefaultHasher<JSScript *>,
+                SystemAllocPolicy> TracedScriptSet;
+
+/*
+ * Trace monitor. Every JSThread (if JS_THREADSAFE) or JSRuntime (if not
+ * JS_THREADSAFE) has an associated trace monitor that keeps track of loop
+ * frequencies for all JavaScript code loaded into that runtime.
+ */
+struct TraceMonitor {
+    /*
+     * The context currently executing JIT-compiled code on this thread, or
+     * NULL if none. Among other things, this can in certain cases prevent
+     * last-ditch GC and suppress calls to JS_ReportOutOfMemory.
+     *
+     * !tracecx && !recorder: not on trace
+     * !tracecx && recorder: recording
+     * tracecx && !recorder: executing a trace
+     * tracecx && recorder: executing inner loop, recording outer loop
+     */
+    JSContext               *tracecx;
+
+    /*
+     * Cached storage to use when executing on trace. While we may enter nested
+     * traces, we always reuse the outer trace's storage, so never need more
+     * than of these.
+     */
+    TraceNativeStorage      *storage;
+
+    /*
+     * There are 4 allocators here.  This might seem like overkill, but they
+     * have different lifecycles, and by keeping them separate we keep the
+     * amount of retained memory down significantly.  They are flushed (ie.
+     * all the allocated memory is freed) periodically.
+     *
+     * - dataAlloc has the lifecycle of the monitor.  It's flushed only when
+     *   the monitor is flushed.  It's used for fragments.
+     *
+     * - traceAlloc has the same flush lifecycle as the dataAlloc, but it is
+     *   also *marked* when a recording starts and rewinds to the mark point
+     *   if recording aborts.  So you can put things in it that are only
+     *   reachable on a successful record/compile cycle like GuardRecords and
+     *   SideExits.
+     *
+     * - tempAlloc is flushed after each recording, successful or not.  It's
+     *   used to store LIR code and for all other elements in the LIR
+     *   pipeline.
+     *
+     * - codeAlloc has the same lifetime as dataAlloc, but its API is
+     *   different (CodeAlloc vs. VMAllocator).  It's used for native code.
+     *   It's also a good idea to keep code and data separate to avoid I-cache
+     *   vs. D-cache issues.
+     */
+    VMAllocator*            dataAlloc;
+    VMAllocator*            traceAlloc;
+    VMAllocator*            tempAlloc;
+    nanojit::CodeAlloc*     codeAlloc;
+    nanojit::Assembler*     assembler;
+    FrameInfoCache*         frameCache;
+
+    /* This gets incremented every time the monitor is flushed. */
+    uintN                   flushEpoch;
+
+    Oracle*                 oracle;
+    TraceRecorder*          recorder;
+
+    /* If we are profiling a loop, this tracks the current profile. Otherwise NULL. */
+    LoopProfile*            profile;
+
+    GlobalState             globalStates[MONITOR_N_GLOBAL_STATES];
+    TreeFragment*           vmfragments[FRAGMENT_TABLE_SIZE];
+    RecordAttemptMap*       recordAttempts;
+
+    /* A hashtable mapping PC values to loop profiles for those loops. */
+    LoopProfileMap*         loopProfiles;
+
+    /*
+     * Maximum size of the code cache before we start flushing. 1/16 of this
+     * size is used as threshold for the regular expression code cache.
+     */
+    uint32                  maxCodeCacheBytes;
+
+    /*
+     * If nonzero, do not flush the JIT cache after a deep bail. That would
+     * free JITted code pages that we will later return to. Instead, set the
+     * needFlush flag so that it can be flushed later.
+     */
+    JSBool                  needFlush;
+
+    /*
+     * Fragment map for the regular expression compiler.
+     */
+    REHashMap*              reFragments;
+
+    // Cached temporary typemap to avoid realloc'ing every time we create one.
+    // This must be used in only one place at a given time. It must be cleared
+    // before use.
+    TypeMap*                cachedTempTypeMap;
+
+    /* Scripts with recorded fragments. */
+    TracedScriptSet         tracedScripts;
+
+#ifdef DEBUG
+    /* Fields needed for fragment/guard profiling. */
+    nanojit::Seq<nanojit::Fragment*>* branches;
+    uint32                  lastFragID;
+    /*
+     * profAlloc has a lifetime which spans exactly from js_InitJIT to
+     * js_FinishJIT.
+     */
+    VMAllocator*            profAlloc;
+    FragStatsMap*           profTab;
+#endif
+
+    bool ontrace() const {
+        return !!tracecx;
+    }
+
+    /* Flush the JIT cache. */
+    void flush();
+
+    /* Sweep any cache entry pointing to dead GC things. */
+    void sweep();
+
+    bool outOfMemory() const;
+};
+
 namespace mjit {
 class JaegerCompartment;
 }
 }
 
+/* Number of potentially reusable scriptsToGC to search for the eval cache. */
+#ifndef JS_EVAL_CACHE_SHIFT
+# define JS_EVAL_CACHE_SHIFT        6
+#endif
+#define JS_EVAL_CACHE_SIZE          JS_BIT(JS_EVAL_CACHE_SHIFT)
+
+#ifdef DEBUG
+# define EVAL_CACHE_METER_LIST(_)   _(probe), _(hit), _(step), _(noscope)
+# define identity(x)                x
+
+struct JSEvalCacheMeter {
+    uint64 EVAL_CACHE_METER_LIST(identity);
+};
+
+# undef identity
+#endif
+
 struct JS_FRIEND_API(JSCompartment) {
     JSRuntime                    *rt;
     JSPrincipals                 *principals;
     js::gc::Chunk                *chunk;
 
     js::gc::ArenaList            arenas[js::gc::FINALIZE_LIMIT];
     js::gc::FreeLists            freeLists;
 
 #ifdef JS_GCMETER
     js::gc::JSGCArenaStats       compartmentStats[js::gc::FINALIZE_LIMIT];
 #endif
 
+#ifdef JS_TRACER
+    /* Trace-tree JIT recorder/interpreter state. */
+    js::TraceMonitor traceMonitor;
+#endif
+
+    /* Lock-free hashed lists of scripts created by eval to garbage-collect. */
+    JSScript                     *scriptsToGC[JS_EVAL_CACHE_SIZE];
+
+#ifdef DEBUG
+    JSEvalCacheMeter    evalCacheMeter;
+#endif
+
     void                         *data;
     bool                         marked;
     js::WrapperMap               crossCompartmentWrappers;
 
 #ifdef JS_METHODJIT
     js::mjit::JaegerCompartment  *jaegerCompartment;
 #endif
 
@@ -108,16 +279,25 @@ struct JS_FRIEND_API(JSCompartment) {
     bool wrapException(JSContext *cx);
 
     void sweep(JSContext *cx);
     void purge(JSContext *cx);
     void finishArenaLists();
     bool arenaListsAreEmpty();
 };
 
+#define JS_TRACE_MONITOR(cx)    (cx->compartment->traceMonitor)
+#define JS_SCRIPTS_TO_GC(cx)    (cx->compartment->scriptsToGC)
+
+#ifdef DEBUG
+# define EVAL_CACHE_METER(x)    (cx->compartment->evalCacheMeter.x++)
+#else
+# define EVAL_CACHE_METER(x)    ((void) 0)
+#endif
+
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
 namespace js {
 
 class PreserveCompartment {
   protected:
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -480,43 +480,31 @@ JITInhibitingHookChange(JSRuntime *rt, b
             for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
                 js_ContextFromLinkField(cl)->updateJITEnabled();
         }
     } else if (rt->debuggerInhibitsJIT()) {
         for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
             js_ContextFromLinkField(cl)->traceJitEnabled = false;
     }
 }
-
-static void
-LeaveTraceRT(JSRuntime *rt)
-{
-    JSThreadData *data = js_CurrentThreadData(rt);
-    JSContext *cx = data ? data->traceMonitor.tracecx : NULL;
-    JS_UNLOCK_GC(rt);
-
-    if (cx)
-        LeaveTrace(cx);
-}
 #endif
 
 JS_PUBLIC_API(JSBool)
 JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
 {
 #ifdef JS_TRACER
     {
         AutoLockGC lock(rt);
         bool wasInhibited = rt->debuggerInhibitsJIT();
 #endif
         rt->globalDebugHooks.interruptHook = hook;
         rt->globalDebugHooks.interruptHookData = closure;
 #ifdef JS_TRACER
         JITInhibitingHookChange(rt, wasInhibited);
     }
-    LeaveTraceRT(rt);
 #endif
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
 {
 #ifdef JS_TRACER
@@ -1667,18 +1655,16 @@ JS_SetCallHook(JSRuntime *rt, JSInterpre
         AutoLockGC lock(rt);
         bool wasInhibited = rt->debuggerInhibitsJIT();
 #endif
         rt->globalDebugHooks.callHook = hook;
         rt->globalDebugHooks.callHookData = closure;
 #ifdef JS_TRACER
         JITInhibitingHookChange(rt, wasInhibited);
     }
-    if (hook)
-        LeaveTraceRT(rt);
 #endif
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
 {
     rt->globalDebugHooks.throwHook = hook;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2055,17 +2055,17 @@ fun_finalize(JSContext *cx, JSObject *ob
         return;
     }
 
     /*
      * Null-check of u.i.script is required since the parser sets interpreted
      * very early.
      */
     if (FUN_INTERPRETED(fun) && fun->u.i.script)
-        js_DestroyScriptFromGC(cx, fun->u.i.script, NULL);
+        js_DestroyScriptFromGC(cx, fun->u.i.script, obj->compartment());
 }
 
 int
 JSFunction::sharpSlotBase(JSContext *cx)
 {
 #if JS_HAS_SHARP_VARS
     JSAtom *name = js_Atomize(cx, "#array", 6, 0);
     if (name) {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1727,26 +1727,26 @@ TriggerGC(JSRuntime *rt)
      */
     rt->gcIsNeeded = true;
     TriggerAllOperationCallbacks(rt);
 }
 
 } /* namespace js */
 
 void
-js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data)
+js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp)
 {
     JSScript **listp, *script;
 
-    for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) {
-        listp = &data->scriptsToGC[i];
+    for (size_t i = 0; i != JS_ARRAY_LENGTH(comp->scriptsToGC); ++i) {
+        listp = &comp->scriptsToGC[i];
         while ((script = *listp) != NULL) {
             *listp = script->u.nextToGC;
             script->u.nextToGC = NULL;
-            js_DestroyScriptFromGC(cx, script, data);
+            js_DestroyScriptFromGC(cx, script, comp);
         }
     }
 }
 
 /*
  * This function is called from js_FinishAtomState to force the finalization
  * of the permanently interned strings when cx is not available.
  */
@@ -2173,18 +2173,19 @@ MarkAndSweep(JSContext *cx, JSGCInvocati
     js_SweepWatchPoints(cx);
 
 #ifdef DEBUG
     /* Save the pre-sweep count of scope-mapped properties. */
     rt->liveObjectPropsPreSweep = rt->liveObjectProps;
 #endif
 
 #ifdef JS_TRACER
-    for (ThreadDataIter i(rt); !i.empty(); i.popFront())
-        i.threadData()->traceMonitor.sweep();
+    for (JSCompartment **comp = rt->compartments.begin(); comp != rt->compartments.end(); comp++) {
+        (*comp)->traceMonitor.sweep();
+    }
 #endif
 
     /*
      * We finalize iterators before other objects so the iterator can use the
      * object which properties it enumerates over to finalize the enumeration
      * state. We finalize objects before other GC things to ensure that
      * object's finalizer can access them even if they will be freed.
      */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -822,17 +822,17 @@ js_WaitForGC(JSRuntime *rt);
 
 #else /* !JS_THREADSAFE */
 
 # define js_WaitForGC(rt)    ((void) 0)
 
 #endif
 
 extern void
-js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data);
+js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp);
 
 namespace js {
 
 #ifdef JS_THREADSAFE
 
 /*
  * During the finalization we do not free immediately. Rather we add the
  * corresponding pointers to a buffer which we later release on a separated
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -417,17 +417,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
 
 #endif /* JS_HAS_XDR */
 
 static void
 script_finalize(JSContext *cx, JSObject *obj)
 {
     JSScript *script = (JSScript *) obj->getPrivate();
     if (script)
-        js_DestroyScriptFromGC(cx, script, NULL);
+        js_DestroyScriptFromGC(cx, script, obj->compartment());
 }
 
 static void
 script_trace(JSTracer *trc, JSObject *obj)
 {
     JSScript *script = (JSScript *) obj->getPrivate();
     if (script)
         js_TraceScript(trc, script);
@@ -1213,17 +1213,17 @@ js_CallDestroyScriptHook(JSContext *cx, 
     JSDestroyScriptHook hook;
 
     hook = cx->debugHooks->destroyScriptHook;
     if (hook)
         hook(cx, script, cx->debugHooks->destroyScriptHookData);
 }
 
 static void
-DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data)
+DestroyScript(JSContext *cx, JSScript *script, JSCompartment *comp)
 {
 #ifdef DEBUG
     if (script->isEmpty())
         JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
     else
         JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
 #endif
 
@@ -1272,48 +1272,39 @@ DestroyScript(JSContext *cx, JSScript *s
 
 #ifdef CHECK_SCRIPT_OWNER
             JS_ASSERT(script->owner == cx->thread);
 #endif
         }
     }
 
 #ifdef JS_TRACER
-# ifdef JS_THREADSAFE
-    if (data) {
-        PurgeScriptFragments(&data->traceMonitor, script);
-    } else {
-        for (ThreadDataIter i(cx->runtime); !i.empty(); i.popFront())
-            PurgeScriptFragments(&i.threadData()->traceMonitor, script);
-    }
-# else
-    PurgeScriptFragments(&JS_TRACE_MONITOR(cx), script);
-# endif
+    PurgeScriptFragments(&comp->traceMonitor, script);
 #endif
 
 #if defined(JS_METHODJIT)
     mjit::ReleaseScriptCode(cx, script);
 #endif
     JS_REMOVE_LINK(&script->links);
 
     cx->free(script);
 }
 
 void
 js_DestroyScript(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(!cx->runtime->gcRunning);
-    DestroyScript(cx, script, JS_THREAD_DATA(cx));
+    DestroyScript(cx, script, cx->compartment);
 }
 
 void
-js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSThreadData *data)
+js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSCompartment *comp)
 {
     JS_ASSERT(cx->runtime->gcRunning);
-    DestroyScript(cx, script, data);
+    DestroyScript(cx, script, comp);
 }
 
 void
 js_TraceScript(JSTracer *trc, JSScript *script)
 {
     JSAtomMap *map = &script->atomMap;
     MarkAtomRange(trc, map->length, map->vector, "atomMap");
 
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -497,17 +497,17 @@ js_CallDestroyScriptHook(JSContext *cx, 
 extern void
 js_DestroyScript(JSContext *cx, JSScript *script);
 
 /*
  * If data is not null, it indicates that the script could been accessed only
  * from that thread.
  */
 extern void
-js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSThreadData *data);
+js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSCompartment *comp);
 
 extern void
 js_TraceScript(JSTracer *trc, JSScript *script);
 
 extern JSBool
 js_NewScriptObject(JSContext *cx, JSScript *script);
 
 /*
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -55,16 +55,17 @@
 #endif
 #include <limits.h>
 
 #include "nanojit/nanojit.h"
 #include "jsapi.h"              // higher-level library and API headers
 #include "jsarray.h"
 #include "jsbool.h"
 #include "jscntxt.h"
+#include "jscompartment.h"
 #include "jsdate.h"
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jsmath.h"
 #include "jsobj.h"
@@ -2425,17 +2426,17 @@ TraceRecorder::finishSuccessfully()
     /* Grab local copies of members needed after |delete this|. */
     JSContext* localcx = cx;
     TraceMonitor* localtm = traceMonitor;
 
     localtm->recorder = NULL;
     delete this;
 
     /* Catch OOM that occurred during recording. */
-    if (localtm->outOfMemory() || OverfullJITCache(localtm)) {
+    if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) {
         ResetJIT(localcx, FR_OOM);
         return ARECORD_ABORTED;
     }
     return ARECORD_COMPLETED;
 }
 
 /* This function aborts a recorder and any pending outer recorders. */
 JS_REQUIRES_STACK TraceRecorder::AbortResult
@@ -2475,17 +2476,17 @@ TraceRecorder::finishAbort(const char* r
     }
 
     /* Grab local copies of members needed after |delete this|. */
     JSContext* localcx = cx;
     TraceMonitor* localtm = traceMonitor;
 
     localtm->recorder = NULL;
     delete this;
-    if (localtm->outOfMemory() || OverfullJITCache(localtm)) {
+    if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) {
         ResetJIT(localcx, FR_OOM);
         return JIT_RESET;
     }
     return NORMAL_ABORT;
 }
 
 inline LIns*
 TraceRecorder::w_immpObjGC(JSObject* obj)
@@ -5517,17 +5518,17 @@ TraceRecorder::startRecorder(JSContext* 
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
     JS_ASSERT(!tm->needFlush);
     JS_ASSERT_IF(cx->fp()->hasImacropc(), f->root != f);
 
     tm->recorder = new TraceRecorder(cx, anchor, f, stackSlots, ngslots, typeMap,
                                      expectedInnerExit, outerScript, outerPC, outerArgc,
                                      speculate);
 
-    if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(tm)) {
+    if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(cx, tm)) {
         ResetJIT(cx, FR_OOM);
         return false;
     }
 
     return true;
 }
 
 static void
@@ -5642,19 +5643,19 @@ RecordTree(JSContext* cx, TreeFragment* 
     if (!CheckGlobalObjectShape(cx, tm, f->globalObj)) {
         Backoff(cx, (jsbytecode*) localRootIP);
         return false;
     }
 
     AUDIT(recorderStarted);
 
     if (tm->outOfMemory() ||
-        OverfullJITCache(tm) ||
+        OverfullJITCache(cx, tm) ||
         !tm->tracedScripts.put(cx->fp()->script())) {
-        if (!OverfullJITCache(tm))
+        if (!OverfullJITCache(cx, tm))
             js_ReportOutOfMemory(cx);
         Backoff(cx, (jsbytecode*) f->root->ip);
         ResetJIT(cx, FR_OOM);
         debug_only_print0(LC_TMTracer,
                           "Out of memory recording new tree, flushing cache.\n");
         return false;
     }
 
@@ -7252,17 +7253,17 @@ TraceRecorder::monitorRecording(JSOp op)
             return ARECORD_CONTINUE;
 
         /* Handle lazy aborts; propagate the 'error' status. */
         if (StatusAbortsRecorderIfActive(status)) {
             AbortRecording(cx, js_CodeName[op]);
             return status == ARECORD_ERROR ? ARECORD_ERROR : ARECORD_ABORTED;
         }
 
-        if (outOfMemory() || OverfullJITCache(&localtm)) {
+        if (outOfMemory() || OverfullJITCache(cx, &localtm)) {
             ResetJIT(cx, FR_OOM);
 
             /*
              * If the status returned was ARECORD_IMACRO, then we just
              * changed cx->regs, we need to tell the interpreter to sync
              * its local variables.
              */
             return status == ARECORD_IMACRO ? ARECORD_IMACRO_ABORTED : ARECORD_ABORTED;
@@ -7521,28 +7522,31 @@ disable_debugger_exceptions() { }
 
 #define K *1024
 #define M K K
 #define G K M
 
 void
 SetMaxCodeCacheBytes(JSContext* cx, uint32 bytes)
 {
-    TraceMonitor* tm = &JS_THREAD_DATA(cx)->traceMonitor;
+    TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     JS_ASSERT(tm->codeAlloc && tm->dataAlloc && tm->traceAlloc);
     if (bytes > 1 G)
         bytes = 1 G;
     if (bytes < 128 K)
         bytes = 128 K;
-    tm->maxCodeCacheBytes = bytes;
+    JS_THREAD_DATA(cx)->maxCodeCacheBytes = bytes;
 }
 
 bool
 InitJIT(TraceMonitor *tm)
 {
+    // InitJIT expects this area to be zero'd
+    memset(tm, 0, sizeof(*tm));
+
 #if defined JS_JIT_SPEW
     tm->profAlloc = NULL;
     /* Set up debug logging. */
     if (!did_we_set_up_debug_logging) {
         InitJITLogController();
         did_we_set_up_debug_logging = true;
     }
     /* Set up fragprofiling, if required. */
@@ -7576,34 +7580,30 @@ InitJIT(TraceMonitor *tm)
 
         // Sanity-check the configuration detection.
         //  * We don't understand architectures prior to ARMv4.
         JS_ASSERT(arm_arch >= 4);
 #endif
         did_we_check_processor_features = true;
     }
 
-    /* Set the default size for the code cache to 16MB. */
-    tm->maxCodeCacheBytes = 16 M;
-
     tm->oracle = new Oracle();
 
     tm->profile = NULL;
     
     tm->recordAttempts = new RecordAttemptMap;
     if (!tm->recordAttempts->init(PC_HASH_COUNT))
         abort();
 
     tm->loopProfiles = new LoopProfileMap;
     if (!tm->loopProfiles->init(PC_HASH_COUNT))
         abort();
 
     tm->flushEpoch = 0;
     
-    JS_ASSERT(!tm->dataAlloc && !tm->traceAlloc && !tm->codeAlloc);
     tm->dataAlloc = new VMAllocator();
     tm->traceAlloc = new VMAllocator();
     tm->tempAlloc = new VMAllocator();
     tm->codeAlloc = new CodeAlloc();
     tm->frameCache = new FrameInfoCache(tm->dataAlloc);
     tm->storage = new TraceNativeStorage();
     tm->cachedTempTypeMap = new TypeMap(0);
     tm->flush();
@@ -7788,17 +7788,17 @@ PurgeScriptFragments(TraceMonitor* tm, J
     RecordAttemptMap &table = *tm->recordAttempts;
     for (RecordAttemptMap::Enum e(table); !e.empty(); e.popFront()) {
         if (JS_UPTRDIFF(e.front().key, script->code) < script->length)
             e.removeFront();
     }
 }
 
 bool
-OverfullJITCache(TraceMonitor* tm)
+OverfullJITCache(JSContext *cx, TraceMonitor* tm)
 {
     /*
      * You might imagine the outOfMemory flag on the allocator is sufficient
      * to model the notion of "running out of memory", but there are actually
      * two separate issues involved:
      *
      *  1. The process truly running out of memory: malloc() or mmap()
      *     failed.
@@ -7824,17 +7824,17 @@ OverfullJITCache(TraceMonitor* tm)
      * time being: condition 1 is handled by the outOfMemory flag inside
      * nanojit, and condition 2 is being handled independently *here*. So
      * we construct our allocators to use all available memory they like,
      * and only report outOfMemory to us when there is literally no OS memory
      * left. Merely purging our cache when we hit our highwater mark is
      * handled by the (few) callers of this function.
      *
      */
-    jsuint maxsz = tm->maxCodeCacheBytes;
+    jsuint maxsz = JS_THREAD_DATA(cx)->maxCodeCacheBytes;
     VMAllocator *dataAlloc = tm->dataAlloc;
     VMAllocator *traceAlloc = tm->traceAlloc;
     CodeAlloc *codeAlloc = tm->codeAlloc;
 
     return (codeAlloc->size() + dataAlloc->size() + traceAlloc->size() > maxsz);
 }
 
 JS_FORCES_STACK JS_FRIEND_API(void)
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -47,16 +47,17 @@
 #include "jstypes.h"
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsdhash.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsvector.h"
+#include "jscompartment.h"
 #include "Writer.h"
 
 namespace js {
 
 template <typename T>
 class Queue {
     T* _data;
     unsigned _len;
@@ -1670,17 +1671,17 @@ InitJIT(TraceMonitor *tm);
 
 extern void
 FinishJIT(TraceMonitor *tm);
 
 extern void
 PurgeScriptFragments(TraceMonitor* tm, JSScript* script);
 
 extern bool
-OverfullJITCache(TraceMonitor* tm);
+OverfullJITCache(JSContext *cx, TraceMonitor* tm);
 
 extern void
 FlushJITCache(JSContext* cx);
 
 extern JSObject *
 GetBuiltinFunction(JSContext *cx, uintN index);
 
 extern void