IC structure

This commit is contained in:
2025-12-28 23:38:10 -06:00
parent dca1963b5d
commit 6d7581eff8
3 changed files with 178 additions and 23 deletions

View File

@@ -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);