From e4f7cccfc6f38235422fe493f6ac4b35f18ded02 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 3 Feb 2025 21:57:29 +0100 Subject: [PATCH] Add pre-init ICs Defer creating the inline cache once, to avoid unprofitable busywork for JS code that only executes once. It's at best a qualified success. It succeeds at keeping the memory and CPU footprint of execute-once code down but it slows down the babel benchmark from the web-tooling-benchmark suite by 500% because that benchmark is all about property access on tree structures. Other benchmarks from the suite are unaffected, neither faster or slower, with the possible exception of the terser benchmark, which is a few percent slower for the same reason as the babel one, only much less pronounced. --- quickjs-opcode.h | 3 +++ quickjs.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/quickjs-opcode.h b/quickjs-opcode.h index fb5394c58..28cf1e532 100644 --- a/quickjs-opcode.h +++ b/quickjs-opcode.h @@ -366,6 +366,9 @@ 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_preinit_ic, 5, 1, 1, none) +DEF( get_field2_preinit_ic, 5, 1, 2, none) +DEF( put_field_preinit_ic, 5, 2, 0, none) DEF( get_field_ic, 5, 1, 1, none) DEF( get_field2_ic, 5, 1, 2, none) DEF( put_field_ic, 5, 2, 0, none) diff --git a/quickjs.c b/quickjs.c index faf5431f9..3eec9cfa6 100644 --- a/quickjs.c +++ b/quickjs.c @@ -16344,6 +16344,22 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, BREAK; CASE(OP_get_field): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], NULL, false); + if (unlikely(JS_IsException(val))) + goto exception; + put_u8(pc - 5, OP_get_field_preinit_ic); + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_get_field_preinit_ic): { JSValue val; JSAtom atom; @@ -16386,6 +16402,21 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, BREAK; CASE(OP_get_field2): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], NULL, false); + if (unlikely(JS_IsException(val))) + goto exception; + put_u8(pc - 5, OP_get_field2_preinit_ic); + *sp++ = val; + } + BREAK; + + CASE(OP_get_field2_preinit_ic): { JSValue val; JSAtom atom; @@ -16426,6 +16457,25 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, BREAK; CASE(OP_put_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + ret = JS_SetPropertyInternal2(ctx, + sp[-2], atom, + sp[-1], sp[-2], + JS_PROP_THROW_STRICT, NULL); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + put_u8(pc - 5, OP_put_field_preinit_ic); + } + BREAK; + + CASE(OP_put_field_preinit_ic): { int ret; JSAtom atom; @@ -34060,7 +34110,7 @@ static void bc_byte_swap(uint8_t *bc_buf, int bc_len) static bool is_ic_op(uint8_t op) { - return op >= OP_get_field_ic && op <= OP_put_field_ic; + return op >= OP_get_field_preinit_ic && op <= OP_put_field_ic; } static int JS_WriteFunctionBytecode(BCWriterState *s, @@ -34101,7 +34151,11 @@ static int JS_WriteFunctionBytecode(BCWriterState *s, 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); + if (op < OP_get_field_ic) { + bc_buf[pos] -= (OP_get_field_preinit_ic - OP_get_field); + } else { + bc_buf[pos] -= (OP_get_field_ic - OP_get_field); + } } break; }