mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-28 12:45:27 +00:00
First stage of loop table work; bitmap free space management and GC hook-up still to come.
This commit is contained in:
parent
e49b7995d0
commit
2a2bfa5432
@ -778,8 +778,6 @@ JS_NewRuntime(uint32 maxbytes)
|
||||
#endif
|
||||
if (!js_InitPropertyTree(rt))
|
||||
goto bad;
|
||||
if (!js_InitTracer(rt))
|
||||
goto bad;
|
||||
return rt;
|
||||
|
||||
bad:
|
||||
|
@ -87,18 +87,18 @@ unsigned char _BitScanReverse(unsigned long * Index, unsigned long Mask);
|
||||
__forceinline static int
|
||||
__BitScanForward32(unsigned int val)
|
||||
{
|
||||
unsigned long idx;
|
||||
unsigned long idx;
|
||||
|
||||
_BitScanForward(&idx, (unsigned long)val);
|
||||
return (int)idx;
|
||||
_BitScanForward(&idx, (unsigned long)val);
|
||||
return (int)idx;
|
||||
}
|
||||
__forceinline static int
|
||||
__BitScanReverse32(unsigned int val)
|
||||
{
|
||||
unsigned long idx;
|
||||
unsigned long idx;
|
||||
|
||||
_BitScanReverse(&idx, (unsigned long)val);
|
||||
return (int)(31-idx);
|
||||
_BitScanReverse(&idx, (unsigned long)val);
|
||||
return (int)(31-idx);
|
||||
}
|
||||
# define js_bitscan_ctz32(val) __BitScanForward32(val)
|
||||
# define js_bitscan_clz32(val) __BitScanReverse32(val)
|
||||
@ -209,7 +209,7 @@ __BitScanReverse32(unsigned int val)
|
||||
# ifdef JS_HAS_BUILTIN_BITSCAN32
|
||||
JS_STATIC_ASSERT(sizeof(unsigned) == sizeof(JSUword));
|
||||
# define js_FloorLog2wImpl(n) \
|
||||
((JSUword)(JS_BITS_PER_WORD - 1 - js_bitscan_clz32(n)))
|
||||
((JSUword)(JS_BITS_PER_WORD - 1 - js_bitscan_clz32(n)))
|
||||
# else
|
||||
# define js_FloorLog2wImpl(n) ((JSUword)JS_FloorLog2(n))
|
||||
#endif
|
||||
@ -219,7 +219,7 @@ JS_STATIC_ASSERT(sizeof(unsigned) == sizeof(JSUword));
|
||||
# ifdef JS_HAS_BUILTIN_BITSCAN64
|
||||
JS_STATIC_ASSERT(sizeof(unsigned long long) == sizeof(JSUword));
|
||||
# define js_FloorLog2wImpl(n) \
|
||||
((JSUword)(JS_BITS_PER_WORD - 1 - js_bitscan_clz64(n)))
|
||||
((JSUword)(JS_BITS_PER_WORD - 1 - js_bitscan_clz64(n)))
|
||||
# else
|
||||
extern JSUword js_FloorLog2wImpl(JSUword n);
|
||||
# endif
|
||||
|
@ -400,7 +400,7 @@ struct JSRuntime {
|
||||
JSPropertyCache propertyCache;
|
||||
|
||||
JSTraceMonitor traceMonitor;
|
||||
|
||||
|
||||
#define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache)
|
||||
#define JS_PROPERTY_CACHE(cx) ((cx)->runtime->propertyCache)
|
||||
#define JS_TRACE_MONITOR(cx) ((cx)->runtime->traceMonitor)
|
||||
@ -409,10 +409,16 @@ struct JSRuntime {
|
||||
/*
|
||||
* Loops are globally numbered (per runtime) using this counter. The actual
|
||||
* loop table that tracks loop statistics is per-thread in a multi-threaded
|
||||
* environment.
|
||||
* environment. Although loopTableCursor is typed jsword because it is used
|
||||
* with js_CompareAndSwap, its value is uint32 and <= LOOP_TABLE_LIMIT.
|
||||
*/
|
||||
uint32 loopTableSlotGen;
|
||||
|
||||
#define LOOP_TABLE_LIMIT 2048
|
||||
#define LOOP_TABLE_BITMAP_WORDS JS_HOWMANY(LOOP_TABLE_LIMIT, JS_BITS_PER_WORD)
|
||||
#define LOOP_TABLE_NO_SLOT ((uint32) -1)
|
||||
|
||||
jsword loopTableCursor;
|
||||
jsbitmap loopTableBitmap[LOOP_TABLE_BITMAP_WORDS];
|
||||
|
||||
/*
|
||||
* Object shape (property cache structural type) identifier generator.
|
||||
*
|
||||
|
@ -287,8 +287,8 @@ typedef enum JSGCInvocationKind {
|
||||
GC_SET_SLOT_REQUEST = GC_LOCK_HELD | 1,
|
||||
|
||||
/*
|
||||
* Called from js_NewGCThing as a last-ditch GC attempt. See comments
|
||||
* in jsgc.c just before js_GC's definition for details.
|
||||
* Called from js_NewGCThing as a last-ditch GC attempt. See comments in
|
||||
* jsgc.c just before js_GC's definition for details.
|
||||
*/
|
||||
GC_LAST_DITCH = GC_LOCK_HELD | 2
|
||||
} JSGCInvocationKind;
|
||||
|
@ -2900,14 +2900,12 @@ JS_INTERPRET(JSContext *cx)
|
||||
if (JS_LIKELY(!fp->spbase)) {
|
||||
ASSERT_NOT_THROWING(cx);
|
||||
JS_ASSERT(!fp->regs);
|
||||
vp = js_AllocRawStack(cx, script->loopHeaders + script->depth, &mark);
|
||||
if (!vp) {
|
||||
fp->spbase = js_AllocRawStack(cx, script->depth, &mark);
|
||||
if (!fp->spbase) {
|
||||
ok = JS_FALSE;
|
||||
goto exit2;
|
||||
}
|
||||
JS_ASSERT(mark);
|
||||
memset(vp, 0, script->loopHeaders * sizeof(jsval));
|
||||
fp->spbase = vp + script->loopHeaders;
|
||||
regs.pc = script->code;
|
||||
regs.sp = fp->spbase;
|
||||
fp->regs = ®s;
|
||||
@ -3023,47 +3021,47 @@ JS_INTERPRET(JSContext *cx)
|
||||
END_EMPTY_CASES
|
||||
|
||||
BEGIN_CASE(JSOP_HEADER)
|
||||
{
|
||||
slot = script->loopBase + GET_UINT8(regs.pc);
|
||||
JS_ASSERT(slot < rt->loopTableSlotGen);
|
||||
if (script->loopBase != LOOP_TABLE_NO_SLOT) {
|
||||
slot = GET_UINT8(regs.pc);
|
||||
JS_ASSERT(slot < script->loopHeaders);
|
||||
slot += script->loopBase;
|
||||
JS_ASSERT(slot < (uint32) rt->loopTableCursor);
|
||||
|
||||
JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
|
||||
if (slot >= tm->loopTableSize && !js_GrowLoopTable(cx, slot))
|
||||
goto error;
|
||||
JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
|
||||
if (slot >= tm->loopTableSize && !js_GrowLoopTable(cx, slot))
|
||||
goto error;
|
||||
|
||||
vp = &JS_TRACE_MONITOR(cx).loopTable[slot];
|
||||
rval = *vp;
|
||||
if (JSVAL_IS_INT(rval)) {
|
||||
/*
|
||||
* There are no concurrent writes to slots. This point in the
|
||||
* program is the only place a slot is updated from.
|
||||
*/
|
||||
if (JSVAL_TO_INT(rval) >= TRACE_THRESHOLD) {
|
||||
vp = &JS_TRACE_MONITOR(cx).loopTable[slot];
|
||||
rval = *vp;
|
||||
if (JSVAL_IS_INT(rval)) {
|
||||
/*
|
||||
* JS_THREADSAFE todo:
|
||||
* Once a thread hits the threshold, it should first read
|
||||
* the other threads' loop tables to see if anyone already
|
||||
* compiled a tree for us, and in that case reuse that
|
||||
* tree instead of recording a new one.
|
||||
* There are no concurrent writes to slots. This is the
|
||||
* only place from which a loop table slot is updated.
|
||||
*/
|
||||
obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
|
||||
if (!obj)
|
||||
goto error;
|
||||
*vp = OBJECT_TO_JSVAL(obj);
|
||||
if (JSVAL_TO_INT(rval) >= TRACE_THRESHOLD) {
|
||||
/*
|
||||
* JS_THREADSAFE todo:
|
||||
* Once a thread hits the threshold, it should first
|
||||
* read other threads' loop tables to see if anyone
|
||||
* already compiled a tree for us, and in that case
|
||||
* reuse that tree instead of recording a new one.
|
||||
*/
|
||||
obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
|
||||
if (!obj)
|
||||
goto error;
|
||||
*vp = OBJECT_TO_JSVAL(obj);
|
||||
} else {
|
||||
/*
|
||||
* Adding 2 is equivalent to setting *vp to the value
|
||||
* INT_TO_JSVAL(JSVAL_TO_INT(*vp) + 1).
|
||||
*/
|
||||
*vp = rval + 2;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* We use FAST_INC_DEC to increment the jsval counter,
|
||||
* which currently contains a jsint. *vp += 2 is
|
||||
* equivalent to INT_TO_JSVAL(JSVAL_TO_INT(*vp) + 1).
|
||||
* JS_ATOMIC_ADD(v, 2) is the atomic version of *vp += 2.
|
||||
*/
|
||||
JS_ATOMIC_ADD(vp, 2);
|
||||
JS_ASSERT(JSVAL_IS_GCTHING(rval));
|
||||
/* Execute the tree. */
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(JSVAL_IS_GCTHING(rval));
|
||||
/* Execute the tree. */
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_HEADER)
|
||||
|
||||
/* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
|
||||
@ -4899,8 +4897,7 @@ JS_INTERPRET(JSContext *cx)
|
||||
nvars = fun->u.i.nvars;
|
||||
script = fun->u.i.script;
|
||||
atoms = script->atomMap.vector;
|
||||
nbytes = (nframeslots + nvars + script->loopHeaders +
|
||||
script->depth) * sizeof(jsval);
|
||||
nbytes = (nframeslots + nvars + script->depth) * sizeof(jsval);
|
||||
|
||||
/* Allocate missing expected args adjacent to actuals. */
|
||||
a = cx->stackPool.current;
|
||||
@ -4988,10 +4985,6 @@ JS_INTERPRET(JSContext *cx)
|
||||
while (nvars--)
|
||||
*newsp++ = JSVAL_VOID;
|
||||
|
||||
/* Clear and reserve space for loop counters. */
|
||||
memset(newsp, 0, script->loopHeaders * sizeof(jsval));
|
||||
newsp += script->loopHeaders;
|
||||
|
||||
newifp->frame.regs = NULL;
|
||||
newifp->frame.spbase = NULL;
|
||||
|
||||
@ -7146,22 +7139,6 @@ JS_INTERPRET(JSContext *cx)
|
||||
JS_ASSERT(fp->regs == ®s);
|
||||
|
||||
if (JS_LIKELY(mark != NULL)) {
|
||||
#ifdef JS_DUMP_LOOP_STATS
|
||||
if (script->loopHeaders) {
|
||||
jsval *lp = fp->spbase;
|
||||
jsval *end = lp - script->loopHeaders;
|
||||
|
||||
do {
|
||||
JS_ASSERT(end < lp && lp <= fp->spbase);
|
||||
lval = *--lp;
|
||||
if (JSVAL_IS_NULL(lval))
|
||||
continue;
|
||||
JS_ASSERT(JSVAL_IS_INT(lval));
|
||||
JS_BASIC_STATS_ACCUM(&rt->loopStats, JSVAL_TO_INT(lval));
|
||||
} while (lp > end);
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_ASSERT(!fp->blockChain);
|
||||
JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
|
||||
JS_ASSERT(!(fp->flags & JSFRAME_GENERATOR));
|
||||
|
@ -720,7 +720,7 @@ JSObject *
|
||||
js_NewGenerator(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
JSObject *obj;
|
||||
uintN argc, nargs, nvars, loops, nslots;
|
||||
uintN argc, nargs, nvars, nslots;
|
||||
JSGenerator *gen;
|
||||
jsval *newsp;
|
||||
|
||||
@ -733,8 +733,7 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
|
||||
argc = fp->argc;
|
||||
nargs = JS_MAX(argc, fp->fun->nargs);
|
||||
nvars = fp->nvars;
|
||||
loops = fp->script->loopHeaders;
|
||||
nslots = 2 + nargs + nvars + loops + fp->script->depth;
|
||||
nslots = 2 + nargs + nvars + fp->script->depth;
|
||||
|
||||
/* Allocate obj's private data struct. */
|
||||
gen = (JSGenerator *)
|
||||
@ -788,12 +787,6 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
|
||||
|
||||
#undef COPY_STACK_ARRAY
|
||||
|
||||
/* Initialize loop counters, if there are any loops. */
|
||||
if (loops) {
|
||||
memset(newsp, 0, loops * sizeof(jsval));
|
||||
newsp += loops;
|
||||
}
|
||||
|
||||
/* Initialize or copy virtual machine state. */
|
||||
gen->frame.down = NULL;
|
||||
gen->frame.annotation = NULL;
|
||||
|
@ -522,4 +522,4 @@ OPDEF(JSOP_INT32, 228, "int32", NULL, 5, 0, 1, 16, JOF_INT32)
|
||||
*/
|
||||
OPDEF(JSOP_LENGTH, 229, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP)
|
||||
|
||||
OPDEF(JSOP_HEADER, 230, "header", NULL, 4, 0, 0, 0, JOF_UINT24)
|
||||
OPDEF(JSOP_HEADER, 230, "header", NULL, 2, 0, 0, 0, JOF_UINT8)
|
||||
|
@ -539,14 +539,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
if (!ok)
|
||||
goto error;
|
||||
|
||||
if (script->loopHeaders) {
|
||||
/* Allocate a loop table slot for all JSOP_HEADER opcodes. */
|
||||
ok = js_AllocateLoopTableSlots(cx, script->loopHeaders,
|
||||
&script->loopBase);
|
||||
if (!ok)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) ||
|
||||
!JS_XDRCStringOrNull(xdr, (char **)&script->filename) ||
|
||||
!JS_XDRUint32(xdr, &lineno) ||
|
||||
@ -1391,6 +1383,7 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes,
|
||||
memset(cursor, 0, vectorSize);
|
||||
cursor += vectorSize;
|
||||
}
|
||||
|
||||
if (nobjects != 0) {
|
||||
JS_SCRIPT_OBJECTS(script)->length = nobjects;
|
||||
JS_SCRIPT_OBJECTS(script)->vector = (JSObject **)cursor;
|
||||
@ -1414,7 +1407,15 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes,
|
||||
#endif
|
||||
cursor += vectorSize;
|
||||
}
|
||||
script->loopHeaders = (uint8) JS_MIN(nloops, 255);
|
||||
|
||||
if (nloops) {
|
||||
/*
|
||||
* Allocate loop table slots for all JSOP_HEADER opcodes eagerly to
|
||||
* avoid penalizing run-time (js_Interpret, under JSOP_HEADER).
|
||||
*/
|
||||
script->loopHeaders = (uint8) JS_MIN(nloops, 255);
|
||||
script->loopBase = js_AllocateLoopTableSlots(cx, script->loopHeaders);
|
||||
}
|
||||
|
||||
script->code = script->main = (jsbytecode *)cursor;
|
||||
JS_ASSERT(cursor +
|
||||
@ -1567,17 +1568,22 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
||||
JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner);
|
||||
#endif
|
||||
|
||||
if (!cx->runtime->gcRunning &&
|
||||
!(cx->fp && (cx->fp->flags & JSFRAME_EVAL))) {
|
||||
if (!cx->runtime->gcRunning) {
|
||||
if (!(cx->fp && (cx->fp->flags & JSFRAME_EVAL))) {
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
JS_ASSERT(script->owner == cx->thread);
|
||||
JS_ASSERT(script->owner == cx->thread);
|
||||
#endif
|
||||
js_FlushPropertyCacheForScript(cx, script);
|
||||
}
|
||||
js_FlushPropertyCacheForScript(cx, script);
|
||||
}
|
||||
|
||||
if (script->loopHeaders) {
|
||||
/* Free the loop table slots for all JSOP_HEADER opcodes. */
|
||||
js_FreeLoopTableSlots(cx, script->loopBase, script->loopHeaders);
|
||||
/*
|
||||
* Free the loop table slots for all JSOP_HEADER opcodes. We do this
|
||||
* only if not called from the GC via script_finalize, because the GC
|
||||
* will re-base all based, traceable scripts having loop table space,
|
||||
* to compress live slots toward the start of the table.
|
||||
*/
|
||||
if (script->loopHeaders)
|
||||
js_FreeLoopTableSlots(cx, script->loopBase, script->loopHeaders);
|
||||
}
|
||||
|
||||
JS_free(cx, script);
|
||||
|
@ -40,40 +40,57 @@
|
||||
|
||||
#include "jsinterp.cpp"
|
||||
|
||||
bool
|
||||
js_InitTracer(JSRuntime* rt)
|
||||
uint32
|
||||
js_AllocateLoopTableSlots(JSContext* cx, uint32 nloops)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
jsword* cursorp = &cx->runtime->loopTableCursor;
|
||||
jsword cursor, fencepost;
|
||||
|
||||
bool
|
||||
js_AllocateLoopTableSlots(JSContext* cx, uint32 nloops, uint32 *basep)
|
||||
{
|
||||
return false;
|
||||
do {
|
||||
cursor = *cursorp;
|
||||
fencepost = cursor + nloops;
|
||||
if (fencepost > LOOP_TABLE_LIMIT) {
|
||||
// try slow path
|
||||
return LOOP_TABLE_NO_SLOT;
|
||||
}
|
||||
} while (!js_CompareAndSwap(cursorp, cursor, fencepost));
|
||||
return (uint32) cursor;
|
||||
}
|
||||
|
||||
void
|
||||
js_FreeLoopTableSlots(JSContext* cx, uint32 base, uint32 nloops)
|
||||
{
|
||||
jsword* cursorp = &cx->runtime->loopTableCursor;
|
||||
jsword cursor;
|
||||
|
||||
cursor = *cursorp;
|
||||
JS_ASSERT(cursor >= (jsword) (base + nloops));
|
||||
|
||||
if ((uint32) cursor == base + nloops &&
|
||||
js_CompareAndSwap(cursorp, cursor, base)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// slow path
|
||||
}
|
||||
|
||||
/*
|
||||
* To grow the loop table that we take the traceMonitor lock, double check
|
||||
* that no other thread grew the table while we were deciding to grow the
|
||||
* table, and only then double the size of the loop table.
|
||||
* To grow the loop table that we take the traceMonitor lock, double check that
|
||||
* no other thread grew the table while we were deciding to grow the table, and
|
||||
* only then double the size of the loop table.
|
||||
*
|
||||
* The initial size of the table is 2^8 and grows to at most 2^24 entries. It
|
||||
* is extended at most a constant number of times (C=16) by doubling its size
|
||||
* every time. When extending the table, each slot is initially filled with
|
||||
* JS_ZERO.
|
||||
* JSVAL_ZERO.
|
||||
*/
|
||||
bool
|
||||
js_GrowLoopTable(JSContext* cx, uint32 index)
|
||||
js_GrowLoopTable(JSContext* cx, uint32 slot)
|
||||
{
|
||||
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
||||
uint32 oldSize = tm->loopTableSize;
|
||||
|
||||
if (index >= oldSize) {
|
||||
if (slot >= oldSize) {
|
||||
uint32 newSize = oldSize << 1;
|
||||
jsval* t = tm->loopTable;
|
||||
if (t) {
|
||||
|
@ -43,16 +43,14 @@
|
||||
#include "jslock.h"
|
||||
|
||||
/*
|
||||
* Trace monitor. Every runtime is associated with a trace monitor that
|
||||
* keeps track of loop frequencies for all JavaScript code loaded into
|
||||
* that runtime. For this we use a loop table. Entries in the loop
|
||||
* table are requested by jsemit.c during compilation. By using atomic
|
||||
* pre-increment obtaining the next index is lock free, but to allocate
|
||||
* more table space the trace monitor lock has to be aquired first.
|
||||
* Trace monitor. Every runtime is associated with a trace monitor that keeps
|
||||
* track of loop frequencies for all JavaScript code loaded into that runtime.
|
||||
* For this we use a loop table. Adjacent slots in the loop table, one for each
|
||||
* loop header in a given script, are requested using lock-free synchronization
|
||||
* from the runtime-wide loop table slot space, when the script is compiled.
|
||||
*
|
||||
* The loop table also doubles as tree pointer table once a loop
|
||||
* achieves a certain number of iterations and we recorded a tree for
|
||||
* that loop.
|
||||
* The loop table also doubles as trace tree pointer table once a loop achieves
|
||||
* a certain number of iterations and we recorded a tree for that loop.
|
||||
*/
|
||||
struct JSTraceMonitor {
|
||||
jsval* loopTable;
|
||||
@ -61,9 +59,8 @@ struct JSTraceMonitor {
|
||||
|
||||
#define TRACE_THRESHOLD 10
|
||||
|
||||
bool js_InitTracer(JSRuntime* rt);
|
||||
bool js_AllocateLoopTableSlots(JSContext* cx, uint32 nloops, uint32 *basep);
|
||||
void js_FreeLoopTableSlots(JSContext* cx, uint32 base, uint32 nloops);
|
||||
bool js_GrowLoopTable(JSContext* cx, uint32 index);
|
||||
uint32 js_AllocateLoopTableSlots(JSContext* cx, uint32 nloops);
|
||||
void js_FreeLoopTableSlots(JSContext* cx, uint32 base, uint32 nloops);
|
||||
bool js_GrowLoopTable(JSContext* cx, uint32 slot);
|
||||
|
||||
#endif /* jstracer_h___ */
|
||||
|
Loading…
Reference in New Issue
Block a user