-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Rework inline cache handling #609
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -602,21 +602,30 @@ typedef struct JSInlineCache { | |
uint32_t hash_bits; | ||
JSInlineCacheHashSlot **hash; | ||
JSInlineCacheRingSlot *cache; | ||
uint32_t updated_offset; | ||
BOOL updated; | ||
} JSInlineCache; | ||
|
||
#define INLINE_CACHE_MISS ((uint32_t)-1) // sentinel | ||
|
||
// This is a struct so we don't tie up two argument registers in calls to | ||
// JS_GetPropertyInternal2 and JS_SetPropertyInternal2 in the common case | ||
// where there is no IC and therefore no offset to update. | ||
typedef struct JSInlineCacheUpdate { | ||
JSInlineCache *ic; | ||
uint32_t offset; | ||
} JSInlineCacheUpdate; | ||
|
||
static JSInlineCache *init_ic(JSContext *ctx); | ||
static int rebuild_ic(JSContext *ctx, JSInlineCache *ic); | ||
static int resize_ic_hash(JSContext *ctx, JSInlineCache *ic); | ||
static int free_ic(JSRuntime *rt, JSInlineCache *ic); | ||
static uint32_t add_ic_slot(JSContext *ctx, JSInlineCache *ic, JSAtom atom, JSObject *object, | ||
uint32_t prop_offset); | ||
static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu, | ||
JSAtom atom, JSObject *object, uint32_t prop_offset); | ||
|
||
static int32_t get_ic_prop_offset(JSInlineCache *ic, uint32_t cache_offset, | ||
JSShape *shape) | ||
static uint32_t get_ic_prop_offset(const JSInlineCacheUpdate *icu, | ||
JSShape *shape) | ||
{ | ||
uint32_t i; | ||
uint32_t i, cache_offset = icu->offset; | ||
JSInlineCache *ic = icu->ic; | ||
JSInlineCacheRingSlot *cr; | ||
JSShape *shape_slot; | ||
assert(cache_offset < ic->capacity); | ||
|
@@ -635,7 +644,7 @@ static int32_t get_ic_prop_offset(JSInlineCache *ic, uint32_t cache_offset, | |
} | ||
} | ||
|
||
return -1; | ||
return INLINE_CACHE_MISS; | ||
} | ||
|
||
static force_inline JSAtom get_ic_atom(JSInlineCache *ic, uint32_t cache_offset) | ||
|
@@ -7283,7 +7292,8 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, | |
|
||
static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, | ||
JSAtom prop, JSValue this_obj, | ||
JSInlineCache* ic, BOOL throw_ref_error) | ||
JSInlineCacheUpdate *icu, | ||
BOOL throw_ref_error) | ||
{ | ||
JSObject *p; | ||
JSProperty *pr; | ||
|
@@ -7352,9 +7362,8 @@ static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, | |
continue; | ||
} | ||
} else { | ||
if (ic && proto_depth == 0 && p->shape->is_hashed) { | ||
ic->updated = TRUE; | ||
ic->updated_offset = add_ic_slot(ctx, ic, prop, p, offset); | ||
if (icu && proto_depth == 0 && p->shape->is_hashed) { | ||
add_ic_slot(ctx, icu, prop, p, offset); | ||
} | ||
return js_dup(pr->u.value); | ||
} | ||
|
@@ -7444,20 +7453,20 @@ JSValue JS_GetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop) | |
|
||
static JSValue JS_GetPropertyInternalWithIC(JSContext *ctx, JSValue obj, | ||
JSAtom prop, JSValue this_obj, | ||
JSInlineCache *ic, int32_t offset, | ||
JSInlineCacheUpdate *icu, | ||
BOOL throw_ref_error) | ||
{ | ||
uint32_t tag; | ||
uint32_t tag, offset; | ||
JSObject *p; | ||
tag = JS_VALUE_GET_TAG(obj); | ||
if (unlikely(tag != JS_TAG_OBJECT)) | ||
goto slow_path; | ||
p = JS_VALUE_GET_OBJ(obj); | ||
offset = get_ic_prop_offset(ic, offset, p->shape); | ||
if (likely(offset >= 0)) | ||
offset = get_ic_prop_offset(icu, p->shape); | ||
if (likely(offset != INLINE_CACHE_MISS)) | ||
return js_dup(p->prop[offset].u.value); | ||
slow_path: | ||
return JS_GetPropertyInternal2(ctx, obj, prop, this_obj, ic, throw_ref_error); | ||
return JS_GetPropertyInternal2(ctx, obj, prop, this_obj, icu, throw_ref_error); | ||
} | ||
|
||
static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) | ||
|
@@ -8611,9 +8620,9 @@ static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) | |
the new property is not added and an error is raised. | ||
'obj' must be an object when obj != this_obj. | ||
*/ | ||
static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, | ||
JSAtom prop, JSValue val, JSValue this_obj, | ||
int flags, JSInlineCache *ic) | ||
static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, JSAtom prop, | ||
JSValue val, JSValue this_obj, int flags, | ||
JSInlineCacheUpdate *icu) | ||
{ | ||
JSObject *p, *p1; | ||
JSShapeProperty *prs; | ||
|
@@ -8649,9 +8658,8 @@ static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, | |
if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | | ||
JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { | ||
/* fast case */ | ||
if (ic && p->shape->is_hashed) { | ||
ic->updated = TRUE; | ||
ic->updated_offset = add_ic_slot(ctx, ic, prop, p, offset); | ||
if (icu && p->shape->is_hashed) { | ||
add_ic_slot(ctx, icu, prop, p, offset); | ||
} | ||
set_value(ctx, &pr->u.value, val); | ||
return TRUE; | ||
|
@@ -8876,23 +8884,24 @@ int JS_SetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop, JSValue val) | |
return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, JS_PROP_THROW, NULL); | ||
} | ||
|
||
// XXX(bnoordhuis) only used by OP_put_field_ic, maybe inline at call site | ||
static int JS_SetPropertyInternalWithIC(JSContext *ctx, JSValue this_obj, | ||
JSAtom prop, JSValue val, int flags, | ||
JSInlineCache *ic, int32_t offset) { | ||
uint32_t tag; | ||
JSInlineCacheUpdate *icu) { | ||
uint32_t tag, offset; | ||
JSObject *p; | ||
tag = JS_VALUE_GET_TAG(this_obj); | ||
if (unlikely(tag != JS_TAG_OBJECT)) | ||
goto slow_path; | ||
p = JS_VALUE_GET_OBJ(this_obj); | ||
offset = get_ic_prop_offset(ic, offset, p->shape); | ||
if (likely(offset >= 0)) { | ||
offset = get_ic_prop_offset(icu, p->shape); | ||
if (likely(offset != INLINE_CACHE_MISS)) { | ||
set_value(ctx, &p->prop[offset].u.value, val); | ||
return TRUE; | ||
} | ||
slow_path: | ||
return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, | ||
flags, ic); | ||
flags, icu); | ||
} | ||
|
||
/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ | ||
|
@@ -16129,16 +16138,17 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, | |
{ | ||
JSValue val; | ||
JSAtom atom; | ||
JSInlineCacheUpdate icu; | ||
atom = get_u32(pc); | ||
pc += 4; | ||
sf->cur_pc = pc; | ||
val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], ic, FALSE); | ||
icu = (JSInlineCacheUpdate){ic, INLINE_CACHE_MISS}; | ||
val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], &icu, FALSE); | ||
if (unlikely(JS_IsException(val))) | ||
goto exception; | ||
if (ic && ic->updated == TRUE) { | ||
ic->updated = FALSE; | ||
if (icu.offset != INLINE_CACHE_MISS) { | ||
put_u8(pc - 5, OP_get_field_ic); | ||
put_u32(pc - 4, ic->updated_offset); | ||
put_u32(pc - 4, icu.offset); | ||
JS_FreeAtom(ctx, atom); | ||
} | ||
JS_FreeValue(ctx, sp[-1]); | ||
|
@@ -16150,33 +16160,39 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, | |
{ | ||
JSValue val; | ||
JSAtom atom; | ||
int32_t ic_offset; | ||
uint32_t ic_offset; | ||
JSInlineCacheUpdate icu; | ||
ic_offset = get_u32(pc); | ||
atom = get_ic_atom(ic, ic_offset); | ||
pc += 4; | ||
sf->cur_pc = pc; | ||
val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], ic, ic_offset, FALSE); | ||
ic->updated = FALSE; | ||
bnoordhuis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
icu = (JSInlineCacheUpdate){ic, ic_offset}; | ||
val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], &icu, FALSE); | ||
if (unlikely(JS_IsException(val))) | ||
goto exception; | ||
if (icu.offset != ic_offset) { | ||
put_u32(pc - 4, icu.offset); | ||
} | ||
JS_FreeValue(ctx, sp[-1]); | ||
sp[-1] = val; | ||
} | ||
BREAK; | ||
|
||
CASE(OP_get_field2): | ||
{ | ||
JSValue val; | ||
JSAtom atom; | ||
JSInlineCacheUpdate icu; | ||
atom = get_u32(pc); | ||
pc += 4; | ||
sf->cur_pc = pc; | ||
val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], NULL, FALSE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note how this was passing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a way to have a test for that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not at the moment (or not easily anyway) but I'm thinking about adding a debug module that would make it easier to inspect the textual representation of the bytecode (basically by using the existing bytecode printer to dump to a string.) You can technically do that already with bjson.write and grubbing through the binary output but that's super fragile. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A la the dis module in Python? That's cool! |
||
icu = (JSInlineCacheUpdate){ic, INLINE_CACHE_MISS}; | ||
val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], &icu, FALSE); | ||
if (unlikely(JS_IsException(val))) | ||
goto exception; | ||
if (ic != NULL && ic->updated == TRUE) { | ||
ic->updated = FALSE; | ||
if (icu.offset != INLINE_CACHE_MISS) { | ||
put_u8(pc - 5, OP_get_field2_ic); | ||
put_u32(pc - 4, ic->updated_offset); | ||
put_u32(pc - 4, icu.offset); | ||
JS_FreeAtom(ctx, atom); | ||
} | ||
*sp++ = val; | ||
|
@@ -16187,15 +16203,19 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, | |
{ | ||
JSValue val; | ||
JSAtom atom; | ||
int32_t ic_offset; | ||
uint32_t ic_offset; | ||
JSInlineCacheUpdate icu; | ||
ic_offset = get_u32(pc); | ||
atom = get_ic_atom(ic, ic_offset); | ||
pc += 4; | ||
sf->cur_pc = pc; | ||
val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], ic, ic_offset, FALSE); | ||
ic->updated = FALSE; | ||
icu = (JSInlineCacheUpdate){ic, ic_offset}; | ||
val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], &icu, FALSE); | ||
if (unlikely(JS_IsException(val))) | ||
goto exception; | ||
if (icu.offset != ic_offset) { | ||
put_u32(pc - 4, icu.offset); | ||
} | ||
*sp++ = val; | ||
} | ||
BREAK; | ||
|
@@ -16204,21 +16224,22 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, | |
{ | ||
int ret; | ||
JSAtom atom; | ||
JSInlineCacheUpdate icu; | ||
atom = get_u32(pc); | ||
pc += 4; | ||
sf->cur_pc = pc; | ||
icu = (JSInlineCacheUpdate){ic, INLINE_CACHE_MISS}; | ||
ret = JS_SetPropertyInternal2(ctx, | ||
sp[-2], atom, | ||
sp[-1], sp[-2], | ||
JS_PROP_THROW_STRICT, ic); | ||
JS_PROP_THROW_STRICT, &icu); | ||
JS_FreeValue(ctx, sp[-2]); | ||
sp -= 2; | ||
if (unlikely(ret < 0)) | ||
goto exception; | ||
if (ic != NULL && ic->updated == TRUE) { | ||
ic->updated = FALSE; | ||
if (icu.offset != INLINE_CACHE_MISS) { | ||
put_u8(pc - 5, OP_put_field_ic); | ||
put_u32(pc - 4, ic->updated_offset); | ||
put_u32(pc - 4, icu.offset); | ||
JS_FreeAtom(ctx, atom); | ||
} | ||
} | ||
|
@@ -16228,17 +16249,22 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, | |
{ | ||
int ret; | ||
JSAtom atom; | ||
int32_t ic_offset; | ||
uint32_t ic_offset; | ||
JSInlineCacheUpdate icu; | ||
ic_offset = get_u32(pc); | ||
atom = get_ic_atom(ic, ic_offset); | ||
pc += 4; | ||
sf->cur_pc = pc; | ||
ret = JS_SetPropertyInternalWithIC(ctx, sp[-2], atom, sp[-1], JS_PROP_THROW_STRICT, ic, ic_offset); | ||
ic->updated = FALSE; | ||
icu = (JSInlineCacheUpdate){ic, ic_offset}; | ||
ret = JS_SetPropertyInternalWithIC(ctx, sp[-2], atom, sp[-1], | ||
JS_PROP_THROW_STRICT, &icu); | ||
JS_FreeValue(ctx, sp[-2]); | ||
sp -= 2; | ||
if (unlikely(ret < 0)) | ||
goto exception; | ||
if (icu.offset != ic_offset) { | ||
put_u32(pc - 4, icu.offset); | ||
} | ||
} | ||
BREAK; | ||
|
||
|
@@ -54433,8 +54459,6 @@ JSInlineCache *init_ic(JSContext *ctx) | |
if (unlikely(!ic->hash)) | ||
goto fail; | ||
ic->cache = NULL; | ||
ic->updated = FALSE; | ||
ic->updated_offset = 0; | ||
return ic; | ||
fail: | ||
js_free(ctx, ic); | ||
|
@@ -54517,11 +54541,12 @@ int free_ic(JSRuntime* rt, JSInlineCache *ic) | |
return 0; | ||
} | ||
|
||
uint32_t add_ic_slot(JSContext *ctx, JSInlineCache *ic, JSAtom atom, JSObject *object, | ||
uint32_t prop_offset) | ||
static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu, | ||
JSAtom atom, JSObject *object, uint32_t prop_offset) | ||
{ | ||
int32_t i; | ||
uint32_t h; | ||
JSInlineCache *ic = icu->ic; | ||
JSInlineCacheHashSlot *ch; | ||
JSInlineCacheRingSlot *cr; | ||
JSShape *sh; | ||
|
@@ -54550,7 +54575,7 @@ uint32_t add_ic_slot(JSContext *ctx, JSInlineCache *ic, JSAtom atom, JSObject *o | |
js_free_shape_null(ctx->rt, sh); | ||
cr->prop_offset[i] = prop_offset; | ||
end: | ||
return ch->index; | ||
icu->offset = ch->index; | ||
} | ||
|
||
/* CallSite */ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Offsets are stored as uint32_t so this should have been uint32_t from the start. Not that it matters in a practical sense, 2^31-1 is large enough by a wide margin. Still, bigger is better according to my girlfriend and 2^32 > 2^31.