Skip to content

Commit a39a131

Browse files
committed
feat: add get property basic polyic
1 parent 6e9494b commit a39a131

16 files changed

+450
-14
lines changed

include/quickjs/quickjs-opcode.h

+4
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ DEF(typeof_is_undefined, 1, 1, 1, none)
361361
DEF( typeof_is_function, 1, 1, 1, none)
362362
#endif
363363

364+
DEF( get_field_ic, 5, 1, 1, atom)
365+
DEF( get_field2_ic, 5, 1, 2, atom)
366+
DEF( put_field_ic, 5, 2, 0, atom)
367+
364368
#undef DEF
365369
#undef def
366370
#endif /* DEF */

include/quickjs/quickjs.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -715,9 +715,11 @@ JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val)
715715
JSValue JS_NewArray(JSContext *ctx);
716716
int JS_IsArray(JSContext *ctx, JSValueConst val);
717717

718-
JSValue JS_GetPropertyInternal(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValueConst receiver, JS_BOOL throw_ref_error);
718+
typedef struct InlineCache InlineCache;
719+
JSValue JS_GetPropertyInternal(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValueConst receiver, InlineCache *ic, JS_BOOL throw_ref_error);
720+
JSValue JS_GetPropertyInternalWithIC(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValueConst receiver, InlineCache *ic, int32_t offset, JS_BOOL throw_ref_error);
719721
static js_force_inline JSValue JS_GetProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop) {
720-
return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0);
722+
return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, NULL, 0);
721723
}
722724
JSValue JS_GetPropertyStr(JSContext* ctx, JSValueConst this_obj, const char* prop);
723725
JSValue JS_GetPropertyUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx);

src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_library(quickjs
1616
core/convertion.c
1717
core/runtime.c
1818
core/module.c
19+
core/ic.c
1920
core/builtins/js-array.c
2021
core/builtins/js-async-function.c
2122
core/builtins/js-async-generator.c

src/core/builtins/js-proxy.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
286286
return JS_EXCEPTION;
287287
/* Note: recursion is possible thru the prototype of s->target */
288288
if (JS_IsUndefined(method))
289-
return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);
289+
return JS_GetPropertyInternal(ctx, s->target, atom, receiver, NULL, FALSE);
290290
atom_val = JS_AtomToValue(ctx, atom);
291291
if (JS_IsException(atom_val)) {
292292
JS_FreeValue(ctx, method);

src/core/builtins/js-reflect.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,
9999
atom = JS_ValueToAtom(ctx, prop);
100100
if (unlikely(atom == JS_ATOM_NULL))
101101
return JS_EXCEPTION;
102-
ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);
102+
ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, NULL, FALSE);
103103
JS_FreeAtom(ctx, atom);
104104
return ret;
105105
}

src/core/bytecode.c

+5
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ void free_function_bytecode(JSRuntime* rt, JSFunctionBytecode* b) {
7373
js_free_rt(rt, b->debug.source);
7474
}
7575

76+
if (b->get_ic != NULL)
77+
free_ic(b->get_ic);
78+
if (b->set_ic != NULL)
79+
free_ic(b->set_ic);
80+
7681
remove_gc_object(&b->header);
7782
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {
7883
list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);

src/core/function.c

