First stage of loop table work; bitmap free space management and GC hook-up still to come.

This commit is contained in:
Brendan Eich 2008-06-03 23:52:28 -07:00
Родитель e49b7995d0
Коммит 2a2bfa5432
10 изменённых файлов: 124 добавлений и 130 удалений

Просмотреть файл

@ -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 = &regs;
@ -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 == &regs);
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___ */