Compare commits
1 Commits
tramp_loop
...
ic_tramp_m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
828a1ddcf7 |
@@ -329,6 +329,39 @@ DEF( call3, 1, 1, 1, npopx)
|
||||
|
||||
DEF( is_null, 1, 1, 1, none)
|
||||
DEF( typeof_is_function, 1, 1, 1, none)
|
||||
|
||||
/* Per-site IC opcodes with quickening support
|
||||
* These opcodes embed an IC index instead of an atom. The atom is stored
|
||||
* in the IC slot itself. This allows direct IC lookup without hashing.
|
||||
*
|
||||
* Quickening: get_field (atom) -> get_field_ic (ic_index)
|
||||
* put_field (atom) -> put_field_ic (ic_index)
|
||||
*
|
||||
* The ic_index is a u16 that indexes into the function's per_site_ic array.
|
||||
* Format: opcode (1 byte) + ic_index (2 bytes) = 3 bytes total
|
||||
*/
|
||||
DEF( get_field_ic, 3, 1, 1, u16) /* quickened from get_field, ic_index operand */
|
||||
DEF( get_field2_ic, 3, 1, 2, u16) /* quickened from get_field2, ic_index operand */
|
||||
DEF( put_field_ic, 3, 2, 0, u16) /* quickened from put_field, ic_index operand */
|
||||
|
||||
/* Fused opcodes for get_loc + get_field pattern
|
||||
* These eliminate a dup/free pair by reading the local without pushing it,
|
||||
* then doing the field lookup directly.
|
||||
*
|
||||
* Format: opcode (1 byte) + loc_idx (2 bytes) + ic_index (2 bytes) = 5 bytes
|
||||
* Stack: -> value (reads local, does field lookup, pushes result)
|
||||
*
|
||||
* The local is NOT consumed (no refcount change to the local variable).
|
||||
*/
|
||||
DEF(get_field_loc_ic, 5, 0, 1, u32) /* fused: get_loc(idx) + get_field_ic, operand is loc_idx:u16 + ic_idx:u16 */
|
||||
DEF(get_field_arg_ic, 5, 0, 1, u32) /* fused: get_arg(idx) + get_field_ic, operand is arg_idx:u16 + ic_idx:u16 */
|
||||
|
||||
/* Fused opcodes for put_field pattern with local source
|
||||
* Format: opcode (1 byte) + loc_idx (2 bytes) + ic_index (2 bytes) = 5 bytes
|
||||
* Stack: value -> (reads local as object, sets field to value from stack)
|
||||
*/
|
||||
DEF(put_field_loc_ic, 5, 1, 0, u32) /* fused: get_loc(idx) + swap + put_field_ic, operand is loc_idx:u16 + ic_idx:u16 */
|
||||
DEF(put_field_arg_ic, 5, 1, 0, u32) /* fused: get_arg(idx) + swap + put_field_ic, operand is arg_idx:u16 + ic_idx:u16 */
|
||||
#endif
|
||||
|
||||
#undef DEF
|
||||
|
||||
923
source/quickjs.c
923
source/quickjs.c
@@ -640,6 +640,11 @@ typedef struct JSFunctionBytecode {
|
||||
/* Inline caches - forward declared below */
|
||||
struct ICSlot *ic_slots; /* array of IC slots (self pointer) */
|
||||
uint32_t ic_count; /* number of IC slots */
|
||||
|
||||
/* Per-site IC with quickening support */
|
||||
struct PerSiteIC *per_site_ic; /* dynamically allocated array */
|
||||
uint16_t per_site_ic_count; /* number of allocated slots */
|
||||
uint16_t per_site_ic_capacity; /* capacity of the array */
|
||||
} JSFunctionBytecode;
|
||||
|
||||
/* Inline cache (IC) support - defined after JSFunctionBytecode */
|
||||
@@ -657,37 +662,117 @@ typedef enum {
|
||||
IC_STATE_MEGA,
|
||||
} ic_state;
|
||||
|
||||
/* Property lookup IC (monomorphic case) */
|
||||
/* Max entries for polymorphic IC */
|
||||
#define IC_POLY_SIZE 4
|
||||
|
||||
/* Property lookup IC entry (for mono and poly) */
|
||||
typedef struct {
|
||||
JSShape *shape; /* expected shape */
|
||||
uint32_t offset; /* property offset in prop array */
|
||||
} PropICEntry;
|
||||
|
||||
/* Property lookup IC with polymorphic support */
|
||||
typedef struct {
|
||||
PropICEntry entries[IC_POLY_SIZE];
|
||||
uint8_t count; /* number of valid entries (0-4) */
|
||||
} GetPropIC;
|
||||
|
||||
typedef struct {
|
||||
JSShape *shape;
|
||||
uint32_t offset;
|
||||
PropICEntry entries[IC_POLY_SIZE];
|
||||
uint8_t count;
|
||||
} SetPropIC;
|
||||
|
||||
/* Call IC (monomorphic case) */
|
||||
/* Call IC entry - stores enough to dispatch directly to C functions */
|
||||
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 */
|
||||
JSObject *func_obj; /* expected function object (for identity check) */
|
||||
union {
|
||||
struct {
|
||||
JSFunctionBytecode *bytecode;
|
||||
} js;
|
||||
struct {
|
||||
JSCFunctionType c_function; /* direct C function pointer */
|
||||
JSContext *realm; /* function's realm */
|
||||
uint8_t cproto; /* calling convention */
|
||||
int16_t magic; /* magic value for *_magic variants */
|
||||
} native;
|
||||
} u;
|
||||
uint8_t is_native; /* 1 if C function, 0 if bytecode */
|
||||
} CallICEntry;
|
||||
|
||||
/* Call IC with polymorphic support */
|
||||
typedef struct {
|
||||
CallICEntry entries[IC_POLY_SIZE];
|
||||
uint8_t count; /* number of valid entries */
|
||||
} 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 */
|
||||
uint16_t aux; /* auxiliary flags/data (e.g., atom for prop IC) */
|
||||
union {
|
||||
GetPropIC get_prop;
|
||||
SetPropIC set_prop;
|
||||
CallIC call;
|
||||
} u;
|
||||
#ifdef DUMP_IC
|
||||
uint32_t hits;
|
||||
uint32_t miss_count;
|
||||
#endif
|
||||
} ICSlot;
|
||||
|
||||
/* ============================================================================
|
||||
* Per-site Inline Cache (IC) with Quickening Support
|
||||
* ============================================================================
|
||||
*
|
||||
* This is a new per-site IC design where each property access site gets its
|
||||
* own IC slot index embedded in the bytecode. The quickening process:
|
||||
*
|
||||
* 1. Cold bytecode: OP_get_field <atom:u32> (5 bytes)
|
||||
* 2. First execution: allocate IC slot, patch to OP_get_field_ic <ic_idx:u16> (3 bytes)
|
||||
* 3. Store atom in IC slot, no more hashing needed
|
||||
*
|
||||
* Benefits:
|
||||
* - No hash collisions (each site has dedicated slot)
|
||||
* - Faster lookup (direct array index vs hash table probe)
|
||||
* - Smaller quickened bytecode (3 bytes vs 5 bytes)
|
||||
*
|
||||
* The IC slots are stored in a dynamically growing array per function.
|
||||
*/
|
||||
|
||||
/* Per-site GetField IC slot */
|
||||
typedef struct GetFieldIC {
|
||||
JSAtom atom; /* property name (owned reference) */
|
||||
uint8_t state; /* IC_STATE_UNINIT/MONO/POLY/MEGA */
|
||||
uint8_t count; /* number of valid entries (0-4) */
|
||||
uint16_t pad;
|
||||
struct {
|
||||
JSShape *shape; /* expected shape */
|
||||
uint32_t prop_idx; /* property index in prop array */
|
||||
} entries[IC_POLY_SIZE];
|
||||
} GetFieldIC;
|
||||
|
||||
/* Per-site PutField IC slot */
|
||||
typedef struct PutFieldIC {
|
||||
JSAtom atom; /* property name (owned reference) */
|
||||
uint8_t state; /* IC_STATE_UNINIT/MONO/POLY/MEGA */
|
||||
uint8_t count; /* number of valid entries (0-4) */
|
||||
uint16_t pad;
|
||||
struct {
|
||||
JSShape *shape; /* expected shape */
|
||||
uint32_t prop_idx; /* property index in prop array */
|
||||
} entries[IC_POLY_SIZE];
|
||||
} PutFieldIC;
|
||||
|
||||
/* Per-site IC array stored in JSFunctionBytecode */
|
||||
typedef struct PerSiteIC {
|
||||
union {
|
||||
GetFieldIC get_field;
|
||||
PutFieldIC put_field;
|
||||
} u;
|
||||
uint8_t kind; /* ic_kind: IC_GET_PROP or IC_SET_PROP */
|
||||
} PerSiteIC;
|
||||
|
||||
typedef struct JSBoundFunction {
|
||||
JSValue func_obj;
|
||||
JSValue this_val;
|
||||
@@ -12569,6 +12654,368 @@ static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx)
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* Per-function Inline Cache (IC) System
|
||||
*
|
||||
* We use a per-function hash table keyed by pc_offset to avoid TLS collisions.
|
||||
* The hash table is allocated lazily on first IC miss. This approach:
|
||||
* - Eliminates the 256-entry TLS cache collision problem
|
||||
* - Supports polymorphic caching (up to IC_POLY_SIZE shapes per site)
|
||||
* - Keeps IC state with the function for better locality
|
||||
*
|
||||
* The IC cache uses a unified slot structure that can hold property or call ICs.
|
||||
* ============================================================================ */
|
||||
|
||||
#define IC_HASH_BITS 10
|
||||
#define IC_HASH_SIZE (1 << IC_HASH_BITS)
|
||||
|
||||
/* Property IC slot - supports polymorphic shapes */
|
||||
typedef struct PropICSlot {
|
||||
uint32_t pc_offset; /* bytecode offset of access site */
|
||||
struct {
|
||||
JSShape *shape; /* expected shape */
|
||||
uint32_t prop_idx; /* property index in shape */
|
||||
} entries[IC_POLY_SIZE];
|
||||
uint8_t count; /* number of valid entries (0 = uninit, 1-4 = poly) */
|
||||
} PropICSlot;
|
||||
|
||||
/* Call IC slot - for future use with managed stack frames */
|
||||
typedef struct CallICSlot {
|
||||
uint32_t pc_offset; /* bytecode offset of call site */
|
||||
JSObject *func_obj; /* expected function object (for identity check) */
|
||||
union {
|
||||
struct {
|
||||
JSFunctionBytecode *bytecode;
|
||||
} js;
|
||||
struct {
|
||||
JSCFunctionType c_function; /* direct C function pointer */
|
||||
JSContext *realm; /* function's realm */
|
||||
uint8_t cproto; /* calling convention */
|
||||
int16_t magic; /* magic value for *_magic variants */
|
||||
uint8_t arg_count; /* expected argument count (for padding) */
|
||||
} native;
|
||||
} u;
|
||||
uint8_t is_native; /* 1 if C function, 0 if bytecode */
|
||||
uint8_t valid; /* 1 if entry is valid */
|
||||
} CallICSlot;
|
||||
|
||||
/* Unified IC cache - one per function, lazily allocated */
|
||||
typedef struct ICCache {
|
||||
PropICSlot prop_slots[IC_HASH_SIZE];
|
||||
CallICSlot call_slots[IC_HASH_SIZE];
|
||||
} ICCache;
|
||||
|
||||
static force_inline uint32_t ic_hash(uint32_t pc_offset)
|
||||
{
|
||||
/* Knuth multiplicative hash - pc offsets are well-distributed */
|
||||
return (pc_offset * 2654435761u) >> (32 - IC_HASH_BITS);
|
||||
}
|
||||
|
||||
/* Ensure IC cache is allocated for a function */
|
||||
static force_inline ICCache *ic_ensure_cache(JSRuntime *rt, JSFunctionBytecode *b)
|
||||
{
|
||||
ICCache *cache = (ICCache *)b->ic_slots;
|
||||
if (likely(cache))
|
||||
return cache;
|
||||
|
||||
cache = js_mallocz_rt(rt, sizeof(ICCache));
|
||||
if (!cache)
|
||||
return NULL;
|
||||
b->ic_slots = (ICSlot *)cache;
|
||||
b->ic_count = IC_HASH_SIZE;
|
||||
return cache;
|
||||
}
|
||||
|
||||
/*
|
||||
* Property IC lookup - returns the property index if found, or -1 for miss.
|
||||
* Supports polymorphic lookup across multiple shapes.
|
||||
*/
|
||||
static force_inline int prop_ic_lookup(ICCache *cache, uint32_t pc_offset, JSShape *shape)
|
||||
{
|
||||
if (!cache)
|
||||
return -1;
|
||||
|
||||
uint32_t idx = ic_hash(pc_offset);
|
||||
PropICSlot *slot = &cache->prop_slots[idx];
|
||||
|
||||
/* Check if this slot is for our pc_offset */
|
||||
if (slot->count == 0 || slot->pc_offset != pc_offset)
|
||||
return -1;
|
||||
|
||||
/* Search through cached shapes (polymorphic) */
|
||||
for (int i = 0; i < slot->count; i++) {
|
||||
if (slot->entries[i].shape == shape) {
|
||||
return (int)slot->entries[i].prop_idx;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; /* Shape not in cache */
|
||||
}
|
||||
|
||||
/*
|
||||
* Property IC update - add or update a shape->prop_idx mapping.
|
||||
* If the slot is full (IC_POLY_SIZE entries), we go megamorphic and stop caching.
|
||||
*/
|
||||
static void prop_ic_update(JSRuntime *rt, JSFunctionBytecode *b,
|
||||
uint32_t pc_offset, JSShape *shape, uint32_t prop_idx)
|
||||
{
|
||||
ICCache *cache = ic_ensure_cache(rt, b);
|
||||
if (!cache)
|
||||
return;
|
||||
|
||||
uint32_t idx = ic_hash(pc_offset);
|
||||
PropICSlot *slot = &cache->prop_slots[idx];
|
||||
|
||||
/* If this is a different pc_offset (hash collision), reset the slot */
|
||||
if (slot->count > 0 && slot->pc_offset != pc_offset) {
|
||||
slot->count = 0;
|
||||
}
|
||||
|
||||
slot->pc_offset = pc_offset;
|
||||
|
||||
/* Check if shape is already cached */
|
||||
for (int i = 0; i < slot->count; i++) {
|
||||
if (slot->entries[i].shape == shape) {
|
||||
/* Update existing entry (prop_idx might have changed) */
|
||||
slot->entries[i].prop_idx = prop_idx;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add new entry if we have room */
|
||||
if (slot->count < IC_POLY_SIZE) {
|
||||
slot->entries[slot->count].shape = shape;
|
||||
slot->entries[slot->count].prop_idx = prop_idx;
|
||||
slot->count++;
|
||||
}
|
||||
/* else: megamorphic - stop caching this site */
|
||||
}
|
||||
|
||||
/*
|
||||
* Call IC lookup - returns the CallICSlot if found and valid, or NULL for miss.
|
||||
* The caller must still verify the cached function matches the actual callee.
|
||||
*/
|
||||
static force_inline CallICSlot *call_ic_lookup(ICCache *cache,
|
||||
uint32_t pc_offset)
|
||||
{
|
||||
if (!cache)
|
||||
return NULL;
|
||||
|
||||
uint32_t idx = ic_hash(pc_offset);
|
||||
CallICSlot *slot = &cache->call_slots[idx];
|
||||
|
||||
/* Check if this slot is valid and for our pc_offset */
|
||||
if (!slot->valid || slot->pc_offset != pc_offset)
|
||||
return NULL;
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call IC update - populate the cache for a call site.
|
||||
* Only caches C functions (native calls).
|
||||
*/
|
||||
static void call_ic_update(JSRuntime *rt, JSFunctionBytecode *b,
|
||||
uint32_t pc_offset, JSObject *func_obj)
|
||||
{
|
||||
ICCache *cache = ic_ensure_cache(rt, b);
|
||||
if (!cache)
|
||||
return;
|
||||
|
||||
/* Only cache C functions for now */
|
||||
if (func_obj->class_id != JS_CLASS_C_FUNCTION)
|
||||
return;
|
||||
|
||||
uint32_t idx = ic_hash(pc_offset);
|
||||
CallICSlot *slot = &cache->call_slots[idx];
|
||||
|
||||
/* If this is a different pc_offset (hash collision), overwrite */
|
||||
slot->pc_offset = pc_offset;
|
||||
slot->func_obj = func_obj;
|
||||
slot->is_native = 1;
|
||||
slot->u.native.c_function = func_obj->u.cfunc.c_function;
|
||||
slot->u.native.realm = func_obj->u.cfunc.realm;
|
||||
slot->u.native.cproto = func_obj->u.cfunc.cproto;
|
||||
slot->u.native.magic = func_obj->u.cfunc.magic;
|
||||
slot->u.native.arg_count = func_obj->u.cfunc.length;
|
||||
slot->valid = 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* Per-site IC with Quickening Support
|
||||
* ============================================================================ */
|
||||
|
||||
#define PER_SITE_IC_INITIAL_CAPACITY 8
|
||||
|
||||
/*
|
||||
* Allocate a new per-site IC slot for GetField.
|
||||
* Returns the IC index, or -1 on allocation failure.
|
||||
*/
|
||||
static int per_site_ic_alloc_get_field(JSRuntime *rt, JSFunctionBytecode *b, JSAtom atom)
|
||||
{
|
||||
if (b->per_site_ic_count >= b->per_site_ic_capacity) {
|
||||
uint16_t new_cap = b->per_site_ic_capacity ? b->per_site_ic_capacity * 2 : PER_SITE_IC_INITIAL_CAPACITY;
|
||||
if (new_cap > 65535)
|
||||
new_cap = 65535; /* max u16 */
|
||||
if (b->per_site_ic_count >= new_cap)
|
||||
return -1; /* overflow */
|
||||
PerSiteIC *new_arr = js_realloc_rt(rt, b->per_site_ic, new_cap * sizeof(PerSiteIC));
|
||||
if (!new_arr)
|
||||
return -1;
|
||||
b->per_site_ic = new_arr;
|
||||
b->per_site_ic_capacity = new_cap;
|
||||
}
|
||||
int idx = b->per_site_ic_count++;
|
||||
PerSiteIC *slot = &b->per_site_ic[idx];
|
||||
memset(slot, 0, sizeof(PerSiteIC));
|
||||
slot->kind = IC_GET_PROP;
|
||||
slot->u.get_field.atom = JS_DupAtomRT(rt, atom);
|
||||
slot->u.get_field.state = IC_STATE_UNINIT;
|
||||
slot->u.get_field.count = 0;
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new per-site IC slot for PutField.
|
||||
* Returns the IC index, or -1 on allocation failure.
|
||||
*/
|
||||
static int per_site_ic_alloc_put_field(JSRuntime *rt, JSFunctionBytecode *b, JSAtom atom)
|
||||
{
|
||||
if (b->per_site_ic_count >= b->per_site_ic_capacity) {
|
||||
uint16_t new_cap = b->per_site_ic_capacity ? b->per_site_ic_capacity * 2 : PER_SITE_IC_INITIAL_CAPACITY;
|
||||
if (new_cap > 65535)
|
||||
new_cap = 65535;
|
||||
if (b->per_site_ic_count >= new_cap)
|
||||
return -1;
|
||||
PerSiteIC *new_arr = js_realloc_rt(rt, b->per_site_ic, new_cap * sizeof(PerSiteIC));
|
||||
if (!new_arr)
|
||||
return -1;
|
||||
b->per_site_ic = new_arr;
|
||||
b->per_site_ic_capacity = new_cap;
|
||||
}
|
||||
int idx = b->per_site_ic_count++;
|
||||
PerSiteIC *slot = &b->per_site_ic[idx];
|
||||
memset(slot, 0, sizeof(PerSiteIC));
|
||||
slot->kind = IC_SET_PROP;
|
||||
slot->u.put_field.atom = JS_DupAtomRT(rt, atom);
|
||||
slot->u.put_field.state = IC_STATE_UNINIT;
|
||||
slot->u.put_field.count = 0;
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up a shape in a GetFieldIC slot.
|
||||
* Returns the property index if found, or -1 for miss.
|
||||
*/
|
||||
static force_inline int get_field_ic_lookup(GetFieldIC *ic, JSShape *shape)
|
||||
{
|
||||
for (int i = 0; i < ic->count; i++) {
|
||||
if (ic->entries[i].shape == shape) {
|
||||
return (int)ic->entries[i].prop_idx;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a GetFieldIC slot with a new shape->prop_idx mapping.
|
||||
*/
|
||||
static force_inline void get_field_ic_update(GetFieldIC *ic, JSShape *shape, uint32_t prop_idx)
|
||||
{
|
||||
/* Check if shape is already cached */
|
||||
for (int i = 0; i < ic->count; i++) {
|
||||
if (ic->entries[i].shape == shape) {
|
||||
ic->entries[i].prop_idx = prop_idx;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Add new entry if we have room */
|
||||
if (ic->count < IC_POLY_SIZE) {
|
||||
ic->entries[ic->count].shape = shape;
|
||||
ic->entries[ic->count].prop_idx = prop_idx;
|
||||
ic->count++;
|
||||
if (ic->count == 1)
|
||||
ic->state = IC_STATE_MONO;
|
||||
else
|
||||
ic->state = IC_STATE_POLY;
|
||||
} else {
|
||||
ic->state = IC_STATE_MEGA;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up a shape in a PutFieldIC slot.
|
||||
* Returns the property index if found, or -1 for miss.
|
||||
*/
|
||||
static force_inline int put_field_ic_lookup(PutFieldIC *ic, JSShape *shape)
|
||||
{
|
||||
for (int i = 0; i < ic->count; i++) {
|
||||
if (ic->entries[i].shape == shape) {
|
||||
return (int)ic->entries[i].prop_idx;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a PutFieldIC slot with a new shape->prop_idx mapping.
|
||||
*/
|
||||
static force_inline void put_field_ic_update(PutFieldIC *ic, JSShape *shape, uint32_t prop_idx)
|
||||
{
|
||||
/* Check if shape is already cached */
|
||||
for (int i = 0; i < ic->count; i++) {
|
||||
if (ic->entries[i].shape == shape) {
|
||||
ic->entries[i].prop_idx = prop_idx;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Add new entry if we have room */
|
||||
if (ic->count < IC_POLY_SIZE) {
|
||||
ic->entries[ic->count].shape = shape;
|
||||
ic->entries[ic->count].prop_idx = prop_idx;
|
||||
ic->count++;
|
||||
if (ic->count == 1)
|
||||
ic->state = IC_STATE_MONO;
|
||||
else
|
||||
ic->state = IC_STATE_POLY;
|
||||
} else {
|
||||
ic->state = IC_STATE_MEGA;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Safely access a GetFieldIC slot from per_site_ic array.
|
||||
* In debug builds, triggers an assertion on invalid access.
|
||||
* In release builds, returns NULL to allow graceful fallback.
|
||||
*/
|
||||
static force_inline GetFieldIC *get_field_ic_checked(JSFunctionBytecode *b, uint16_t ic_idx)
|
||||
{
|
||||
if (unlikely(ic_idx >= b->per_site_ic_count)) {
|
||||
assert(0 && "IC index out of bounds");
|
||||
return NULL;
|
||||
}
|
||||
PerSiteIC *slot = &b->per_site_ic[ic_idx];
|
||||
if (unlikely(slot->kind != IC_GET_PROP)) {
|
||||
assert(0 && "IC kind mismatch (expected GET_PROP)");
|
||||
return NULL;
|
||||
}
|
||||
return &slot->u.get_field;
|
||||
}
|
||||
|
||||
static force_inline PutFieldIC *put_field_ic_checked(JSFunctionBytecode *b, uint16_t ic_idx)
|
||||
{
|
||||
if (unlikely(ic_idx >= b->per_site_ic_count)) {
|
||||
assert(0 && "IC index out of bounds");
|
||||
return NULL;
|
||||
}
|
||||
PerSiteIC *slot = &b->per_site_ic[ic_idx];
|
||||
if (unlikely(slot->kind != IC_SET_PROP)) {
|
||||
assert(0 && "IC kind mismatch (expected SET_PROP)");
|
||||
return NULL;
|
||||
}
|
||||
return &slot->u.put_field;
|
||||
}
|
||||
|
||||
#define JS_CALL_FLAG_COPY_ARGV (1 << 1)
|
||||
|
||||
static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
|
||||
@@ -15021,6 +15468,450 @@ CASE(OP_push_i32):
|
||||
}
|
||||
BREAK;
|
||||
|
||||
/* Per-site IC opcodes with quickening support */
|
||||
CASE(OP_get_field_ic):
|
||||
{
|
||||
JSValue val;
|
||||
JSValue obj;
|
||||
uint16_t ic_idx;
|
||||
GetFieldIC *ic;
|
||||
|
||||
ic_idx = get_u16(pc);
|
||||
pc += 2;
|
||||
sf->cur_pc = pc;
|
||||
|
||||
obj = sp[-1];
|
||||
ic = get_field_ic_checked(b, ic_idx);
|
||||
if (unlikely(!ic)) {
|
||||
JS_ThrowInternalError(ctx, "invalid IC slot");
|
||||
goto exception;
|
||||
}
|
||||
|
||||
/* Fast path: object with cached shape */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
JSShape *sh = p->shape;
|
||||
|
||||
/* Direct IC lookup - no hashing */
|
||||
int prop_idx = get_field_ic_lookup(ic, sh);
|
||||
if (likely(prop_idx >= 0)) {
|
||||
JSProperty *pr = &p->prop[prop_idx];
|
||||
val = JS_DupValue(ctx, pr->u.value);
|
||||
JS_FreeValue(ctx, obj);
|
||||
sp[-1] = val;
|
||||
BREAK;
|
||||
}
|
||||
|
||||
/* IC miss: do lookup and update cache */
|
||||
{
|
||||
JSProperty *pr;
|
||||
JSShapeProperty *prs = find_own_property(&pr, p, ic->atom);
|
||||
|
||||
if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_NORMAL) {
|
||||
uint32_t idx = prs - get_shape_prop(sh);
|
||||
get_field_ic_update(ic, sh, idx);
|
||||
|
||||
val = JS_DupValue(ctx, pr->u.value);
|
||||
JS_FreeValue(ctx, obj);
|
||||
sp[-1] = val;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slow path: proto chain, getters, non-objects, etc. */
|
||||
val = JS_GetProperty(ctx, obj, ic->atom);
|
||||
if (unlikely(JS_IsException(val)))
|
||||
goto exception;
|
||||
JS_FreeValue(ctx, sp[-1]);
|
||||
sp[-1] = val;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE(OP_get_field2_ic):
|
||||
{
|
||||
JSValue val;
|
||||
JSValue obj;
|
||||
uint16_t ic_idx;
|
||||
GetFieldIC *ic;
|
||||
|
||||
ic_idx = get_u16(pc);
|
||||
pc += 2;
|
||||
sf->cur_pc = pc;
|
||||
|
||||
obj = sp[-1];
|
||||
ic = get_field_ic_checked(b, ic_idx);
|
||||
if (unlikely(!ic)) {
|
||||
JS_ThrowInternalError(ctx, "invalid IC slot");
|
||||
goto exception;
|
||||
}
|
||||
|
||||
/* Fast path: object with cached shape */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
JSShape *sh = p->shape;
|
||||
|
||||
int prop_idx = get_field_ic_lookup(ic, sh);
|
||||
if (likely(prop_idx >= 0)) {
|
||||
JSProperty *pr = &p->prop[prop_idx];
|
||||
val = JS_DupValue(ctx, pr->u.value);
|
||||
*sp++ = val;
|
||||
BREAK;
|
||||
}
|
||||
|
||||
/* IC miss: do lookup and update cache */
|
||||
{
|
||||
JSProperty *pr;
|
||||
JSShapeProperty *prs = find_own_property(&pr, p, ic->atom);
|
||||
|
||||
if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_NORMAL) {
|
||||
uint32_t idx = prs - get_shape_prop(sh);
|
||||
get_field_ic_update(ic, sh, idx);
|
||||
|
||||
val = JS_DupValue(ctx, pr->u.value);
|
||||
*sp++ = val;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slow path */
|
||||
val = JS_GetProperty(ctx, obj, ic->atom);
|
||||
if (unlikely(JS_IsException(val)))
|
||||
goto exception;
|
||||
*sp++ = val;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE(OP_put_field_ic):
|
||||
{
|
||||
int ret;
|
||||
uint16_t ic_idx;
|
||||
PutFieldIC *ic;
|
||||
JSValue obj, val;
|
||||
|
||||
ic_idx = get_u16(pc);
|
||||
pc += 2;
|
||||
sf->cur_pc = pc;
|
||||
|
||||
ic = put_field_ic_checked(b, ic_idx);
|
||||
if (unlikely(!ic)) {
|
||||
JS_ThrowInternalError(ctx, "invalid IC slot");
|
||||
goto exception;
|
||||
}
|
||||
|
||||
obj = sp[-2];
|
||||
val = sp[-1];
|
||||
|
||||
/* Fast path: object with cached shape */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
JSShape *sh = p->shape;
|
||||
|
||||
int prop_idx = put_field_ic_lookup(ic, sh);
|
||||
if (likely(prop_idx >= 0)) {
|
||||
JSShapeProperty *prs = &get_shape_prop(sh)[prop_idx];
|
||||
/* Check it's writable and a normal data property */
|
||||
if (likely((prs->flags & (JS_PROP_WRITABLE | JS_PROP_TMASK)) ==
|
||||
(JS_PROP_WRITABLE | JS_PROP_NORMAL))) {
|
||||
JSProperty *pr = &p->prop[prop_idx];
|
||||
JSValue old_val = pr->u.value;
|
||||
pr->u.value = val;
|
||||
JS_FreeValue(ctx, old_val);
|
||||
JS_FreeValue(ctx, obj);
|
||||
sp -= 2;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
/* IC miss: do lookup and update cache */
|
||||
{
|
||||
JSProperty *pr;
|
||||
JSShapeProperty *prs = find_own_property(&pr, p, ic->atom);
|
||||
|
||||
if (prs && (prs->flags & (JS_PROP_WRITABLE | JS_PROP_TMASK)) ==
|
||||
(JS_PROP_WRITABLE | JS_PROP_NORMAL)) {
|
||||
uint32_t idx = prs - get_shape_prop(sh);
|
||||
put_field_ic_update(ic, sh, idx);
|
||||
|
||||
JSValue old_val = pr->u.value;
|
||||
pr->u.value = val;
|
||||
JS_FreeValue(ctx, old_val);
|
||||
JS_FreeValue(ctx, obj);
|
||||
sp -= 2;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slow path */
|
||||
ret = JS_SetPropertyInternal(ctx, obj, ic->atom, val, obj,
|
||||
JS_PROP_THROW_STRICT);
|
||||
JS_FreeValue(ctx, obj);
|
||||
sp -= 2;
|
||||
if (unlikely(ret < 0))
|
||||
goto exception;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE(OP_get_field_loc_ic):
|
||||
{
|
||||
JSValue val;
|
||||
uint16_t loc_idx, ic_idx;
|
||||
GetFieldIC *ic;
|
||||
uint32_t operand;
|
||||
|
||||
operand = get_u32(pc);
|
||||
pc += 4;
|
||||
sf->cur_pc = pc;
|
||||
|
||||
loc_idx = operand & 0xFFFF;
|
||||
ic_idx = operand >> 16;
|
||||
|
||||
ic = get_field_ic_checked(b, ic_idx);
|
||||
if (unlikely(!ic)) {
|
||||
JS_ThrowInternalError(ctx, "invalid IC slot");
|
||||
goto exception;
|
||||
}
|
||||
|
||||
JSValue obj = var_buf[loc_idx];
|
||||
|
||||
/* Fast path: object with cached shape */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
JSShape *sh = p->shape;
|
||||
|
||||
int prop_idx = get_field_ic_lookup(ic, sh);
|
||||
if (likely(prop_idx >= 0)) {
|
||||
JSProperty *pr = &p->prop[prop_idx];
|
||||
val = JS_DupValue(ctx, pr->u.value);
|
||||
*sp++ = val;
|
||||
BREAK;
|
||||
}
|
||||
|
||||
/* IC miss: do lookup and update cache */
|
||||
{
|
||||
JSProperty *pr;
|
||||
JSShapeProperty *prs = find_own_property(&pr, p, ic->atom);
|
||||
|
||||
if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_NORMAL) {
|
||||
uint32_t idx = prs - get_shape_prop(sh);
|
||||
get_field_ic_update(ic, sh, idx);
|
||||
|
||||
val = JS_DupValue(ctx, pr->u.value);
|
||||
*sp++ = val;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slow path */
|
||||
val = JS_GetProperty(ctx, obj, ic->atom);
|
||||
if (unlikely(JS_IsException(val)))
|
||||
goto exception;
|
||||
*sp++ = val;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE(OP_get_field_arg_ic):
|
||||
{
|
||||
JSValue val;
|
||||
uint16_t arg_idx, ic_idx;
|
||||
GetFieldIC *ic;
|
||||
uint32_t operand;
|
||||
|
||||
operand = get_u32(pc);
|
||||
pc += 4;
|
||||
sf->cur_pc = pc;
|
||||
|
||||
arg_idx = operand & 0xFFFF;
|
||||
ic_idx = operand >> 16;
|
||||
|
||||
ic = get_field_ic_checked(b, ic_idx);
|
||||
if (unlikely(!ic)) {
|
||||
JS_ThrowInternalError(ctx, "invalid IC slot");
|
||||
goto exception;
|
||||
}
|
||||
|
||||
JSValue obj = arg_buf[arg_idx];
|
||||
|
||||
/* Fast path: object with cached shape */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
JSShape *sh = p->shape;
|
||||
|
||||
int prop_idx = get_field_ic_lookup(ic, sh);
|
||||
if (likely(prop_idx >= 0)) {
|
||||
JSProperty *pr = &p->prop[prop_idx];
|
||||
val = JS_DupValue(ctx, pr->u.value);
|
||||
*sp++ = val;
|
||||
BREAK;
|
||||
}
|
||||
|
||||
/* IC miss: do lookup and update cache */
|
||||
{
|
||||
JSProperty *pr;
|
||||
JSShapeProperty *prs = find_own_property(&pr, p, ic->atom);
|
||||
|
||||
if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_NORMAL) {
|
||||
uint32_t idx = prs - get_shape_prop(sh);
|
||||
get_field_ic_update(ic, sh, idx);
|
||||
|
||||
val = JS_DupValue(ctx, pr->u.value);
|
||||
*sp++ = val;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slow path */
|
||||
val = JS_GetProperty(ctx, obj, ic->atom);
|
||||
if (unlikely(JS_IsException(val)))
|
||||
goto exception;
|
||||
*sp++ = val;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE(OP_put_field_loc_ic):
|
||||
{
|
||||
int ret;
|
||||
uint16_t loc_idx, ic_idx;
|
||||
PutFieldIC *ic;
|
||||
uint32_t operand;
|
||||
|
||||
operand = get_u32(pc);
|
||||
pc += 4;
|
||||
sf->cur_pc = pc;
|
||||
|
||||
loc_idx = operand & 0xFFFF;
|
||||
ic_idx = operand >> 16;
|
||||
|
||||
ic = put_field_ic_checked(b, ic_idx);
|
||||
if (unlikely(!ic)) {
|
||||
JS_ThrowInternalError(ctx, "invalid IC slot");
|
||||
goto exception;
|
||||
}
|
||||
|
||||
JSValue obj = var_buf[loc_idx];
|
||||
JSValue val = sp[-1];
|
||||
|
||||
/* Fast path: object with cached shape */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
JSShape *sh = p->shape;
|
||||
|
||||
int prop_idx = put_field_ic_lookup(ic, sh);
|
||||
if (likely(prop_idx >= 0)) {
|
||||
JSShapeProperty *prs = &get_shape_prop(sh)[prop_idx];
|
||||
if (likely((prs->flags & (JS_PROP_WRITABLE | JS_PROP_TMASK)) ==
|
||||
(JS_PROP_WRITABLE | JS_PROP_NORMAL))) {
|
||||
JSProperty *pr = &p->prop[prop_idx];
|
||||
JSValue old_val = pr->u.value;
|
||||
pr->u.value = val;
|
||||
JS_FreeValue(ctx, old_val);
|
||||
sp--;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
/* IC miss: do lookup and update cache */
|
||||
{
|
||||
JSProperty *pr;
|
||||
JSShapeProperty *prs = find_own_property(&pr, p, ic->atom);
|
||||
|
||||
if (prs && (prs->flags & (JS_PROP_WRITABLE | JS_PROP_TMASK)) ==
|
||||
(JS_PROP_WRITABLE | JS_PROP_NORMAL)) {
|
||||
uint32_t idx = prs - get_shape_prop(sh);
|
||||
put_field_ic_update(ic, sh, idx);
|
||||
|
||||
JSValue old_val = pr->u.value;
|
||||
pr->u.value = val;
|
||||
JS_FreeValue(ctx, old_val);
|
||||
sp--;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slow path */
|
||||
ret = JS_SetPropertyInternal(ctx, obj, ic->atom, val, obj,
|
||||
JS_PROP_THROW_STRICT);
|
||||
sp--;
|
||||
if (unlikely(ret < 0))
|
||||
goto exception;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE(OP_put_field_arg_ic):
|
||||
{
|
||||
int ret;
|
||||
uint16_t arg_idx, ic_idx;
|
||||
PutFieldIC *ic;
|
||||
uint32_t operand;
|
||||
|
||||
operand = get_u32(pc);
|
||||
pc += 4;
|
||||
sf->cur_pc = pc;
|
||||
|
||||
arg_idx = operand & 0xFFFF;
|
||||
ic_idx = operand >> 16;
|
||||
|
||||
ic = put_field_ic_checked(b, ic_idx);
|
||||
if (unlikely(!ic)) {
|
||||
JS_ThrowInternalError(ctx, "invalid IC slot");
|
||||
goto exception;
|
||||
}
|
||||
|
||||
JSValue obj = arg_buf[arg_idx];
|
||||
JSValue val = sp[-1];
|
||||
|
||||
/* Fast path: object with cached shape */
|
||||
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
||||
JSShape *sh = p->shape;
|
||||
|
||||
int prop_idx = put_field_ic_lookup(ic, sh);
|
||||
if (likely(prop_idx >= 0)) {
|
||||
JSShapeProperty *prs = &get_shape_prop(sh)[prop_idx];
|
||||
if (likely((prs->flags & (JS_PROP_WRITABLE | JS_PROP_TMASK)) ==
|
||||
(JS_PROP_WRITABLE | JS_PROP_NORMAL))) {
|
||||
JSProperty *pr = &p->prop[prop_idx];
|
||||
JSValue old_val = pr->u.value;
|
||||
pr->u.value = val;
|
||||
JS_FreeValue(ctx, old_val);
|
||||
sp--;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
/* IC miss: do lookup and update cache */
|
||||
{
|
||||
JSProperty *pr;
|
||||
JSShapeProperty *prs = find_own_property(&pr, p, ic->atom);
|
||||
|
||||
if (prs && (prs->flags & (JS_PROP_WRITABLE | JS_PROP_TMASK)) ==
|
||||
(JS_PROP_WRITABLE | JS_PROP_NORMAL)) {
|
||||
uint32_t idx = prs - get_shape_prop(sh);
|
||||
put_field_ic_update(ic, sh, idx);
|
||||
|
||||
JSValue old_val = pr->u.value;
|
||||
pr->u.value = val;
|
||||
JS_FreeValue(ctx, old_val);
|
||||
sp--;
|
||||
BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slow path */
|
||||
ret = JS_SetPropertyInternal(ctx, obj, ic->atom, val, obj,
|
||||
JS_PROP_THROW_STRICT);
|
||||
sp--;
|
||||
if (unlikely(ret < 0))
|
||||
goto exception;
|
||||
}
|
||||
BREAK;
|
||||
|
||||
CASE(OP_define_field):
|
||||
{
|
||||
int ret;
|
||||
@@ -26156,6 +27047,20 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
|
||||
js_free_rt(rt, b->ic_slots);
|
||||
}
|
||||
|
||||
/* Free per-site IC slots */
|
||||
if (b->per_site_ic) {
|
||||
/* Free atoms owned by per-site IC slots */
|
||||
for (i = 0; i < b->per_site_ic_count; i++) {
|
||||
PerSiteIC *slot = &b->per_site_ic[i];
|
||||
if (slot->kind == IC_GET_PROP) {
|
||||
JS_FreeAtomRT(rt, slot->u.get_field.atom);
|
||||
} else if (slot->kind == IC_SET_PROP) {
|
||||
JS_FreeAtomRT(rt, slot->u.put_field.atom);
|
||||
}
|
||||
}
|
||||
js_free_rt(rt, b->per_site_ic);
|
||||
}
|
||||
|
||||
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