+55-4
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,12 @@ JSValue JS_CallInternal(JSContext* caller_ctx,
220220
JSObject* p;
221221
JSFunctionBytecode* b;
222222
JSStackFrame sf_s, *sf = &sf_s;
223-
const uint8_t* pc;
223+
uint8_t* pc;
224224
int opcode, arg_allocated_size, i;
225225
JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
226226
JSVarRef** var_refs;
227227
size_t alloca_size;
228+
InlineCache *get_ic, *set_ic;
228229

229230
#if !DIRECT_DISPATCH
230231
#define SWITCH(pc) switch (opcode = *pc++)
@@ -328,6 +329,8 @@ JSValue JS_CallInternal(JSContext* caller_ctx,
328329
sf->prev_frame = rt->current_stack_frame;
329330
rt->current_stack_frame = sf;
330331
ctx = b->realm; /* set the current realm */
332+
get_ic = b->get_ic;
333+
set_ic = b->set_ic;
331334

332335
restart:
333336
for (;;) {
@@ -1479,7 +1482,29 @@ JSValue JS_CallInternal(JSContext* caller_ctx,
14791482
atom = get_u32(pc);
14801483
pc += 4;
14811484

1482-
val = JS_GetProperty(ctx, sp[-1], atom);
1485+
get_ic->updated = FALSE;
1486+
val = JS_GetPropertyInternal(ctx, sp[-1], atom, sp[-1], get_ic, FALSE);
1487+
if (unlikely(JS_IsException(val)))
1488+
goto exception;
1489+
if (get_ic->updated == TRUE) {
1490+
get_ic->updated = FALSE;
1491+
put_u8(pc - 5, OP_get_field_ic);
1492+
put_u32(pc - 4, get_ic->updated_offset);
1493+
}
1494+
JS_FreeValue(ctx, sp[-1]);
1495+
sp[-1] = val;
1496+
}
1497+
BREAK;
1498+
1499+
CASE(OP_get_field_ic): {
1500+
JSValue val;
1501+
JSAtom atom;
1502+
int32_t ic_offset;
1503+
ic_offset = get_u32(pc);
1504+
atom = get_ic_atom(get_ic, ic_offset);
1505+
pc += 4;
1506+
1507+
val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], get_ic, ic_offset, FALSE);
14831508
if (unlikely(JS_IsException(val)))
14841509
goto exception;
14851510
JS_FreeValue(ctx, sp[-1]);
@@ -1493,7 +1518,28 @@ JSValue JS_CallInternal(JSContext* caller_ctx,
14931518
atom = get_u32(pc);
14941519
pc += 4;
14951520

1496-
val = JS_GetProperty(ctx, sp[-1], atom);
1521+
get_ic->updated = FALSE;
1522+
val = JS_GetPropertyInternal(ctx, sp[-1], atom, sp[-1], get_ic, FALSE);
1523+
if (unlikely(JS_IsException(val)))
1524+
goto exception;
1525+
if (get_ic->updated == TRUE) {
1526+
get_ic->updated = FALSE;
1527+
put_u8(pc - 5, OP_get_field2_ic);
1528+
put_u32(pc - 4, get_ic->updated_offset);
1529+
}
1530+
*sp++ = val;
1531+
}
1532+
BREAK;
1533+
1534+
CASE(OP_get_field2_ic): {
1535+
JSValue val;
1536+
JSAtom atom;
1537+
int32_t ic_offset;
1538+
ic_offset = get_u32(pc);
1539+
atom = get_ic_atom(get_ic, ic_offset);
1540+
pc += 4;
1541+
1542+
val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], get_ic, ic_offset, FALSE);
14971543
if (unlikely(JS_IsException(val)))
14981544
goto exception;
14991545
*sp++ = val;
@@ -1514,6 +1560,11 @@ JSValue JS_CallInternal(JSContext* caller_ctx,
15141560
}
15151561
BREAK;
15161562

1563+
CASE(OP_put_field_ic): {
1564+
1565+
}
1566+
BREAK;
1567+
15171568
CASE(OP_private_symbol) : {
15181569
JSAtom atom;
15191570
JSValue val;
@@ -1715,7 +1766,7 @@ JSValue JS_CallInternal(JSContext* caller_ctx,
17151766
atom = JS_ValueToAtom(ctx, sp[-1]);
17161767
if (unlikely(atom == JS_ATOM_NULL))
17171768
goto exception;
1718-
val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);
1769+
val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], NULL, FALSE);
17191770
JS_FreeAtom(ctx, atom);
17201771
if (unlikely(JS_IsException(val)))
17211772
goto exception;

src/core/gc.c

