зеркало из https://github.com/mozilla/gecko-dev.git
Bug 584860 - Move TraceMonitor into compartment (r=gal,a=blocker)
This commit is contained in:
Родитель
96e25b9462
Коммит
f3b6a7abb0
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
219
js/src/jscntxt.h
219
js/src/jscntxt.h
|
@ -884,147 +884,6 @@ private:
|
|||
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 */
|
||||
|
||||
/*
|
||||
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
|
|
@ -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___ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -55,11 +55,170 @@
|
|||
#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;
|
||||
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче