High-level CSE for shape guards (518448, r=jorendorff).

This commit is contained in:
Brendan Eich 2009-09-25 14:07:08 -07:00
Родитель 8a6c2dc53d
Коммит fd25e563c7
5 изменённых файлов: 177 добавлений и 14 удалений

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

@ -1022,8 +1022,27 @@ JSScope::reportReadOnlyScope(JSContext *cx)
void
JSScope::generateOwnShape(JSContext *cx)
{
if (object)
js_LeaveTraceIfGlobalObject(cx, object);
#ifdef JS_TRACER
if (object) {
js_LeaveTraceIfGlobalObject(cx, object);
/*
* The JIT must have arranged to re-guard after any unpredictable shape
* change, so if we are on trace here, we should already be prepared to
* bail off trace.
*/
JS_ASSERT_IF(JS_ON_TRACE(cx), cx->bailExit);
/*
* If we are recording, here is where we forget already-guarded shapes.
* Any subsequent property operation upon object on the trace currently
* being recorded will re-guard (and re-memoize).
*/
JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
if (TraceRecorder *tr = tm->recorder)
tr->forgetGuardedShapesForObject(object);
}
#endif
shape = js_GenerateShape(cx, false);
setOwnShape();

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

@ -2181,6 +2181,8 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
this->newobj_ins = NULL;
this->loopLabel = NULL;
guardedShapeTable.ops = NULL;
#ifdef JS_JIT_SPEW
debug_only_print0(LC_TMMinimal, "\n");
debug_only_printf(LC_TMMinimal, "Recording starting from %s:%u@%u (FragID=%06u)\n",
@ -2242,7 +2244,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
fragment->loopLabel = entryLabel;
})
lirbuf->sp = addName(lir->insLoad(LIR_ldp, lirbuf->state, (int)offsetof(InterpState, sp)), "sp");
lirbuf->sp = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, sp)), "sp");
lirbuf->rp = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, rp)), "rp");
cx_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, cx)), "cx");
eos_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eos)), "eos");
@ -2299,6 +2301,8 @@ TraceRecorder::~TraceRecorder()
delete func_filter;
delete float_filter;
delete lir_buf_writer;
forgetGuardedShapes();
}
bool
@ -8583,20 +8587,136 @@ TraceRecorder::binary(LOpcode op)
return JSRS_STOP;
}
struct GuardedShapeEntry : public JSDHashEntryStub
{
JSObject* obj;
};
#ifdef DEBUG
#include <stdio.h>
static FILE* shapefp = NULL;
static void
DumpShape(JSObject* obj, const char* prefix)
{
JSScope* scope = OBJ_SCOPE(obj);
if (!shapefp) {
shapefp = fopen("/tmp/shapes.dump", "w");
if (!shapefp)
return;
}
fprintf(shapefp, "\n%s: shape %u flags %x\n", prefix, scope->shape, scope->flags);
for (JSScopeProperty* sprop = scope->lastProp; sprop; sprop = sprop->parent) {
if (JSID_IS_ATOM(sprop->id)) {
fprintf(shapefp, " %s", JS_GetStringBytes(JSVAL_TO_STRING(ID_TO_VALUE(sprop->id))));
} else {
JS_ASSERT(!JSID_IS_OBJECT(sprop->id));
fprintf(shapefp, " %d", JSID_TO_INT(sprop->id));
}
fprintf(shapefp, " %u %p %p %x %x %d\n",
sprop->slot, sprop->getter, sprop->setter, sprop->attrs, sprop->flags,
sprop->shortid);
}
fflush(shapefp);
}
static JSDHashOperator
DumpShapeEnumerator(JSDHashTable* table, JSDHashEntryHdr* hdr, uint32 number, void* arg)
{
GuardedShapeEntry* entry = (GuardedShapeEntry*) hdr;
const char* prefix = (const char*) arg;
DumpShape(entry->obj, prefix);
return JS_DHASH_NEXT;
}
void
TraceRecorder::dumpGuardedShapes(const char* prefix)
{
if (guardedShapeTable.ops)
JS_DHashTableEnumerate(&guardedShapeTable, DumpShapeEnumerator, (void*) prefix);
}
#endif /* DEBUG */
JS_REQUIRES_STACK JSRecordingStatus
TraceRecorder::guardShape(LIns* obj_ins, JSObject* obj, uint32 shape, const char* guardName,
LIns* map_ins, VMSideExit* exit)
{
if (!guardedShapeTable.ops) {
JS_DHashTableInit(&guardedShapeTable, JS_DHashGetStubOps(), NULL,
sizeof(GuardedShapeEntry), JS_DHASH_MIN_SIZE);
}
// Test (with add if missing) for a remembered guard for (obj_ins, obj).
GuardedShapeEntry* entry = (GuardedShapeEntry*)
JS_DHashTableOperate(&guardedShapeTable, obj_ins, JS_DHASH_ADD);
if (!entry) {
JS_ReportOutOfMemory(cx);
return JSRS_ERROR;
}
// If already guarded, emit an assertion that the shape matches.
if (entry->key) {
JS_ASSERT(entry->key == obj_ins);
JS_ASSERT(entry->obj == obj);
debug_only_stmt(
lir->insAssert(lir->ins2i(LIR_eq,
lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)),
shape));
)
return JSRS_CONTINUE;
}
// Not yet guarded. Remember obj_ins along with obj (for invalidation).
entry->key = obj_ins;
entry->obj = obj;
debug_only_stmt(DumpShape(obj, "guard");)
debug_only_stmt(fprintf(shapefp, "for obj_ins %p\n", obj_ins);)
// Finally, emit the shape guard.
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape");
guard(true,
addName(lir->ins2i(LIR_eq, shape_ins, shape), guardName),
exit);
return JSRS_CONTINUE;
}
static JSDHashOperator
ForgetGuardedShapesForObject(JSDHashTable* table, JSDHashEntryHdr* hdr, uint32 number, void* arg)
{
GuardedShapeEntry* entry = (GuardedShapeEntry*) hdr;
if (entry->obj == arg) {
debug_only_stmt(DumpShape(entry->obj, "forget");)
return JS_DHASH_REMOVE;
}
return JS_DHASH_NEXT;
}
void
TraceRecorder::forgetGuardedShapesForObject(JSObject* obj)
{
if (guardedShapeTable.ops)
JS_DHashTableEnumerate(&guardedShapeTable, ForgetGuardedShapesForObject, obj);
}
void
TraceRecorder::forgetGuardedShapes()
{
if (guardedShapeTable.ops) {
debug_only_stmt(dumpGuardedShapes("forget-all");)
JS_DHashTableFinish(&guardedShapeTable);
guardedShapeTable.ops = NULL;
}
}
JS_STATIC_ASSERT(offsetof(JSObjectOps, objectMap) == 0);
inline LIns*
TraceRecorder::map(LIns *obj_ins)
TraceRecorder::map(LIns* obj_ins)
{
return addName(lir->insLoad(LIR_ldp, obj_ins, (int) offsetof(JSObject, map)), "map");
}
@ -8776,7 +8896,7 @@ TraceRecorder::guardPropertyCacheHit(LIns* obj_ins,
// Otherwise guard on key object exact match.
if (PCVCAP_TAG(entry->vcap) <= 1) {
if (aobj != globalObj)
guardShape(obj_ins, aobj, entry->kshape, "guard_kshape", map_ins, exit);
CHECK_STATUS(guardShape(obj_ins, aobj, entry->kshape, "guard_kshape", map_ins, exit));
if (entry->adding()) {
if (aobj == globalObj)
@ -8825,7 +8945,7 @@ TraceRecorder::guardPropertyCacheHit(LIns* obj_ins,
} else {
obj2_ins = INS_CONSTOBJ(obj2);
}
guardShape(obj2_ins, obj2, vshape, "guard_vshape", map(obj2_ins), exit);
CHECK_STATUS(guardShape(obj2_ins, obj2, vshape, "guard_vshape", map(obj2_ins), exit));
}
pcval = entry->vword;
@ -9114,7 +9234,7 @@ TraceRecorder::guardPrototypeHasNoIndexedProperties(JSObject* obj, LIns* obj_ins
return JSRS_STOP;
while (guardHasPrototype(obj, obj_ins, &obj, &obj_ins, exit))
guardShape(obj_ins, obj, OBJ_SHAPE(obj), "guard(shape)", map(obj_ins), exit);
CHECK_STATUS(guardShape(obj_ins, obj, OBJ_SHAPE(obj), "guard(shape)", map(obj_ins), exit));
return JSRS_CONTINUE;
}
@ -10589,9 +10709,11 @@ TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop
if (obj == globalObj)
ABORT_TRACE("can't trace function-valued property set in branded global scope");
enterDeepBailCall();
LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins };
LIns* ok_ins = lir->insCall(&MethodWriteBarrier_ci, args);
guard(false, lir->ins_eq0(ok_ins), OOM_EXIT);
leaveDeepBailCall();
}
// Find obj2. If entry->adding(), the TAG bits are all 0.
@ -10700,6 +10822,9 @@ TraceRecorder::enterDeepBailCall()
// Tell nanojit not to discard or defer stack writes before this call.
GuardRecord* guardRec = createGuardRecord(exit);
lir->insGuard(LIR_xbarrier, NULL, guardRec);
// Forget about guarded shapes, since deep bailers can reshape the world.
forgetGuardedShapes();
return exit;
}
@ -11853,7 +11978,8 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32 *slotp, LIns** v_insp,
LIns* map_ins = map(obj_ins);
LIns* ops_ins;
if (map_is_native(obj->map, map_ins, ops_ins)) {
guardShape(obj_ins, obj, OBJ_SHAPE(obj), "guard(shape)", map_ins, exit);
CHECK_STATUS(guardShape(obj_ins, obj, OBJ_SHAPE(obj), "guard(shape)", map_ins,
exit));
} else if (!guardDenseArray(obj, obj_ins, exit)) {
ABORT_TRACE("non-native object involved in undefined property access");
}
@ -11932,8 +12058,10 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32 *slotp, LIns** v_insp,
* leaked to the calling script.
*/
if (isMethod && !cx->fp->imacpc) {
enterDeepBailCall();
LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins };
v_ins = lir->insCall(&MethodReadBarrier_ci, args);
leaveDeepBailCall();
}
if (slotp) {
@ -12678,7 +12806,7 @@ TraceRecorder::record_JSOP_INSTANCEOF()
stack(-2, lir->insCall(&HasInstance_ci, args));
LIns* status_ins = lir->insLoad(LIR_ld,
lirbuf->state,
(int) offsetof(InterpState, builtinStatus));
offsetof(InterpState, builtinStatus));
pendingGuardCondition = lir->ins_eq0(status_ins);
leaveDeepBailCall();

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

@ -47,6 +47,7 @@
#include "jstypes.h"
#include "jsbuiltins.h"
#include "jscntxt.h"
#include "jsdhash.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
@ -837,9 +838,17 @@ class TraceRecorder {
JS_REQUIRES_STACK JSRecordingStatus unary(nanojit::LOpcode op);
JS_REQUIRES_STACK JSRecordingStatus binary(nanojit::LOpcode op);
JS_REQUIRES_STACK void guardShape(nanojit::LIns* obj_ins, JSObject* obj,
uint32 shape, const char* guardName,
nanojit::LIns* map_ins, VMSideExit* exit);
JS_REQUIRES_STACK JSRecordingStatus guardShape(nanojit::LIns* obj_ins, JSObject* obj,
uint32 shape, const char* name,
nanojit::LIns* map_ins, VMSideExit* exit);
JSDHashTable guardedShapeTable;
#ifdef DEBUG
void dumpGuardedShapes(const char* prefix);
#endif
void forgetGuardedShapes();
inline nanojit::LIns* map(nanojit::LIns *obj_ins);
JS_REQUIRES_STACK bool map_is_native(JSObjectMap* map, nanojit::LIns* map_ins,
@ -1047,7 +1056,7 @@ public:
JS_REQUIRES_STACK JSRecordingStatus record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
JS_REQUIRES_STACK JSRecordingStatus record_NativeCallComplete();
TreeInfo* getTreeInfo() { return treeInfo; }
void forgetGuardedShapesForObject(JSObject* obj);
#ifdef DEBUG
void tprint(const char *format, int count, nanojit::LIns *insa[]);

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

@ -1079,6 +1079,13 @@ namespace nanojit
virtual LInsp insSkip(size_t size) {
return out->insSkip(size);
}
void insAssert(LIns* expr) {
#if defined DEBUG
LIns* branch = insBranch(LIR_jt, expr, NULL);
ins0(LIR_dbreak);
branch->setTarget(ins0(LIR_label));
#endif
}
// convenience functions

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

@ -74,7 +74,7 @@
OPDEF(start, 0, 0, Op0) // start of a fragment
OPDEF(regfence, 1, 0, Op0) // register fence, no register allocation is allowed across this meta instruction
OPDEF(skip, 2, 1, Sk) // holds blobs ("payloads") of data; also links pages
OPDEF(unused3, 3,-1, None)
OPDEF(dbreak, 3, 0, Op0)
OPDEF(unused4, 4,-1, None)
OPDEF(unused5, 5,-1, None)
OPDEF(unused6, 6,-1, None)