Bug 584860 - Move TraceMonitor into compartment (r=gal)
authorBill McCloskey <wmccloskey@mozilla.com>
Wed, 22 Dec 2010 12:02:25 -0800
changeset 59915 2d7468b6054f2794e4c4953b44d8eebbd157ea8f
parent 59914 89ec0c0b48e2b24e1822ee16261c8d2c80114f1e
child 59916 8a9c6231fd52016278567f257be5c1d0874e51a3
push id17820
push usercleary@mozilla.com
push dateTue, 04 Jan 2011 21:40:57 +0000
treeherdermozilla-central@969691cfe40e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgal
bugs584860
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 584860 - Move TraceMonitor into compartment (r=gal)
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
@@ -2701,17 +2701,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
@@ -1721,24 +1556,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
@@ -3258,60 +3085,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), active(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()
@@ -406,16 +427,28 @@ JSCompartment::sweep(JSContext *cx, uint
     active = false;
 }
 
 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;
     bool                         active;  // GC flag, whether there are active frames
     js::WrapperMap               crossCompartmentWrappers;
 
 #ifdef JS_METHODJIT
     js::mjit::JaegerCompartment  *jaegerCompartment;
 #endif
@@ -109,16 +280,25 @@ struct JS_FRIEND_API(JSCompartment) {
     bool wrapException(JSContext *cx);
 
     void sweep(JSContext *cx, uint32 releaseInterval);
     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
@@ -513,43 +513,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
@@ -1700,18 +1688,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
@@ -2046,17 +2046,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
@@ -1738,26 +1738,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.
  */
@@ -2200,18 +2200,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
@@ -498,17 +498,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"
@@ -2430,17 +2431,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
@@ -2480,17 +2481,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)
@@ -5524,17 +5525,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
@@ -5649,19 +5650,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;
     }
 
@@ -7260,17 +7261,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;
@@ -7529,28 +7530,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. */
@@ -7584,34 +7588,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();
@@ -7796,17 +7796,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.
@@ -7832,17 +7832,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;
@@ -1686,17 +1687,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