From 625946d567641ab137aa996c964aa0bd0736ea69 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Wed, 29 Dec 2010 17:46:14 -0800 Subject: [PATCH 1/7] Backed out changeset c35a4e6ea3ca -- done with diagnotics. --- js/src/jsobj.cpp | 2 +- js/src/jsobj.h | 10 ++-------- js/src/jsscope.h | 26 ++++++++------------------ js/src/jsscopeinlines.h | 12 ++++-------- 4 files changed, 15 insertions(+), 35 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index f8f8efaba5f..6f29bb0f178 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -106,7 +106,7 @@ using namespace js; using namespace js::gc; -JS_FRIEND_DATA(const JSObjectMap) JSObjectMap::sharedNonNative(JSObjectMap::NON_NATIVE_START_MARKER, JSObjectMap::SHAPELESS, 0); +JS_FRIEND_DATA(const JSObjectMap) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS); Class js_ObjectClass = { js_Object_str, diff --git a/js/src/jsobj.h b/js/src/jsobj.h index ec8ed2a900d..f12793f7eff 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -176,21 +176,15 @@ typedef Vector PropDescArray; } /* namespace js */ struct JSObjectMap { - uint32 startMarker; /* start marker for diagnostics */ uint32 shape; /* shape identifier */ uint32 slotSpan; /* one more than maximum live slot number */ static JS_FRIEND_DATA(const JSObjectMap) sharedNonNative; - JSObjectMap(uint32 startMarker, uint32 shape, uint32 slotSpan) - : startMarker(startMarker), shape(shape), slotSpan(slotSpan) {} + explicit JSObjectMap(uint32 shape) : shape(shape), slotSpan(0) {} + JSObjectMap(uint32 shape, uint32 slotSpan) : shape(shape), slotSpan(slotSpan) {} enum { INVALID_SHAPE = 0x8fffffff, SHAPELESS = 0xffffffff }; - enum { NON_NATIVE_START_MARKER = 0xeaeaeaea, - SHAPE_START_MARKER = 0xebebebeb, - SHAPE_MARKER_1 = 0xecececec, - SHAPE_MARKER_2 = 0xedededed, - SHAPE_END_MARKER = 0xefefefef }; bool isNative() const { return this != &sharedNonNative; } diff --git a/js/src/jsscope.h b/js/src/jsscope.h index dcfdecedabd..4ea2786bf1b 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -292,7 +292,6 @@ struct Shape : public JSObjectMap friend class js::PropertyTree; friend bool HasUnreachableGCThings(TreeFragment *f); - uint32 marker1; protected: mutable js::PropertyTable *table; @@ -331,7 +330,6 @@ struct Shape : public JSObjectMap null if shape->hasSetterValue() */ }; - uint32 marker2; public: uint32 slot; /* abstract index in object slots */ private: @@ -350,7 +348,6 @@ struct Shape : public JSObjectMap either to shape->parent if not last, else to obj->lastProp */ }; - uint32 endMarker; /* end marker for diagnostics */ static inline js::Shape **search(js::Shape **startp, jsid id, bool adding = false); static js::Shape *newDictionaryShape(JSContext *cx, const js::Shape &child, js::Shape **listp); @@ -650,24 +647,9 @@ struct EmptyShape : public js::Shape #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape) \ (*(spp) = (js::Shape *) (jsuword(shape) | SHAPE_HAD_COLLISION(*(spp)))) -inline static volatile int *vcopy(volatile int *dst, int *src, size_t bytes) -{ - int *end = src + bytes / sizeof(int); - for (; src < end; ++src, ++dst) - *dst = *src; - return dst; -} - inline js::Shape ** JSObject::nativeSearch(jsid id, bool adding) { - { - char blackbox[sizeof(JSObject) + 8]; - volatile int *p = (int *) blackbox; - *p++ = 0xacacacac; - p = vcopy(p, (int *) this, sizeof(JSObject)); - *p = 0xadadadad; - } return js::Shape::search(&lastProp, id, adding); } @@ -847,6 +829,14 @@ extern JS_FRIEND_DATA(JSScopeStats) js_scope_stats; namespace js { +inline static volatile int *vcopy(volatile int *dst, int *src, size_t bytes) +{ + int *end = src + bytes / sizeof(int); + for (; src < end; ++src, ++dst) + *dst = *src; + return dst; +} + JS_ALWAYS_INLINE js::Shape ** Shape::search(js::Shape **startp, jsid id, bool adding) { diff --git a/js/src/jsscopeinlines.h b/js/src/jsscopeinlines.h index 54d532b25a0..b36ec5ed6d7 100644 --- a/js/src/jsscopeinlines.h +++ b/js/src/jsscopeinlines.h @@ -169,11 +169,9 @@ namespace js { inline Shape::Shape(jsid id, js::PropertyOp getter, js::PropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid, uint32 shape, uint32 slotSpan) - : JSObjectMap(JSObjectMap::SHAPE_START_MARKER, shape, slotSpan), + : JSObjectMap(shape, slotSpan), table(NULL), id(id), rawGetter(getter), rawSetter(setter), slot(slot), attrs(uint8(attrs)), - flags(uint8(flags)), shortid(int16(shortid)), parent(NULL), - marker1(JSObjectMap::SHAPE_MARKER_1), marker2(JSObjectMap::SHAPE_MARKER_2), - endMarker(JSObjectMap::SHAPE_END_MARKER) + flags(uint8(flags)), shortid(int16(shortid)), parent(NULL) { JS_ASSERT_IF(slotSpan != SHAPE_INVALID_SLOT, slotSpan < JSObject::NSLOTS_LIMIT); JS_ASSERT_IF(getter && (attrs & JSPROP_GETTER), getterObj->isCallable()); @@ -183,11 +181,9 @@ Shape::Shape(jsid id, js::PropertyOp getter, js::PropertyOp setter, uint32 slot, inline Shape::Shape(JSContext *cx, Class *aclasp) - : JSObjectMap(JSObjectMap::SHAPE_START_MARKER, js_GenerateShape(cx, false), JSSLOT_FREE(aclasp)), table(NULL), + : JSObjectMap(js_GenerateShape(cx, false), JSSLOT_FREE(aclasp)), table(NULL), id(JSID_EMPTY), clasp(aclasp), rawSetter(NULL), slot(SHAPE_INVALID_SLOT), attrs(0), - flags(SHARED_EMPTY), shortid(0), parent(NULL), - marker1(JSObjectMap::SHAPE_MARKER_1), marker2(JSObjectMap::SHAPE_MARKER_2), - endMarker(JSObjectMap::SHAPE_END_MARKER) + flags(SHARED_EMPTY), shortid(0), parent(NULL) { kids.setNull(); } From 0ce50fbcc8988906becd604304306434f3e1d730 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Wed, 29 Dec 2010 17:47:42 -0800 Subject: [PATCH 2/7] Backed out changeset e5e50e5a2816 -- done with diagnostic --- js/src/jsscope.h | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 4ea2786bf1b..354cc940b6e 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -829,14 +829,6 @@ extern JS_FRIEND_DATA(JSScopeStats) js_scope_stats; namespace js { -inline static volatile int *vcopy(volatile int *dst, int *src, size_t bytes) -{ - int *end = src + bytes / sizeof(int); - for (; src < end; ++src, ++dst) - *dst = *src; - return dst; -} - JS_ALWAYS_INLINE js::Shape ** Shape::search(js::Shape **startp, jsid id, bool adding) { @@ -860,13 +852,6 @@ Shape::search(js::Shape **startp, jsid id, bool adding) METER(misses); return spp; } - { - char blackbox[sizeof(Shape) + 8]; - volatile int *p = (int *) blackbox; - *p++ = 0xfcfcfcfc; - p = vcopy(p, (int *) *startp, sizeof(Shape)); - *p = 0xfdfdfdfd; - } return (*startp)->table->search(id, adding); } From 6f47af60d726601f07540ebdb68faeebc8dfa550 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 29 Dec 2010 17:53:58 -0800 Subject: [PATCH 3/7] Bug 584860 - Move TraceMonitor into compartment (r=gal,a=blocker) --- js/src/jsapi.cpp | 2 +- js/src/jscntxt.cpp | 42 +++----- js/src/jscntxt.h | 219 +-------------------------------------- js/src/jscntxtinlines.h | 31 ++++++ js/src/jscompartment.cpp | 37 ++++++- js/src/jscompartment.h | 180 ++++++++++++++++++++++++++++++++ js/src/jsdbgapi.cpp | 14 --- js/src/jsfun.cpp | 2 +- js/src/jsgc.cpp | 13 +-- js/src/jsgc.h | 2 +- js/src/jsscript.cpp | 21 ++-- js/src/jsscript.h | 2 +- js/src/jstracer.cpp | 28 ++--- js/src/jstracer.h | 3 +- 14 files changed, 298 insertions(+), 298 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 730a3411d11..f7a2161bec7 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2662,7 +2662,7 @@ 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 diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 5d6b92c67b2..9312d4ba217 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -494,18 +494,16 @@ JSThreadData::init() #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; } @@ -522,19 +520,11 @@ JSThreadData::allocMathCache(JSContext *cx) 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; } @@ -553,18 +543,6 @@ JSThreadData::purge(JSContext *cx) /* 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; @@ -736,7 +714,6 @@ js_PurgeThreads(JSContext *cx) if (JS_CLIST_IS_EMPTY(&thread->contextList)) { JS_ASSERT(cx->thread != thread); - js_DestroyScriptsToGC(cx, &thread->data); DestroyThread(thread); e.removeFront(); @@ -921,7 +898,7 @@ DumpEvalCacheMeter(JSContext *cx) 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")) @@ -2316,6 +2293,15 @@ JSContext::updateJITEnabled() 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) { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 711ddeebda5..705401056db 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -884,147 +884,6 @@ private: JSStackFrame *curfp; }; -/* Holds the number of recording attemps for an address. */ -typedef HashMap, - SystemAllocPolicy> RecordAttemptMap; - -/* Holds the profile data for loops. */ -typedef HashMap, - SystemAllocPolicy> LoopProfileMap; - -class Oracle; - -typedef HashSet, - 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* 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 */ /* @@ -1038,23 +897,6 @@ struct TraceMonitor { # 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), \ @@ -1118,18 +960,11 @@ struct JSThreadData { 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. */ @@ -1725,14 +1560,6 @@ struct JSRuntime { /* 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) @@ -3238,50 +3065,14 @@ 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 */ /* diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 50d47e82148..fbdc5ebf4ce 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -42,6 +42,7 @@ #define jscntxtinlines_h___ #include "jscntxt.h" +#include "jscompartment.h" #include "jsparse.h" #include "jsstaticcheck.h" #include "jsxml.h" @@ -753,6 +754,36 @@ CallSetter(JSContext *cx, JSObject *obj, jsid id, PropertyOp op, uintN attrs, ui 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___ */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index d75088c2ba5..c35d7b08ddb 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -44,6 +44,7 @@ #include "jsiter.h" #include "jsproxy.h" #include "jsscope.h" +#include "jstracer.h" #include "methodjit/MethodJIT.h" #include "methodjit/PolyIC.h" #include "methodjit/MonoIC.h" @@ -58,13 +59,23 @@ JSCompartment::JSCompartment(JSRuntime *rt) 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 @@ -81,9 +92,19 @@ JSCompartment::init() if (!crossCompartmentWrappers.init()) return false; -#ifdef JS_METHODJIT - if (!(jaegerCompartment = new mjit::JaegerCompartment)) +#ifdef JS_TRACER + if (!InitJIT(&traceMonitor)) { return false; + } +#endif + +#ifdef JS_METHODJIT + if (!(jaegerCompartment = new mjit::JaegerCompartment)) { +#ifdef JS_TRACER + FinishJIT(&traceMonitor); +#endif + return false; + } return jaegerCompartment->Initialize(); #else return true; @@ -357,6 +378,18 @@ 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; diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 2cd3bea9e9e..be3624c3fc3 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -55,11 +55,170 @@ #endif namespace js { + +/* Holds the number of recording attemps for an address. */ +typedef HashMap, + SystemAllocPolicy> RecordAttemptMap; + +/* Holds the profile data for loops. */ +typedef HashMap, + SystemAllocPolicy> LoopProfileMap; + +class Oracle; + +typedef HashSet, + 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* 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; @@ -72,6 +231,18 @@ struct JS_FRIEND_API(JSCompartment) { 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; @@ -113,6 +284,15 @@ struct JS_FRIEND_API(JSCompartment) { 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 diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index dc5e61b95bd..c13e9ca6157 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -485,17 +485,6 @@ JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited) 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) @@ -511,7 +500,6 @@ JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure) #ifdef JS_TRACER JITInhibitingHookChange(rt, wasInhibited); } - LeaveTraceRT(rt); #endif return JS_TRUE; } @@ -1672,8 +1660,6 @@ JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) #ifdef JS_TRACER JITInhibitingHookChange(rt, wasInhibited); } - if (hook) - LeaveTraceRT(rt); #endif return JS_TRUE; } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index d1d52c5f12e..b4d36879a0c 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2060,7 +2060,7 @@ fun_finalize(JSContext *cx, JSObject *obj) * 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 diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 1e062d44053..9e5136d2fc6 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1732,16 +1732,16 @@ TriggerGC(JSRuntime *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); } } } @@ -2178,8 +2178,9 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM) #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 /* diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 99838cd66fd..59c9eb60437 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -827,7 +827,7 @@ js_WaitForGC(JSRuntime *rt); #endif extern void -js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data); +js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp); namespace js { diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index ff436525208..62d37c3f5d3 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -422,7 +422,7 @@ 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 @@ -1218,7 +1218,7 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script) } static void -DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data) +DestroyScript(JSContext *cx, JSScript *script, JSCompartment *comp) { #ifdef DEBUG if (script->isEmpty()) @@ -1277,16 +1277,7 @@ DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data) } #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) @@ -1301,14 +1292,14 @@ 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 diff --git a/js/src/jsscript.h b/js/src/jsscript.h index cd9ee325496..87d0ed26b5c 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -502,7 +502,7 @@ js_DestroyScript(JSContext *cx, JSScript *script); * 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); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index ea1e264c6bf..a6a35c0f190 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -60,6 +60,7 @@ #include "jsarray.h" #include "jsbool.h" #include "jscntxt.h" +#include "jscompartment.h" #include "jsdate.h" #include "jsdbgapi.h" #include "jsemit.h" @@ -2430,7 +2431,7 @@ TraceRecorder::finishSuccessfully() 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; } @@ -2480,7 +2481,7 @@ TraceRecorder::finishAbort(const char* reason) localtm->recorder = NULL; delete this; - if (localtm->outOfMemory() || OverfullJITCache(localtm)) { + if (localtm->outOfMemory() || OverfullJITCache(localcx, localtm)) { ResetJIT(localcx, FR_OOM); return JIT_RESET; } @@ -5522,7 +5523,7 @@ TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f, 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; } @@ -5647,9 +5648,9 @@ RecordTree(JSContext* cx, TreeFragment* first, JSScript* outerScript, jsbytecode 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); @@ -7257,7 +7258,7 @@ TraceRecorder::monitorRecording(JSOp op) return status == ARECORD_ERROR ? ARECORD_ERROR : ARECORD_ABORTED; } - if (outOfMemory() || OverfullJITCache(&localtm)) { + if (outOfMemory() || OverfullJITCache(cx, &localtm)) { ResetJIT(cx, FR_OOM); /* @@ -7526,18 +7527,21 @@ disable_debugger_exceptions() { } 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. */ @@ -7581,9 +7585,6 @@ InitJIT(TraceMonitor *tm) 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; @@ -7598,7 +7599,6 @@ InitJIT(TraceMonitor *tm) tm->flushEpoch = 0; - JS_ASSERT(!tm->dataAlloc && !tm->traceAlloc && !tm->codeAlloc); tm->dataAlloc = new VMAllocator(); tm->traceAlloc = new VMAllocator(); tm->tempAlloc = new VMAllocator(); @@ -7793,7 +7793,7 @@ PurgeScriptFragments(TraceMonitor* tm, JSScript* script) } bool -OverfullJITCache(TraceMonitor* tm) +OverfullJITCache(JSContext *cx, TraceMonitor* tm) { /* * You might imagine the outOfMemory flag on the allocator is sufficient @@ -7829,7 +7829,7 @@ OverfullJITCache(TraceMonitor* tm) * 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; diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 0bac7c3ea4b..73b4cd9ea6b 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -52,6 +52,7 @@ #include "jslock.h" #include "jsnum.h" #include "jsvector.h" +#include "jscompartment.h" #include "Writer.h" namespace js { @@ -1675,7 +1676,7 @@ extern void PurgeScriptFragments(TraceMonitor* tm, JSScript* script); extern bool -OverfullJITCache(TraceMonitor* tm); +OverfullJITCache(JSContext *cx, TraceMonitor* tm); extern void FlushJITCache(JSContext* cx); From 38b90b5400eff6a6804ef96f10f294ef6c1dd37d Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 29 Dec 2010 17:55:24 -0800 Subject: [PATCH 4/7] Bug 584860 - TraceMonitor cleanups (r=igor,a=blocker) --- js/src/jscntxt.h | 2 +- js/src/jscompartment.cpp | 4 ++++ js/src/jscompartment.h | 8 ++++---- js/src/jsfun.cpp | 2 +- js/src/jsgc.cpp | 8 +------- js/src/jsscript.cpp | 12 ++++++------ js/src/jsscript.h | 2 +- 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 705401056db..6c2874c6c63 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -964,7 +964,7 @@ struct JSThreadData { unsigned iterationCounter; /* Maximum size of the tracer's code cache before we start flushing. */ - uint32 maxCodeCacheBytes; + uint32 maxCodeCacheBytes; #endif /* State used by dtoa.c. */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index c35d7b08ddb..461008abb28 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -364,6 +364,10 @@ JSCompartment::sweep(JSContext *cx) } } +#ifdef JS_TRACER + traceMonitor.sweep(); +#endif + #if defined JS_METHODJIT && defined JS_MONOIC for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) { JSScript *script = reinterpret_cast(cursor); diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index be3624c3fc3..24f9e5c3c46 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -75,13 +75,13 @@ typedef HashSet 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. + * Trace monitor. Every JSCompartment 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 + * The context currently executing JIT-compiled code in this compartment, or * NULL if none. Among other things, this can in certain cases prevent * last-ditch GC and suppress calls to JS_ReportOutOfMemory. * diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index b4d36879a0c..3a4507deaed 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2060,7 +2060,7 @@ fun_finalize(JSContext *cx, JSObject *obj) * very early. */ if (FUN_INTERPRETED(fun) && fun->u.i.script) - js_DestroyScriptFromGC(cx, fun->u.i.script, obj->compartment()); + js_DestroyScriptFromGC(cx, fun->u.i.script); } int diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 9e5136d2fc6..600d60651fe 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1741,7 +1741,7 @@ js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp) while ((script = *listp) != NULL) { *listp = script->u.nextToGC; script->u.nextToGC = NULL; - js_DestroyScriptFromGC(cx, script, comp); + js_DestroyScriptFromGC(cx, script); } } } @@ -2177,12 +2177,6 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM) rt->liveObjectPropsPreSweep = rt->liveObjectProps; #endif -#ifdef JS_TRACER - 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 diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 62d37c3f5d3..6327c4c7c84 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -422,7 +422,7 @@ script_finalize(JSContext *cx, JSObject *obj) { JSScript *script = (JSScript *) obj->getPrivate(); if (script) - js_DestroyScriptFromGC(cx, script, obj->compartment()); + js_DestroyScriptFromGC(cx, script); } static void @@ -1218,7 +1218,7 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script) } static void -DestroyScript(JSContext *cx, JSScript *script, JSCompartment *comp) +DestroyScript(JSContext *cx, JSScript *script) { #ifdef DEBUG if (script->isEmpty()) @@ -1277,7 +1277,7 @@ DestroyScript(JSContext *cx, JSScript *script, JSCompartment *comp) } #ifdef JS_TRACER - PurgeScriptFragments(&comp->traceMonitor, script); + PurgeScriptFragments(&script->compartment->traceMonitor, script); #endif #if defined(JS_METHODJIT) @@ -1292,14 +1292,14 @@ void js_DestroyScript(JSContext *cx, JSScript *script) { JS_ASSERT(!cx->runtime->gcRunning); - DestroyScript(cx, script, cx->compartment); + DestroyScript(cx, script); } void -js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSCompartment *comp) +js_DestroyScriptFromGC(JSContext *cx, JSScript *script) { JS_ASSERT(cx->runtime->gcRunning); - DestroyScript(cx, script, comp); + DestroyScript(cx, script); } void diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 87d0ed26b5c..2b5e427e1b9 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -502,7 +502,7 @@ js_DestroyScript(JSContext *cx, JSScript *script); * from that thread. */ extern void -js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSCompartment *comp); +js_DestroyScriptFromGC(JSContext *cx, JSScript *script); extern void js_TraceScript(JSTracer *trc, JSScript *script); From d63ea55abad5e807b1fd5f9ed4405679ec238163 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 29 Dec 2010 17:59:02 -0800 Subject: [PATCH 5/7] Bug 621032 - Move MathCache from JSThreadData to JSCompartment (r=igor,a=blocker) --- js/src/jscntxt.cpp | 11 ----------- js/src/jscntxt.h | 10 ---------- js/src/jscompartment.cpp | 24 +++++++++++++++++++++--- js/src/jscompartment.h | 18 ++++++++++++++++++ js/src/jsmath.cpp | 19 ++++++++++--------- js/src/jstracer.cpp | 2 +- 6 files changed, 50 insertions(+), 34 deletions(-) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 9312d4ba217..759debaf2f6 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -507,16 +507,6 @@ JSThreadData::init() return true; } -MathCache * -JSThreadData::allocMathCache(JSContext *cx) -{ - JS_ASSERT(!mathCache); - mathCache = new MathCache; - if (!mathCache) - js_ReportOutOfMemory(cx); - return mathCache; -} - void JSThreadData::finish() { @@ -526,7 +516,6 @@ JSThreadData::finish() js_FinishGSNCache(&gsnCache); propertyCache.~PropertyCache(); stackSpace.finish(); - delete mathCache; } void diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 6c2874c6c63..d3331aea560 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -998,16 +998,6 @@ struct JSThreadData { js::ConservativeGCThreadData conservativeGC; - private: - js::MathCache *mathCache; - - js::MathCache *allocMathCache(JSContext *cx); - public: - - js::MathCache *getMathCache(JSContext *cx) { - return mathCache ? mathCache : allocMathCache(cx); - } - bool init(); void finish(); void mark(JSTracer *trc); diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 461008abb28..13d195c1a41 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -55,12 +55,18 @@ 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) + : rt(rt), + principals(NULL), + data(NULL), + marked(false), + debugMode(rt->debugMode), + anynameObject(NULL), + functionNamespaceObject(NULL), + mathCache(NULL) { JS_INIT_CLIST(&scripts); - memset(scriptsToGC, 0, sizeof(scriptsToGC)); + PodArrayZero(scriptsToGC); } JSCompartment::~JSCompartment() @@ -72,6 +78,8 @@ JSCompartment::~JSCompartment() delete jaegerCompartment; #endif + delete mathCache; + #ifdef DEBUG for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i) JS_ASSERT(!scriptsToGC[i]); @@ -414,3 +422,13 @@ JSCompartment::purge(JSContext *cx) } #endif } + +MathCache * +JSCompartment::allocMathCache(JSContext *cx) +{ + JS_ASSERT(!mathCache); + mathCache = new MathCache; + if (!mathCache) + js_ReportOutOfMemory(cx); + return mathCache; +} diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 24f9e5c3c46..7096b80ea4a 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -282,11 +282,29 @@ struct JS_FRIEND_API(JSCompartment) { void purge(JSContext *cx); void finishArenaLists(); bool arenaListsAreEmpty(); + + private: + js::MathCache *mathCache; + + js::MathCache *allocMathCache(JSContext *cx); + + public: + js::MathCache *getMathCache(JSContext *cx) { + return mathCache ? mathCache : allocMathCache(cx); + } }; #define JS_TRACE_MONITOR(cx) (cx->compartment->traceMonitor) #define JS_SCRIPTS_TO_GC(cx) (cx->compartment->scriptsToGC) +namespace js { +static inline MathCache * +GetMathCache(JSContext *cx) +{ + return cx->compartment->getMathCache(cx); +} +} + #ifdef DEBUG # define EVAL_CACHE_METER(x) (cx->compartment->evalCacheMeter.x++) #else diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 9feba7c72cf..c0024ece19e 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -54,6 +54,7 @@ #include "jsmath.h" #include "jsnum.h" #include "jslibmath.h" +#include "jscompartment.h" using namespace js; @@ -148,7 +149,7 @@ math_acos(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } #endif - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(acos, x); @@ -173,7 +174,7 @@ math_asin(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } #endif - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(asin, x); @@ -192,7 +193,7 @@ math_atan(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(atan, x); @@ -285,7 +286,7 @@ math_cos(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(cos, x); @@ -318,7 +319,7 @@ math_exp(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(math_exp_body, x); @@ -365,7 +366,7 @@ math_log(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } #endif - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(log, x); @@ -612,7 +613,7 @@ math_sin(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(sin, x); @@ -631,7 +632,7 @@ math_sqrt(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(sqrt, x); @@ -650,7 +651,7 @@ math_tan(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return JS_FALSE; z = mathCache->lookup(tan, x); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index a6a35c0f190..83f8c19871f 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -11050,7 +11050,7 @@ TraceRecorder::callSpecializedNative(JSNativeTraceInfo *trcinfo, uintN argc, goto next_specialization; *argp = this_ins; } else if (argtype == 'M') { - MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + MathCache *mathCache = GetMathCache(cx); if (!mathCache) return RECORD_ERROR; *argp = w.nameImmpNonGC(mathCache); From 0dd3c4d1bd99b81c57aeb4ce2ebd451b08756cf0 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Thu, 23 Dec 2010 15:10:36 -0800 Subject: [PATCH 6/7] Bug 621032 - Move iterationCounter from JSThreadData to TraceMonitor (r=igor,a=blocker) --- js/src/jscntxt.h | 3 --- js/src/jscompartment.h | 3 +++ js/src/jstracer.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index d3331aea560..1c6fa6fc155 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -960,9 +960,6 @@ struct JSThreadData { js::PropertyCache propertyCache; #ifdef JS_TRACER - /* Counts the number of iterations run by a trace. */ - unsigned iterationCounter; - /* Maximum size of the tracer's code cache before we start flushing. */ uint32 maxCodeCacheBytes; #endif diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 7096b80ea4a..9ae38830b88 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -92,6 +92,9 @@ struct TraceMonitor { */ JSContext *tracecx; + /* Counts the number of iterations run by the currently executing trace. */ + unsigned iterationCounter; + /* * 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 diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 83f8c19871f..f7f4c9ea557 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2358,7 +2358,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag #ifdef JS_METHODJIT if (cx->methodJitEnabled) { w.comment("begin-count-loop-iterations"); - LIns* counterPtr = w.nameImmpNonGC((void *) &JS_THREAD_DATA(cx)->iterationCounter); + LIns* counterPtr = w.nameImmpNonGC((void *) &traceMonitor->iterationCounter); LIns* counterValue = w.ldiVolatile(counterPtr); LIns* test = w.ltiN(counterValue, LOOP_COUNT_MAX); LIns *branch = w.jfUnoptimizable(test); @@ -6517,7 +6517,7 @@ ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount, debug_only_stmt(*(uint64*)&tm->storage->global()[globalSlots] = 0xdeadbeefdeadbeefLL;) /* Execute trace. */ - JS_THREAD_DATA(cx)->iterationCounter = 0; + tm->iterationCounter = 0; debug_only(int64 t0 = PRMJ_Now();) #ifdef MOZ_TRACEVIS VMSideExit* lr = (TraceVisStateObj(cx, S_NATIVE), ExecuteTrace(cx, f, state)); @@ -6536,7 +6536,7 @@ ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount, bool ok = !(state.builtinStatus & BUILTIN_ERROR); JS_ASSERT_IF(cx->throwing, !ok); - size_t iters = JS_THREAD_DATA(cx)->iterationCounter; + size_t iters = tm->iterationCounter; f->execs++; f->iters += iters; From 176cdc3928b02cbdf565af15279807ac4bb2aea7 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Thu, 23 Dec 2010 15:14:33 -0800 Subject: [PATCH 7/7] Bug 621072 - Don't bake JS_THREAD_DATA(cx)->interruptFlags into traces (r=igor,a=blocker) --- js/src/jstracer.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index f7f4c9ea557..ad8d6623c78 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2345,7 +2345,13 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag * thread and cannot outlive the corresponding JSThreadData. */ w.comment("begin-interruptFlags-check"); - LIns* flagptr = w.nameImmpNonGC((void *) &JS_THREAD_DATA(cx)->interruptFlags); + /* FIXME: See bug 621140 for moving interruptCounter to the compartment. */ +#ifdef JS_THREADSAFE + void *interrupt = (void*) &cx->runtime->interruptCounter; +#else + void *interrupt = (void*) &JS_THREAD_DATA(cx)->interruptFlags; +#endif + LIns* flagptr = w.nameImmpNonGC(interrupt); LIns* x = w.ldiVolatile(flagptr); guard(true, w.eqi0(x), TIMEOUT_EXIT); w.comment("end-interruptFlags-check");