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:
Ben Noordhuis 2024-03-27 12:07:11 +01:00 committed by GitHub
parent f02ed184a2
commit c7ca3febd3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 10128 additions and 10222 deletions

View file

@ -2,10 +2,10 @@
#include "quickjs-libc.h"
const uint32_t qjsc_function_source_size = 393;
const uint32_t qjsc_function_source_size = 384;
const uint8_t qjsc_function_source[393] = {
0x08, 0x06, 0x0c, 0x61, 0x63, 0x74, 0x75, 0x61,
const uint8_t qjsc_function_source[384] = {
0x09, 0x06, 0x0c, 0x61, 0x63, 0x74, 0x75, 0x61,
0x6c, 0x02, 0x66, 0x0c, 0x65, 0x78, 0x70, 0x65,
0x63, 0x74, 0x14, 0x75, 0x73, 0x65, 0x20, 0x73,
0x74, 0x72, 0x69, 0x63, 0x74, 0x34, 0x66, 0x75,
@ -46,15 +46,13 @@ const uint8_t qjsc_function_source[393] = {
0x0c, 0x06, 0x11, 0x18, 0x2a, 0x1c, 0x37, 0x41,
0x21, 0x1c, 0x34, 0x18, 0x1b, 0x04, 0x26, 0x11,
0x3f, 0x1d, 0x0c, 0x06, 0x11, 0x18, 0x2a, 0x1c,
0x53, 0x41, 0x00, 0xff, 0x49, 0x43, 0x01, 0x6c,
0x0c, 0x43, 0x02, 0x01, 0xae, 0x03, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0xbb, 0x2a,
0x28, 0xb6, 0x03, 0x03, 0x01, 0x04, 0x02, 0x1e,
0x0c, 0x0e, 0x1a, 0x66, 0x75, 0x6e, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x20, 0x66, 0x28, 0x29, 0x20,
0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
0x20, 0x34, 0x32, 0x20, 0x7d, 0xff, 0x49, 0x43,
0x00,
0x53, 0x41, 0x00, 0x0c, 0x43, 0x02, 0x01, 0xae,
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03,
0x00, 0xbb, 0x2a, 0x28, 0xb6, 0x03, 0x03, 0x01,
0x04, 0x02, 0x1e, 0x0c, 0x0e, 0x1a, 0x66, 0x75,
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66,
0x28, 0x29, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x74,
0x75, 0x72, 0x6e, 0x20, 0x34, 0x32, 0x20, 0x7d,
};
static JSContext *JS_NewCustomContext(JSRuntime *rt)

View file

@ -2,10 +2,10 @@
#include "quickjs-libc.h"
const uint32_t qjsc_hello_size = 95;
const uint32_t qjsc_hello_size = 89;
const uint8_t qjsc_hello[95] = {
0x08, 0x04, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f,
const uint8_t qjsc_hello[89] = {
0x09, 0x04, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f,
0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x67, 0x16, 0x48,
0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72,
0x6c, 0x64, 0x22, 0x65, 0x78, 0x61, 0x6d, 0x70,
@ -16,7 +16,7 @@ const uint8_t qjsc_hello[95] = {
0xd6, 0x00, 0x00, 0x00, 0x42, 0xd7, 0x00, 0x00,
0x00, 0x04, 0xd8, 0x00, 0x00, 0x00, 0x24, 0x01,
0x00, 0xcc, 0x28, 0xb2, 0x03, 0x01, 0x01, 0x00,
0x00, 0xff, 0x49, 0x43, 0x01, 0xae, 0x03,
0x00,
};
static JSContext *JS_NewCustomContext(JSRuntime *rt)

View file

@ -2,10 +2,10 @@
#include "quickjs-libc.h"
const uint32_t qjsc_fib_module_size = 318;
const uint32_t qjsc_fib_module_size = 310;
const uint8_t qjsc_fib_module[318] = {
0x08, 0x03, 0x2c, 0x65, 0x78, 0x61, 0x6d, 0x70,
const uint8_t qjsc_fib_module[310] = {
0x09, 0x03, 0x2c, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x73, 0x2f, 0x66, 0x69, 0x62, 0x5f,
0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x6a,
0x73, 0x06, 0x66, 0x69, 0x62, 0x02, 0x6e, 0x0d,
@ -15,42 +15,41 @@ const uint8_t qjsc_fib_module[318] = {
0xae, 0x03, 0x00, 0x01, 0x08, 0xe9, 0x05, 0xbe,
0x00, 0xe0, 0x29, 0x06, 0x2e, 0xac, 0x03, 0x01,
0x01, 0x06, 0x01, 0x01, 0x00, 0x07, 0x14, 0x02,
0x00, 0xff, 0x49, 0x43, 0x00, 0x0c, 0x43, 0x02,
0x01, 0xae, 0x03, 0x01, 0x00, 0x01, 0x04, 0x01,
0x00, 0x1a, 0x01, 0xb0, 0x03, 0x00, 0x01, 0x00,
0xae, 0x03, 0x00, 0x00, 0xd0, 0xb3, 0xa7, 0xe9,
0x03, 0xb3, 0x28, 0xd0, 0xb4, 0xac, 0xe9, 0x03,
0xb4, 0x28, 0xdc, 0xd0, 0xb4, 0x9e, 0xee, 0xdc,
0xd0, 0xb5, 0x9e, 0xee, 0x9d, 0x28, 0xac, 0x03,
0x02, 0x08, 0x20, 0x04, 0x00, 0x07, 0x06, 0x07,
0x06, 0x12, 0x09, 0x08, 0x07, 0x07, 0x10, 0x07,
0x06, 0x07, 0x06, 0x12, 0x13, 0x08, 0x07, 0x08,
0x16, 0x0c, 0x0c, 0x07, 0x04, 0x0c, 0x0a, 0x0c,
0x0c, 0x07, 0x04, 0x8d, 0x01, 0x66, 0x75, 0x6e,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69,
0x62, 0x28, 0x6e, 0x29, 0x0a, 0x7b, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e,
0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72,
0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73,
0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x20,
0x3d, 0x3d, 0x20, 0x31, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x3b, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
0x66, 0x69, 0x62, 0x28, 0x6e, 0x20, 0x2d, 0x20,
0x31, 0x29, 0x20, 0x2b, 0x20, 0x66, 0x69, 0x62,
0x28, 0x6e, 0x20, 0x2d, 0x20, 0x32, 0x29, 0x3b,
0x0a, 0x7d, 0xff, 0x49, 0x43, 0x00,
0x00, 0x0c, 0x43, 0x02, 0x01, 0xae, 0x03, 0x01,
0x00, 0x01, 0x04, 0x01, 0x00, 0x1a, 0x01, 0xb0,
0x03, 0x00, 0x01, 0x00, 0xae, 0x03, 0x00, 0x00,
0xd0, 0xb3, 0xa7, 0xe9, 0x03, 0xb3, 0x28, 0xd0,
0xb4, 0xac, 0xe9, 0x03, 0xb4, 0x28, 0xdc, 0xd0,
0xb4, 0x9e, 0xee, 0xdc, 0xd0, 0xb5, 0x9e, 0xee,
0x9d, 0x28, 0xac, 0x03, 0x02, 0x08, 0x20, 0x04,
0x00, 0x07, 0x06, 0x07, 0x06, 0x12, 0x09, 0x08,
0x07, 0x07, 0x10, 0x07, 0x06, 0x07, 0x06, 0x12,
0x13, 0x08, 0x07, 0x08, 0x16, 0x0c, 0x0c, 0x07,
0x04, 0x0c, 0x0a, 0x0c, 0x0c, 0x07, 0x04, 0x8d,
0x01, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x20, 0x66, 0x69, 0x62, 0x28, 0x6e, 0x29,
0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
0x66, 0x20, 0x28, 0x6e, 0x20, 0x3c, 0x3d, 0x20,
0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72,
0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66,
0x20, 0x28, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x31,
0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
0x75, 0x72, 0x6e, 0x20, 0x66, 0x69, 0x62, 0x28,
0x6e, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x20, 0x2b,
0x20, 0x66, 0x69, 0x62, 0x28, 0x6e, 0x20, 0x2d,
0x20, 0x32, 0x29, 0x3b, 0x0a, 0x7d,
};
const uint32_t qjsc_hello_module_size = 183;
const uint32_t qjsc_hello_module_size = 177;
const uint8_t qjsc_hello_module[183] = {
0x08, 0x07, 0x30, 0x65, 0x78, 0x61, 0x6d, 0x70,
const uint8_t qjsc_hello_module[177] = {
0x09, 0x07, 0x30, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x73, 0x2f, 0x68, 0x65, 0x6c, 0x6c,
0x6f, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
0x2e, 0x6a, 0x73, 0x1e, 0x2e, 0x2f, 0x66, 0x69,
@ -72,7 +71,7 @@ const uint8_t qjsc_hello_module[183] = {
0xbb, 0x0a, 0xee, 0x24, 0x02, 0x00, 0x0e, 0x06,
0x2e, 0xac, 0x03, 0x01, 0x01, 0x0a, 0x01, 0x01,
0x00, 0x04, 0x0a, 0x02, 0x62, 0x00, 0x4d, 0x30,
0x00, 0xff, 0x49, 0x43, 0x01, 0xb4, 0x03,
0x00,
};
static JSContext *JS_NewCustomContext(JSRuntime *rt)

20112
gen/repl.c

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,10 @@
#include "quickjs-libc.h"
const uint32_t qjsc_test_fib_size = 172;
const uint32_t qjsc_test_fib_size = 166;
const uint8_t qjsc_test_fib[172] = {
0x08, 0x07, 0x28, 0x65, 0x78, 0x61, 0x6d, 0x70,
const uint8_t qjsc_test_fib[166] = {
0x09, 0x07, 0x28, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74,
0x5f, 0x66, 0x69, 0x62, 0x2e, 0x6a, 0x73, 0x10,
0x2e, 0x2f, 0x66, 0x69, 0x62, 0x2e, 0x73, 0x6f,
@ -25,8 +25,7 @@ const uint8_t qjsc_test_fib[172] = {
0x00, 0x00, 0x65, 0x00, 0x00, 0xbb, 0x0a, 0xee,
0x24, 0x02, 0x00, 0x0e, 0x06, 0x2e, 0xac, 0x03,
0x01, 0x01, 0x0a, 0x01, 0x01, 0x00, 0x04, 0x0a,
0x02, 0x62, 0x00, 0x4d, 0x30, 0x00, 0xff, 0x49,
0x43, 0x01, 0xb4, 0x03,
0x02, 0x62, 0x00, 0x4d, 0x30, 0x00,
};
static JSContext *JS_NewCustomContext(JSRuntime *rt)

View file

@ -136,9 +136,12 @@ DEF( put_ref_value, 1, 3, 0, none)
DEF( define_var, 6, 0, 0, atom_u8)
DEF(check_define_var, 6, 0, 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_field2, 5, 1, 2, atom)
DEF( put_field, 5, 2, 0, atom)
DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
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_function, 1, 1, 1, none)
// order matters, see non-IC counterparts
DEF( get_field_ic, 5, 1, 1, none)
DEF( get_field2_ic, 5, 1, 2, none)
DEF( put_field_ic, 5, 2, 0, none)

122
quickjs.c
View file

@ -593,7 +593,6 @@ static int resize_ic_hash(JSContext *ctx, JSInlineCache *ic);
static int free_ic(JSRuntime *rt, JSInlineCache *ic);
static uint32_t add_ic_slot(JSContext *ctx, JSInlineCache *ic, JSAtom atom, JSObject *object,
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,
JSShape *shape)
@ -19995,8 +19994,34 @@ static void emit_atom(JSParseState *s, JSAtom name)
emit_u32(s, JS_DupAtom(s->ctx, name));
}
static void emit_ic(JSParseState *s, JSAtom atom) {
add_ic_slot1(s->ctx, s->cur_func->ic, atom);
static force_inline uint32_t get_index_hash(JSAtom atom, int hash_bits)
{
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)
@ -32518,7 +32543,7 @@ typedef enum BCTagEnum {
BC_TAG_OBJECT_REFERENCE,
} BCTagEnum;
#define BC_VERSION 8
#define BC_VERSION 9
typedef struct BCWriterState {
JSContext *ctx;
@ -32722,18 +32747,24 @@ static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
}
}
static int JS_WriteFunctionBytecode(BCWriterState *s,
const uint8_t *bc_buf1, int bc_len)
static BOOL is_ic_op(uint8_t op)
{
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;
uint8_t *bc_buf;
uint32_t val;
bc_len = b->byte_code_len;
bc_buf = js_malloc(s->ctx, bc_len);
if (!bc_buf)
return -1;
memcpy(bc_buf, bc_buf1, bc_len);
memcpy(bc_buf, b->byte_code_buf, bc_len);
pos = 0;
while (pos < bc_len) {
@ -32751,6 +32782,16 @@ static int JS_WriteFunctionBytecode(BCWriterState *s,
put_u32(bc_buf + pos + 1, val);
break;
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;
}
pos += len;
@ -32918,7 +32959,7 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValue obj)
bc_put_u8(s, flags);
}
if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
if (JS_WriteFunctionBytecode(s, b))
goto fail;
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);
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++) {
if (JS_WriteObjectRec(s, b->cpool[i]))
goto fail;
@ -33643,6 +33671,7 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
#endif
break;
default:
assert(!is_ic_op(op)); // should not end up in serialized bytecode
break;
}
pos += len;
@ -33919,28 +33948,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
if (bc_get_buf(s, b->source, b->source_len))
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) {
bc_read_trace(s, "cpool {\n");
for(i = 0; i < b->cpool_count; i++) {
@ -52120,11 +52127,6 @@ static void insert_weakref_record(JSValue target, struct JSWeakRefRecord *wr)
/* 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 *ic;
@ -52258,28 +52260,6 @@ end:
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 */
static void js_callsite_finalizer(JSRuntime *rt, JSValue val)