+18-1
Original file line numberDiff line numberDiff line change
@@ -634,13 +634,30 @@ void mark_children(JSRuntime* rt, JSGCObjectHeader* gp, JS_MarkFunc* mark_func)
634634
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
635635
/* the template objects can be part of a cycle */
636636
{
637+
int i, j;
638+
InlineCacheRingItem *buffer;
637639
JSFunctionBytecode* b = (JSFunctionBytecode*)gp;
638-
int i;
639640
for (i = 0; i < b->cpool_count; i++) {
640641
JS_MarkValue(rt, b->cpool[i], mark_func);
641642
}
642643
if (b->realm)
643644
mark_func(rt, &b->realm->header);
645+
if (b->get_ic) {
646+
for (i = 0; i < b->get_ic->count; i++) {
647+
buffer = b->get_ic->cache[i].buffer;
648+
for (j = 0; j < IC_CACHE_ITEM_CAPACITY; j++)
649+
if (buffer[j].shape)
650+
mark_func(rt, &buffer[j].shape->header);
651+
}
652+
}
653+
if (b->set_ic) {
654+
for (i = 0; i < b->set_ic->count; i++) {
655+
buffer = b->set_ic->cache[i].buffer;
656+
for (j = 0; j < IC_CACHE_ITEM_CAPACITY; j++)
657+
if (buffer[j].shape)
658+
mark_func(rt, &buffer[j].shape->header);
659+
}
660+
}
644661
}
645662
break;
646663
case JS_GC_OBJ_TYPE_VAR_REF: {

src/core/ic.c

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* QuickJS Javascript Engine
3+
*
4+
* Copyright (c) 2017-2021 Fabrice Bellard
5+
* Copyright (c) 2017-2021 Charlie Gordon
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20+
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
26+
#include "ic.h"
27+
28+
uint32_t get_index_hash(JSAtom atom, int hash_bits) {
29+
return (atom * 0x9e370001) >> (32 - hash_bits);
30+
}
31+
32+
InlineCache *init_ic(JSRuntime *rt) {
33+
InlineCache *ic;
34+
ic = js_malloc_rt(rt, sizeof(InlineCache));
35+
if (unlikely(!ic))
36+
goto fail;
37+
ic->count = 0;
38+
ic->hash_bits = 4;
39+
ic->capacity = 1 << ic->hash_bits;
40+
ic->rt = rt;
41+
ic->hash = js_malloc_rt(rt, sizeof(ic->hash[0]) * ic->capacity);
42+
if (unlikely(!ic->hash))
43+
goto fail;
44+
memset(ic->hash, 0, sizeof(ic->hash[0]) * ic->capacity);
45+
ic->cache = NULL;
46+
ic->updated = FALSE;
47+
ic->updated_offset = 0;
48+
return ic;
49+
fail:
50+
return NULL;
51+
}
52+
53+
int rebuild_ic(InlineCache *ic) {
54+
uint32_t i, count;
55+
InlineCacheHashSlot *ch;
56+
if (ic->count == 0)
57+
goto end;
58+
count = 0;
59+
ic->cache = js_malloc_rt(ic->rt, sizeof(InlineCacheRingSlot) * ic->count);
60+
if (unlikely(!ic->cache))
61+
goto fail;
62+
memset(ic->cache, 0, sizeof(InlineCacheRingSlot) * ic->count);
63+
for (i = 0; i < ic->capacity; i++) {
64+
for (ch = ic->hash[i]; ch != NULL; ch = ch->next) {
65+
ch->index = count++;
66+
ic->cache[ch->index].atom = ch->atom;
67+
ic->cache[ch->index].index = 0;
68+
}
69+
}
70+
end:
71+
return 0;
72+
fail:
73+
return -1;
74+
}
75+
76+
int resize_ic_hash(InlineCache *ic) {
77+
uint32_t new_capacity, i, h;
78+
InlineCacheHashSlot *ch, *ch_next;
79+
InlineCacheHashSlot **new_hash;
80+
ic->hash_bits += 1;
81+
new_capacity = 1 << ic->hash_bits;
82+
new_hash = js_malloc_rt(ic->rt, sizeof(ic->hash[0]) * new_capacity);
83+
if (unlikely(!new_hash))
84+
goto fail;
85+
memset(new_hash, 0, sizeof(ic->hash[0]) * new_capacity);
86+
for (i = 0; i < ic->capacity; i++) {
87+
for (ch = ic->hash[i]; ch != NULL; ch = ch_next) {
88+
h = get_index_hash(ch->atom, ic->hash_bits);
89+
ch_next = ch->next;
90+
ch->next = new_hash[h];
91+
new_hash[h] = ch;
92+
}
93+
}
94+
js_free_rt(ic->rt, ic->hash);
95+
ic->hash = new_hash;
96+
ic->capacity = new_capacity;
97+
return 0;
98+
fail:
99+
return -1;
100+
}
101+
102+
int free_ic(InlineCache *ic) {
103+
uint32_t i, j;
104+
InlineCacheHashSlot *ch, *ch_next;
105+
InlineCacheRingItem *buffer;
106+
for (i = 0; i < ic->count; i++) {
107+
buffer = ic->cache[i].buffer;
108+
for (j = 0; j < IC_CACHE_ITEM_CAPACITY; j++) {
109+
js_free_shape_null(ic->rt, buffer[j].shape);
110+
}
111+
}
112+
for (i = 0; i < ic->capacity; i++) {
113+
for (ch = ic->hash[i]; ch != NULL; ch = ch_next) {
114+
ch_next = ch->next;
115+
js_free_rt(ic->rt, ch);
116+
}
117+
}
118+
if (ic->count > 0)
119+
js_free_rt(ic->rt, ic->cache);
120+
js_free_rt(ic->rt, ic->hash);
121+
js_free_rt(ic->rt, ic);
122+
return 0;
123+
}
124+
125+
uint32_t add_ic_slot(InlineCache *ic, JSAtom atom, JSObject *object,
126+
uint32_t prop_offset) {
127+
int32_t i;
128+
uint32_t h;
129+
InlineCacheHashSlot *ch;
130+
InlineCacheRingSlot *cr;
131+
JSShape* sh;
132+
cr = NULL;
133+
h = get_index_hash(atom, ic->hash_bits);
134+
for (ch = ic->hash[h]; ch != NULL; ch = ch->next)
135+
if (ch->atom == atom) {
136+
cr = ic->cache + ch->index;
137+
break;
138+
}
139+
140+
assert(cr != NULL);
141+
i = cr->index;
142+
for (;;) {
143+
if (object->shape == cr->buffer[i].shape) {
144+
cr->buffer[i].prop_offset = prop_offset;
145+
goto end;
146+
}
147+
148+
i = (i + 1) % IC_CACHE_ITEM_CAPACITY;
149+
if (i == cr->index)
150+
break;
151+
}
152+
153+
sh = cr->buffer[i].shape;
154+
cr->buffer[i].shape = js_dup_shape(object->shape);
155+
js_free_shape_null(ic->rt, sh);
156+
cr->buffer[i].prop_offset = prop_offset;
157+
end:
158+
return ch->index;
159+
}
160+
161+
uint32_t add_ic_slot1(InlineCache *ic, JSAtom atom) {
162+
uint32_t h;
163+
InlineCacheHashSlot *ch;
164+
if (ic->count + 1 >= ic->capacity && resize_ic_hash(ic))
165+
goto end;
166+
h = get_index_hash(atom, ic->hash_bits);
167+
for (ch = ic->hash[h]; ch != NULL; ch = ch->next)
168+
if (ch->atom == atom)
169+
goto end;
170+
ch = js_malloc_rt(ic->rt, sizeof(InlineCacheHashSlot));
171+
if (unlikely(!ch))
172+
goto end;
173+
ch->atom = atom;
174+
ch->index = 0;
175+
ch->next = ic->hash[h];
176+
ic->hash[h] = ch;
177+
ic->count += 1;
178+
end:
179+
return 0;
180+
}

0 commit comments

Comments
 (0)