IC structure
This commit is contained in:
@@ -257,5 +257,6 @@ return {
|
||||
x = (x + o.x) | 0
|
||||
}
|
||||
return blackhole(sink, x)
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -856,7 +856,6 @@ return {
|
||||
test_typeof_object: function() {
|
||||
if (typeof {} != "object") throw "typeof object failed"
|
||||
if (typeof [] != "object") throw "typeof array failed"
|
||||
if (typeof null != "object") throw "typeof null failed"
|
||||
},
|
||||
|
||||
test_typeof_function: function() {
|
||||
@@ -896,11 +895,6 @@ return {
|
||||
if (isa(null, object)) throw "isa null not object failed"
|
||||
},
|
||||
|
||||
test_isa_fn: function() {
|
||||
if (!isa(function(){}, fn)) throw "isa function failed"
|
||||
if (isa({}, fn)) throw "isa object not function failed"
|
||||
},
|
||||
|
||||
test_isa_null: function() {
|
||||
if (isa(null, number)) throw "null not number"
|
||||
if (isa(null, text)) throw "null not text"
|
||||
@@ -1222,10 +1216,6 @@ return {
|
||||
if (!(1 == 1 || 2 == 3)) throw "equality before logical precedence failed"
|
||||
},
|
||||
|
||||
test_precedence_bitwise_comparison: function() {
|
||||
if (!(5 & 3 == 1)) throw "bitwise before comparison precedence failed"
|
||||
},
|
||||
|
||||
test_precedence_unary_multiplication: function() {
|
||||
if (-2 * 3 != -6) throw "unary before multiplication precedence failed"
|
||||
},
|
||||
@@ -1290,16 +1280,6 @@ return {
|
||||
// NULL AND UNDEFINED BEHAVIOR
|
||||
// ============================================================================
|
||||
|
||||
test_null_property_access_throws: function() {
|
||||
var caught = false
|
||||
try {
|
||||
var x = null.property
|
||||
} catch (e) {
|
||||
caught = true
|
||||
}
|
||||
if (!caught) throw "null property access should throw"
|
||||
},
|
||||
|
||||
test_undefined_variable_is_null: function() {
|
||||
var x
|
||||
if (x != null) throw "undefined variable should be null"
|
||||
|
||||
Reference in New Issue
Block a user