diff --git a/include/silver/engine.h b/include/silver/engine.h index a592f4a..eb61468 100644 --- a/include/silver/engine.h +++ b/include/silver/engine.h @@ -154,6 +154,7 @@ struct sv_func { bool has_await; bool is_generator; bool is_method; + bool is_static; bool is_tla; uint64_t gc_epoch; diff --git a/src/silver/compiler.c b/src/silver/compiler.c index 47e7322..d9df9bc 100644 --- a/src/silver/compiler.c +++ b/src/silver/compiler.c @@ -3962,6 +3962,7 @@ static void compile_class_method( bool is_fn = is_class_method_def(m); if (is_fn) { + if (is_static) m->right->flags |= FN_STATIC; compile_func_expr(c, m->right); emit_get_local(c, home_local); emit_op(c, OP_SET_HOME_OBJ); @@ -4594,9 +4595,11 @@ sv_func_t *compile_function_body( func->has_await = false; func->is_generator = !!(node->flags & FN_GENERATOR); func->is_method = !!(node->flags & FN_METHOD); + func->is_static = !!(node->flags & FN_STATIC); func->is_tla = comp.is_tla; func->filename = enclosing->filename ? enclosing->filename : enclosing->js->filename; func->source_line = (int)node->line; + if (node->str && node->len > 0) { char *name = code_arena_bump(node->len + 1); memcpy(name, node->str, node->len); diff --git a/src/silver/engine.c b/src/silver/engine.c index 9c6d610..0b7e841 100644 --- a/src/silver/engine.c +++ b/src/silver/engine.c @@ -1020,9 +1020,9 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_PUT_PRIVATE: { sv_op_put_private(vm, js); NEXT(1); } L_DEF_PRIVATE: { sv_op_def_private(vm, js); NEXT(1); } - L_GET_SUPER: { sv_op_get_super(vm, js); NEXT(1); } - L_GET_SUPER_VAL: { sv_op_get_super_val(vm, js); NEXT(1); } - L_PUT_SUPER_VAL: { sv_op_put_super_val(vm, js); NEXT(1); } + L_GET_SUPER: { sv_op_get_super(vm, js); NEXT(1); } + L_GET_SUPER_VAL: { sv_op_get_super_val(vm, js, frame); NEXT(1); } + L_PUT_SUPER_VAL: { sv_op_put_super_val(vm, js); NEXT(1); } L_ADD: { ant_value_t r = vm->stack[vm->sp - 1], l = vm->stack[vm->sp - 2]; diff --git a/src/silver/ops/super.h b/src/silver/ops/super.h index e096c58..e57e8c0 100644 --- a/src/silver/ops/super.h +++ b/src/silver/ops/super.h @@ -8,15 +8,21 @@ static inline void sv_op_get_super(sv_vm_t *vm, ant_t *js) { vm->stack[vm->sp - 1] = js_get_proto(js, obj); } -static inline void sv_op_get_super_val(sv_vm_t *vm, ant_t *js) { +static inline void sv_op_get_super_val(sv_vm_t *vm, ant_t *js, sv_frame_t *frame) { ant_value_t prop = vm->stack[--vm->sp]; ant_value_t obj = vm->stack[--vm->sp]; ant_value_t receiver = vm->stack[--vm->sp]; - ant_value_t proto; - if (vtype(obj) == T_FUNC && vtype(receiver) != T_FUNC) { - proto = js_getprop_fallback(js, obj, "prototype"); - } else proto = js_get_proto(js, obj); + sv_func_t *current_func = frame ? frame->func : NULL; + bool use_ctor_prototype = + vtype(obj) == T_FUNC && + vtype(receiver) != T_FUNC && + current_func && + !current_func->is_static; + + ant_value_t proto; + if (use_ctor_prototype) proto = js_getprop_fallback(js, obj, "prototype"); + else proto = js_get_proto(js, obj); if (vtype(prop) == T_SYMBOL) { vm->stack[vm->sp++] = js_get_sym_with_receiver(js, proto, prop, receiver); diff --git a/src/silver/swarm.c b/src/silver/swarm.c index 78495a8..915a6db 100644 --- a/src/silver/swarm.c +++ b/src/silver/swarm.c @@ -1053,14 +1053,14 @@ static bool jit_inlineable(sv_func_t *f) { case OP_JMP_TRUE_PEEK: case OP_JMP_FALSE_PEEK: case OP_RETURN: case OP_RETURN_UNDEF: case OP_GET_FIELD: case OP_GET_FIELD2: case OP_GET_GLOBAL: + case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: + break; case OP_SPECIAL_OBJ: // OP_SPECIAL_OBJ(0) materializes `arguments`. keep these functions on // the interpreter until JIT routes a real per-call activation/object // with matching lifetime and semantics. if (sv_get_u8(ip + 1) == 0) return false; break; - case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: - break; default: return false; } @@ -1130,6 +1130,7 @@ static bool jit_emit_inline_body( MIR_reg_t result, MIR_label_t slow, MIR_label_t join, MIR_reg_t r_bool, MIR_reg_t *p_d_slot, int id, MIR_reg_t r_inl_closure, MIR_reg_t r_inl_this, + MIR_reg_t r_inl_new_target, MIR_reg_t r_inl_super, MIR_reg_t r_vm, MIR_reg_t r_js, MIR_item_t helper2_proto, MIR_item_t imp_seq, MIR_item_t imp_sne, MIR_item_t imp_eq, MIR_item_t imp_ne, @@ -1811,14 +1812,15 @@ static bool jit_emit_inline_body( uint8_t which = sv_get_u8(ip + 1); MIR_reg_t dst = inl_vs[isp++]; if (which == 1) { - mir_load_imm(ctx, jit_func, dst, mkval(T_UNDEF, 0)); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, dst), + MIR_new_reg_op(ctx, r_inl_new_target))); } else if (which == 2) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), - MIR_new_mem_op(ctx, MIR_T_I64, - (MIR_disp_t)offsetof(sv_closure_t, super_val), - r_inl_closure, 0, 1))); + MIR_new_reg_op(ctx, r_inl_super))); } else if (which == 3) { MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 6, @@ -4724,6 +4726,8 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_label_t inl_join = MIR_new_label(ctx); MIR_reg_t r_inl_cl = 0; + MIR_reg_t r_inl_new_target = 0; + MIR_reg_t r_inl_super = 0; char inl_this_rn[32], inl_flags_rn[32], inl_bound_rn[32]; snprintf(inl_this_rn, sizeof(inl_this_rn), "inl%d_this", cn); snprintf(inl_flags_rn, sizeof(inl_flags_rn), "inl%d_flags", cn); @@ -4743,6 +4747,22 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_reg_op(ctx, r_inl_callee), MIR_new_uint_op(ctx, NANBOX_DATA_MASK))); } + { + char nt_rn[32], sup_rn[32]; + snprintf(nt_rn, sizeof(nt_rn), "inl%d_nt", cn); + snprintf(sup_rn, sizeof(sup_rn), "inl%d_sup", cn); + r_inl_new_target = MIR_new_func_reg(ctx, jit_func->u.func, + MIR_JSVAL, nt_rn); + r_inl_super = MIR_new_func_reg(ctx, jit_func->u.func, + MIR_JSVAL, sup_rn); + mir_load_imm(ctx, jit_func, r_inl_new_target, mkval(T_UNDEF, 0)); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_inl_super), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)offsetof(sv_closure_t, super_val), + r_inl_cl, 0, 1))); + } if (speculative) { char gt_rn[32]; snprintf(gt_rn, sizeof(gt_rn), "inl%d_gt", cn); @@ -4783,7 +4803,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos inl_arg_regs, (int)call_argc, r_call_res, inl_slow, inl_join, r_bool, &r_d_slot, cn, - r_inl_cl, r_inl_this, + r_inl_cl, r_inl_this, r_inl_new_target, r_inl_super, r_vm, r_js, helper2_proto, imp_seq, imp_sne, imp_eq, imp_ne, gf_proto, imp_get_field, diff --git a/tests/test_jit_inline_special_obj.mjs b/tests/test_jit_inline_special_obj.mjs new file mode 100644 index 0000000..052bfdd --- /dev/null +++ b/tests/test_jit_inline_special_obj.mjs @@ -0,0 +1,97 @@ +function assert(condition, message) { + if (!condition) throw new Error(message); +} + +function assertEq(actual, expected, message) { + if (actual !== expected) { + throw new Error(`${message}: expected ${expected}, got ${actual}`); + } +} + +function runNewTargetHot() { + function readNewTarget() { + return new.target; + } + + let last = null; + for (let i = 0; i < 600; i++) { + last = readNewTarget(); + } + return last; +} + +class BaseBox { + static value = 7; +} + +class DerivedBox extends BaseBox { + static readSuperValue() { + return super.value; + } +} + +const superReader = DerivedBox.readSuperValue; + +const staticValueSymbol = Symbol("static-value"); + +class SymbolBaseBox { + static [staticValueSymbol] = 11; +} + +class SymbolDerivedBox extends SymbolBaseBox { + static readSuperSymbol() { + return super[staticValueSymbol]; + } +} + +const superSymbolReader = SymbolDerivedBox.readSuperSymbol; + +class ConstructorBase { + get value() { + return this.tag; + } +} + +class ConstructorDerived extends ConstructorBase { + constructor() { + super(); + this.tag = "ctor-super"; + this.fromCtor = super.value; + } +} + +function runSuperHot() { + let last = 0; + for (let i = 0; i < 600; i++) { + last = superReader(); + } + return last; +} + +function runSuperSymbolHot() { + let last = 0; + for (let i = 0; i < 600; i++) { + last = superSymbolReader(); + } + return last; +} + +const isAnt = typeof process === "object" + && process !== null + && typeof process.versions === "object" + && process.versions !== null + && typeof process.versions.ant === "string"; + +assertEq(runNewTargetHot(), undefined, "hot inline helper preserves new.target"); +assertEq(superReader(), 7, "extracted static super value lookup"); +assertEq(runSuperHot(), 7, "hot direct call preserves super value lookup"); +assertEq(superSymbolReader(), 11, "extracted static super symbol lookup"); +assertEq(runSuperSymbolHot(), 11, "hot direct call preserves super symbol lookup"); +assertEq(new ConstructorDerived().fromCtor, "ctor-super", "constructor super getter lookup"); + +if (isAnt) { + const antOnly = await import("./test_jit_inline_special_obj_import.mjs"); + await antOnly.verifyImportBinding(assert, assertEq); +} + +console.log("OK: test_jit_inline_special_obj"); diff --git a/tests/test_jit_inline_special_obj_dep.mjs b/tests/test_jit_inline_special_obj_dep.mjs new file mode 100644 index 0000000..a2bce03 --- /dev/null +++ b/tests/test_jit_inline_special_obj_dep.mjs @@ -0,0 +1 @@ +export const marker = "inline-special-obj"; diff --git a/tests/test_jit_inline_special_obj_import.mjs b/tests/test_jit_inline_special_obj_import.mjs new file mode 100644 index 0000000..1db8c54 --- /dev/null +++ b/tests/test_jit_inline_special_obj_import.mjs @@ -0,0 +1,15 @@ +function getImportBinding() { + return import; +} + +export async function verifyImportBinding(assert, assertEq) { + let importBinding = null; + for (let i = 0; i < 600; i++) { + importBinding = getImportBinding(); + } + + assert(typeof importBinding === "function", "hot inline helper returns import binding"); + + const imported = await importBinding("./test_jit_inline_special_obj_dep.mjs"); + assertEq(imported.marker, "inline-special-obj", "returned import binding loads sibling module"); +}