mirror of
https://github.com/DoneJS-Runtime/quickjs-done-nextgen.git
synced 2025-01-09 17:43:15 +00:00
Don't serialize IC opcodes (#334)
Translate IC opcodes to their non-IC variants before writing them out. Before this commit they were not byte-swapped properly, breaking the ability to load serialized bytecode containing ICs on systems with different endianness. Inline caches are recomputed as needed now. A pleasing side effect of this change is that serialized bytecode is, on average, a little smaller because fewer atoms are duplicated now.
This commit is contained in:
parent
f02ed184a2
commit
c7ca3febd3
7 changed files with 55 additions and 71 deletions
Binary file not shown.
BIN
gen/hello.c
BIN
gen/hello.c
Binary file not shown.
Binary file not shown.
BIN
gen/repl.c
BIN
gen/repl.c
Binary file not shown.
BIN
gen/test_fib.c
BIN
gen/test_fib.c
Binary file not shown.
|
@ -136,9 +136,12 @@ DEF( put_ref_value, 1, 3, 0, none)
|
||||||
DEF( define_var, 6, 0, 0, atom_u8)
|
DEF( define_var, 6, 0, 0, atom_u8)
|
||||||
DEF(check_define_var, 6, 0, 0, atom_u8)
|
DEF(check_define_var, 6, 0, 0, atom_u8)
|
||||||
DEF( define_func, 6, 1, 0, atom_u8)
|
DEF( define_func, 6, 1, 0, atom_u8)
|
||||||
|
|
||||||
|
// order matters, see IC counterparts
|
||||||
DEF( get_field, 5, 1, 1, atom)
|
DEF( get_field, 5, 1, 1, atom)
|
||||||
DEF( get_field2, 5, 1, 2, atom)
|
DEF( get_field2, 5, 1, 2, atom)
|
||||||
DEF( put_field, 5, 2, 0, atom)
|
DEF( put_field, 5, 2, 0, atom)
|
||||||
|
|
||||||
DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
|
DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
|
||||||
DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
|
DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
|
||||||
DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
|
DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
|
||||||
|
@ -358,6 +361,7 @@ DEF( is_null, 1, 1, 1, none)
|
||||||
DEF(typeof_is_undefined, 1, 1, 1, none)
|
DEF(typeof_is_undefined, 1, 1, 1, none)
|
||||||
DEF( typeof_is_function, 1, 1, 1, none)
|
DEF( typeof_is_function, 1, 1, 1, none)
|
||||||
|
|
||||||
|
// order matters, see non-IC counterparts
|
||||||
DEF( get_field_ic, 5, 1, 1, none)
|
DEF( get_field_ic, 5, 1, 1, none)
|
||||||
DEF( get_field2_ic, 5, 1, 2, none)
|
DEF( get_field2_ic, 5, 1, 2, none)
|
||||||
DEF( put_field_ic, 5, 2, 0, none)
|
DEF( put_field_ic, 5, 2, 0, none)
|
||||||
|
|
122
quickjs.c
122
quickjs.c
|
@ -593,7 +593,6 @@ static int resize_ic_hash(JSContext *ctx, JSInlineCache *ic);
|
||||||
static int free_ic(JSRuntime *rt, JSInlineCache *ic);
|
static int free_ic(JSRuntime *rt, JSInlineCache *ic);
|
||||||
static uint32_t add_ic_slot(JSContext *ctx, JSInlineCache *ic, JSAtom atom, JSObject *object,
|
static uint32_t add_ic_slot(JSContext *ctx, JSInlineCache *ic, JSAtom atom, JSObject *object,
|
||||||
uint32_t prop_offset);
|
uint32_t prop_offset);
|
||||||
static uint32_t add_ic_slot1(JSContext *ctx, JSInlineCache *ic, JSAtom atom);
|
|
||||||
|
|
||||||
static int32_t get_ic_prop_offset(JSInlineCache *ic, uint32_t cache_offset,
|
static int32_t get_ic_prop_offset(JSInlineCache *ic, uint32_t cache_offset,
|
||||||
JSShape *shape)
|
JSShape *shape)
|
||||||
|
@ -19995,8 +19994,34 @@ static void emit_atom(JSParseState *s, JSAtom name)
|
||||||
emit_u32(s, JS_DupAtom(s->ctx, name));
|
emit_u32(s, JS_DupAtom(s->ctx, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void emit_ic(JSParseState *s, JSAtom atom) {
|
static force_inline uint32_t get_index_hash(JSAtom atom, int hash_bits)
|
||||||
add_ic_slot1(s->ctx, s->cur_func->ic, atom);
|
{
|
||||||
|
return (atom * 0x9e370001) >> (32 - hash_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void emit_ic(JSParseState *s, JSAtom atom)
|
||||||
|
{
|
||||||
|
uint32_t h;
|
||||||
|
JSContext *ctx;
|
||||||
|
JSInlineCache *ic;
|
||||||
|
JSInlineCacheHashSlot *ch;
|
||||||
|
|
||||||
|
ic = s->cur_func->ic;
|
||||||
|
ctx = s->ctx;
|
||||||
|
if (ic->count + 1 >= ic->capacity && resize_ic_hash(ctx, ic))
|
||||||
|
return;
|
||||||
|
h = get_index_hash(atom, ic->hash_bits);
|
||||||
|
for (ch = ic->hash[h]; ch != NULL; ch = ch->next)
|
||||||
|
if (ch->atom == atom)
|
||||||
|
return;
|
||||||
|
ch = js_malloc(ctx, sizeof(*ch));
|
||||||
|
if (unlikely(!ch))
|
||||||
|
return;
|
||||||
|
ch->atom = JS_DupAtom(ctx, atom);
|
||||||
|
ch->index = 0;
|
||||||
|
ch->next = ic->hash[h];
|
||||||
|
ic->hash[h] = ch;
|
||||||
|
ic->count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int update_label(JSFunctionDef *s, int label, int delta)
|
static int update_label(JSFunctionDef *s, int label, int delta)
|
||||||
|
@ -32518,7 +32543,7 @@ typedef enum BCTagEnum {
|
||||||
BC_TAG_OBJECT_REFERENCE,
|
BC_TAG_OBJECT_REFERENCE,
|
||||||
} BCTagEnum;
|
} BCTagEnum;
|
||||||
|
|
||||||
#define BC_VERSION 8
|
#define BC_VERSION 9
|
||||||
|
|
||||||
typedef struct BCWriterState {
|
typedef struct BCWriterState {
|
||||||
JSContext *ctx;
|
JSContext *ctx;
|
||||||
|
@ -32722,18 +32747,24 @@ static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int JS_WriteFunctionBytecode(BCWriterState *s,
|
static BOOL is_ic_op(uint8_t op)
|
||||||
const uint8_t *bc_buf1, int bc_len)
|
|
||||||
{
|
{
|
||||||
int pos, len, op;
|
return op >= OP_get_field_ic && op <= OP_put_field_ic;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int JS_WriteFunctionBytecode(BCWriterState *s,
|
||||||
|
const JSFunctionBytecode *b)
|
||||||
|
{
|
||||||
|
int pos, len, bc_len, op;
|
||||||
JSAtom atom;
|
JSAtom atom;
|
||||||
uint8_t *bc_buf;
|
uint8_t *bc_buf;
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
|
|
||||||
|
bc_len = b->byte_code_len;
|
||||||
bc_buf = js_malloc(s->ctx, bc_len);
|
bc_buf = js_malloc(s->ctx, bc_len);
|
||||||
if (!bc_buf)
|
if (!bc_buf)
|
||||||
return -1;
|
return -1;
|
||||||
memcpy(bc_buf, bc_buf1, bc_len);
|
memcpy(bc_buf, b->byte_code_buf, bc_len);
|
||||||
|
|
||||||
pos = 0;
|
pos = 0;
|
||||||
while (pos < bc_len) {
|
while (pos < bc_len) {
|
||||||
|
@ -32751,6 +32782,16 @@ static int JS_WriteFunctionBytecode(BCWriterState *s,
|
||||||
put_u32(bc_buf + pos + 1, val);
|
put_u32(bc_buf + pos + 1, val);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
// IC (inline cache) opcodes should not end up in the serialized
|
||||||
|
// bytecode; translate them to their non-IC counterparts here
|
||||||
|
if (is_ic_op(op)) {
|
||||||
|
val = get_u32(bc_buf + pos + 1);
|
||||||
|
atom = get_ic_atom(b->ic, val);
|
||||||
|
if (bc_atom_to_idx(s, &val, atom))
|
||||||
|
goto fail;
|
||||||
|
put_u32(bc_buf + pos + 1, val);
|
||||||
|
bc_buf[pos] -= (OP_get_field_ic - OP_get_field);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pos += len;
|
pos += len;
|
||||||
|
@ -32918,7 +32959,7 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValue obj)
|
||||||
bc_put_u8(s, flags);
|
bc_put_u8(s, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
|
if (JS_WriteFunctionBytecode(s, b))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
bc_put_atom(s, b->filename);
|
bc_put_atom(s, b->filename);
|
||||||
|
@ -32929,19 +32970,6 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValue obj)
|
||||||
bc_put_leb128(s, b->source_len);
|
bc_put_leb128(s, b->source_len);
|
||||||
dbuf_put(&s->dbuf, b->source, b->source_len);
|
dbuf_put(&s->dbuf, b->source, b->source_len);
|
||||||
|
|
||||||
/* compatibility */
|
|
||||||
dbuf_putc(&s->dbuf, 255);
|
|
||||||
dbuf_putc(&s->dbuf, 73); // 'I'
|
|
||||||
dbuf_putc(&s->dbuf, 67); // 'C'
|
|
||||||
if (b->ic == NULL) {
|
|
||||||
bc_put_leb128(s, 0);
|
|
||||||
} else {
|
|
||||||
bc_put_leb128(s, b->ic->count);
|
|
||||||
for (i = 0; i < b->ic->count; i++) {
|
|
||||||
bc_put_atom(s, b->ic->cache[i].atom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0; i < b->cpool_count; i++) {
|
for(i = 0; i < b->cpool_count; i++) {
|
||||||
if (JS_WriteObjectRec(s, b->cpool[i]))
|
if (JS_WriteObjectRec(s, b->cpool[i]))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -33643,6 +33671,7 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
assert(!is_ic_op(op)); // should not end up in serialized bytecode
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pos += len;
|
pos += len;
|
||||||
|
@ -33919,28 +33948,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
|
||||||
if (bc_get_buf(s, b->source, b->source_len))
|
if (bc_get_buf(s, b->source, b->source_len))
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (s->buf_end - s->ptr > 3 && s->ptr[0] == 255 &&
|
|
||||||
s->ptr[1] == 73 && s->ptr[2] == 67) {
|
|
||||||
s->ptr += 3;
|
|
||||||
bc_get_leb128(s, &ic_len);
|
|
||||||
if (ic_len == 0) {
|
|
||||||
b->ic = NULL;
|
|
||||||
} else {
|
|
||||||
b->ic = init_ic(ctx);
|
|
||||||
if (b->ic == NULL)
|
|
||||||
goto fail;
|
|
||||||
for (i = 0; i < ic_len; i++) {
|
|
||||||
bc_get_atom(s, &atom);
|
|
||||||
add_ic_slot1(ctx, b->ic, atom);
|
|
||||||
JS_FreeAtom(ctx, atom);
|
|
||||||
}
|
|
||||||
rebuild_ic(ctx, b->ic);
|
|
||||||
}
|
|
||||||
#ifdef DUMP_READ_OBJECT
|
|
||||||
bc_read_trace(s, "filename: "); print_atom(s->ctx, b->filename); printf("\n");
|
|
||||||
#endif
|
|
||||||
bc_read_trace(s, "}\n");
|
|
||||||
}
|
|
||||||
if (b->cpool_count != 0) {
|
if (b->cpool_count != 0) {
|
||||||
bc_read_trace(s, "cpool {\n");
|
bc_read_trace(s, "cpool {\n");
|
||||||
for(i = 0; i < b->cpool_count; i++) {
|
for(i = 0; i < b->cpool_count; i++) {
|
||||||
|
@ -52120,11 +52127,6 @@ static void insert_weakref_record(JSValue target, struct JSWeakRefRecord *wr)
|
||||||
|
|
||||||
/* Poly IC */
|
/* Poly IC */
|
||||||
|
|
||||||
static force_inline uint32_t get_index_hash(JSAtom atom, int hash_bits)
|
|
||||||
{
|
|
||||||
return (atom * 0x9e370001) >> (32 - hash_bits);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSInlineCache *init_ic(JSContext *ctx)
|
JSInlineCache *init_ic(JSContext *ctx)
|
||||||
{
|
{
|
||||||
JSInlineCache *ic;
|
JSInlineCache *ic;
|
||||||
|
@ -52258,28 +52260,6 @@ end:
|
||||||
return ch->index;
|
return ch->index;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t add_ic_slot1(JSContext *ctx, JSInlineCache *ic, JSAtom atom)
|
|
||||||
{
|
|
||||||
uint32_t h;
|
|
||||||
JSInlineCacheHashSlot *ch;
|
|
||||||
if (ic->count + 1 >= ic->capacity && resize_ic_hash(ctx, ic))
|
|
||||||
goto end;
|
|
||||||
h = get_index_hash(atom, ic->hash_bits);
|
|
||||||
for (ch = ic->hash[h]; ch != NULL; ch = ch->next)
|
|
||||||
if (ch->atom == atom)
|
|
||||||
goto end;
|
|
||||||
ch = js_malloc(ctx, sizeof(*ch));
|
|
||||||
if (unlikely(!ch))
|
|
||||||
goto end;
|
|
||||||
ch->atom = JS_DupAtom(ctx, atom);
|
|
||||||
ch->index = 0;
|
|
||||||
ch->next = ic->hash[h];
|
|
||||||
ic->hash[h] = ch;
|
|
||||||
ic->count += 1;
|
|
||||||
end:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CallSite */
|
/* CallSite */
|
||||||
|
|
||||||
static void js_callsite_finalizer(JSRuntime *rt, JSValue val)
|
static void js_callsite_finalizer(JSRuntime *rt, JSValue val)
|
||||||
|
|
Loading…
Reference in a new issue