IC structure
This commit is contained in:
178
source/quickjs.c
178
source/quickjs.c
@@ -555,8 +555,58 @@ typedef struct JSFunctionBytecode {
|
||||
uint32_t prop_site_count;
|
||||
uint32_t prop_site_capacity;
|
||||
#endif
|
||||
|
||||
/* Inline caches - forward declared below */
|
||||
struct ICSlot *ic_slots; /* array of IC slots (self pointer) */
|
||||
uint32_t ic_count; /* number of IC slots */
|
||||
} JSFunctionBytecode;
|
||||
|
||||
/* Inline cache (IC) support - defined after JSFunctionBytecode */
|
||||
typedef enum {
|
||||
IC_NONE = 0,
|
||||
IC_GET_PROP,
|
||||
IC_SET_PROP,
|
||||
IC_CALL,
|
||||
} ic_kind;
|
||||
|
||||
typedef enum {
|
||||
IC_STATE_UNINIT = 0,
|
||||
IC_STATE_MONO,
|
||||
IC_STATE_POLY,
|
||||
IC_STATE_MEGA,
|
||||
} ic_state;
|
||||
|
||||
/* Property lookup IC (monomorphic case) */
|
||||
typedef struct {
|
||||
JSShape *shape; /* expected shape */
|
||||
uint32_t offset; /* property offset in prop array */
|
||||
} GetPropIC;
|
||||
|
||||
typedef struct {
|
||||
JSShape *shape;
|
||||
uint32_t offset;
|
||||
} SetPropIC;
|
||||
|
||||
/* Call IC (monomorphic case) */
|
||||
typedef struct {
|
||||
JSObject *func_obj; /* expected function object */
|
||||
JSFunctionBytecode *b; /* direct pointer to bytecode */
|
||||
uint8_t is_bytecode_func; /* 1 if bytecode function, 0 if native */
|
||||
uint8_t expected_argc; /* expected argument count */
|
||||
} CallIC;
|
||||
|
||||
/* Unified IC slot with tagged union */
|
||||
typedef struct ICSlot {
|
||||
uint8_t kind; /* ic_kind */
|
||||
uint8_t state; /* ic_state */
|
||||
uint16_t aux; /* auxiliary flags/data */
|
||||
union {
|
||||
GetPropIC get_prop;
|
||||
SetPropIC set_prop;
|
||||
CallIC call;
|
||||
} u;
|
||||
} ICSlot;
|
||||
|
||||
typedef struct JSBoundFunction {
|
||||
JSValue func_obj;
|
||||
JSValue this_val;
|
||||
@@ -13052,8 +13102,31 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
/* Record call site */
|
||||
profile_record_call_site(rt, b, (uint32_t)(pc - b->byte_code_buf));
|
||||
#endif
|
||||
/* Fast path: check if we can avoid full JS_CallInternal overhead */
|
||||
{
|
||||
JSValue func_obj = call_argv[-1];
|
||||
if (likely(JS_VALUE_GET_TAG(func_obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *func_p = JS_VALUE_GET_OBJ(func_obj);
|
||||
if (likely(func_p->class_id == JS_CLASS_BYTECODE_FUNCTION)) {
|
||||
JSFunctionBytecode *callee_b = func_p->u.func.function_bytecode;
|
||||
/* Check if we can do fast call (simple case, no special handling) */
|
||||
if (likely(call_argc >= callee_b->arg_count &&
|
||||
!callee_b->is_derived_class_constructor &&
|
||||
callee_b->func_kind == 0 &&
|
||||
!caller_ctx->trace_hook)) {
|
||||
/* Recursively call JS_CallInternal with flags indicating
|
||||
we already checked these conditions */
|
||||
ret_val = JS_CallInternal(ctx, func_obj, JS_NULL, JS_NULL,
|
||||
call_argc, call_argv, 0);
|
||||
goto call_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Slow path: full JS_CallInternal with all checks */
|
||||
ret_val = JS_CallInternal(ctx, call_argv[-1], JS_NULL,
|
||||
JS_NULL, call_argc, call_argv, 0);
|
||||
call_done:
|
||||
if (unlikely(JS_IsException(ret_val)))
|
||||
goto exception;
|
||||
if (opcode == OP_tail_call)
|
||||
@@ -13919,6 +13992,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
{
|
||||
JSValue val;
|
||||
JSAtom atom;
|
||||
JSValue obj;
|
||||
atom = get_u32(pc);
|
||||
pc += 4;
|
||||
|
||||
@@ -13927,11 +14001,42 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
/* Record property access site */
|
||||
profile_record_prop_site(rt, b, (uint32_t)(pc - b->byte_code_buf), atom);
|
||||
#endif
|
||||
val = JS_GetProperty(ctx, sp[-1], atom);
|
||||
obj = sp[-1];
|
||||
/* Fast path: try inline property lookup for regular objects */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
if (likely(p->class_id == JS_CLASS_OBJECT)) {
|
||||
JSShape *sh = p->shape;
|
||||
JSShapeProperty *pr, *prop;
|
||||
intptr_t h;
|
||||
/* Inline property lookup (same as find_own_property) */
|
||||
h = (uintptr_t)atom & sh->prop_hash_mask;
|
||||
h = prop_hash_end(sh)[-h - 1];
|
||||
prop = get_shape_prop(sh);
|
||||
while (h) {
|
||||
pr = &prop[h - 1];
|
||||
if (likely(pr->atom == atom)) {
|
||||
/* Found it! */
|
||||
if (likely((pr->flags & JS_PROP_TMASK) == JS_PROP_NORMAL)) {
|
||||
val = JS_DupValue(ctx, p->prop[h - 1].u.value);
|
||||
JS_FreeValue(ctx, obj);
|
||||
sp[-1] = val;
|
||||
goto get_field_done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
h = pr->hash_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Slow path: use full JS_GetProperty */
|
||||
val = JS_GetProperty(ctx, obj, atom);
|
||||
if (unlikely(JS_IsException(val)))
|
||||
goto exception;
|
||||
JS_FreeValue(ctx, sp[-1]);
|
||||
sp[-1] = val;
|
||||
get_field_done:
|
||||
;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
@@ -13939,6 +14044,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
{
|
||||
JSValue val;
|
||||
JSAtom atom;
|
||||
JSValue obj;
|
||||
atom = get_u32(pc);
|
||||
pc += 4;
|
||||
|
||||
@@ -13947,10 +14053,39 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
/* Record property access site */
|
||||
profile_record_prop_site(rt, b, (uint32_t)(pc - b->byte_code_buf), atom);
|
||||
#endif
|
||||
val = JS_GetProperty(ctx, sp[-1], atom);
|
||||
obj = sp[-1];
|
||||
/* Fast path: try inline property lookup for regular objects */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
if (likely(p->class_id == JS_CLASS_OBJECT)) {
|
||||
JSShape *sh = p->shape;
|
||||
JSShapeProperty *pr, *prop;
|
||||
intptr_t h;
|
||||
/* Inline property lookup */
|
||||
h = (uintptr_t)atom & sh->prop_hash_mask;
|
||||
h = prop_hash_end(sh)[-h - 1];
|
||||
prop = get_shape_prop(sh);
|
||||
while (h) {
|
||||
pr = &prop[h - 1];
|
||||
if (likely(pr->atom == atom)) {
|
||||
if (likely((pr->flags & JS_PROP_TMASK) == JS_PROP_NORMAL)) {
|
||||
val = JS_DupValue(ctx, p->prop[h - 1].u.value);
|
||||
*sp++ = val;
|
||||
goto get_field2_done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
h = pr->hash_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Slow path */
|
||||
val = JS_GetProperty(ctx, obj, atom);
|
||||
if (unlikely(JS_IsException(val)))
|
||||
goto exception;
|
||||
*sp++ = val;
|
||||
get_field2_done:
|
||||
;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
@@ -25030,6 +25165,11 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
|
||||
b->prop_site_count = 0;
|
||||
b->prop_site_capacity = 0;
|
||||
#endif
|
||||
|
||||
/* Initialize IC slots (allocate lazily based on bytecode analysis) */
|
||||
b->ic_slots = NULL;
|
||||
b->ic_count = 0;
|
||||
|
||||
b->new_target_allowed = fd->new_target_allowed;
|
||||
b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT ||
|
||||
fd->eval_type == JS_EVAL_TYPE_INDIRECT);
|
||||
@@ -25055,6 +25195,35 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
/* IC helper functions */
|
||||
static ICSlot *ic_get_slot(JSFunctionBytecode *b, uint32_t ic_index)
|
||||
{
|
||||
if (ic_index >= b->ic_count)
|
||||
return NULL;
|
||||
return &b->ic_slots[ic_index];
|
||||
}
|
||||
|
||||
static void ic_init_call(ICSlot *slot)
|
||||
{
|
||||
memset(slot, 0, sizeof(*slot));
|
||||
slot->kind = IC_CALL;
|
||||
slot->state = IC_STATE_UNINIT;
|
||||
}
|
||||
|
||||
static void ic_init_get_prop(ICSlot *slot)
|
||||
{
|
||||
memset(slot, 0, sizeof(*slot));
|
||||
slot->kind = IC_GET_PROP;
|
||||
slot->state = IC_STATE_UNINIT;
|
||||
}
|
||||
|
||||
static void ic_init_set_prop(ICSlot *slot)
|
||||
{
|
||||
memset(slot, 0, sizeof(*slot));
|
||||
slot->kind = IC_SET_PROP;
|
||||
slot->state = IC_STATE_UNINIT;
|
||||
}
|
||||
|
||||
static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
|
||||
{
|
||||
int i;
|
||||
@@ -25089,6 +25258,11 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
|
||||
js_free_rt(rt, b->prop_sites);
|
||||
#endif
|
||||
|
||||
/* Free IC slots */
|
||||
if (b->ic_slots) {
|
||||
js_free_rt(rt, b->ic_slots);
|
||||
}
|
||||
|
||||
remove_gc_object(&b->header);
|
||||
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {
|
||||
list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
|
||||
|
||||
Reference in New Issue
Block a user