diff --git a/RELEASES.rst b/RELEASES.rst index a66fab23..9adce504 100644 --- a/RELEASES.rst +++ b/RELEASES.rst @@ -1405,6 +1405,9 @@ Planned * Change OS string (visible in Duktape.env) from "ios" to "osx" for non-phone targets (GH-570, GH-571) +* Internal performance improvement: use raw value stack accessors internally + when it's safe to do so (GH-582) + 2.0.0 (XXXX-XX-XX) ------------------ diff --git a/src/duk_api_internal.h b/src/duk_api_internal.h index 383e90dc..3e7aea60 100644 --- a/src/duk_api_internal.h +++ b/src/duk_api_internal.h @@ -173,4 +173,22 @@ DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_context *ctx, duk_idx_t /* Set object 'length'. */ DUK_INTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_size_t length); +/* Raw internal valstack access macros: access is unsafe so call site + * must have a guarantee that the index is valid. When that is the case, + * using these macro results in faster and smaller code than duk_get_tval(). + * Both 'ctx' and 'idx' are evaluted multiple times, but only for asserts. + */ +#define DUK_ASSERT_VALID_NEGIDX(ctx,idx) \ + (DUK_ASSERT_EXPR((idx) < 0), DUK_ASSERT_EXPR(duk_is_valid_index((ctx), (idx)))) +#define DUK_ASSERT_VALID_POSIDX(ctx,idx) \ + (DUK_ASSERT_EXPR((idx) >= 0), DUK_ASSERT_EXPR(duk_is_valid_index((ctx), (idx)))) +#define DUK_GET_TVAL_NEGIDX(ctx,idx) \ + (DUK_ASSERT_VALID_NEGIDX((ctx),(idx)), ((duk_hthread *) (ctx))->valstack_top + (idx)) +#define DUK_GET_TVAL_POSIDX(ctx,idx) \ + (DUK_ASSERT_VALID_POSIDX((ctx),(idx)), ((duk_hthread *) (ctx))->valstack_bottom + (idx)) +#define DUK_GET_HOBJECT_NEGIDX(ctx,idx) \ + (DUK_ASSERT_VALID_NEGIDX((ctx),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (ctx))->valstack_top + (idx))) +#define DUK_GET_HOBJECT_POSIDX(ctx,idx) \ + (DUK_ASSERT_VALID_POSIDX((ctx),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (ctx))->valstack_bottom + (idx))) + #endif /* DUK_API_INTERNAL_H_INCLUDED */ diff --git a/src/duk_hobject_props.c b/src/duk_hobject_props.c index 682ade1b..fd4fd8f2 100644 --- a/src/duk_hobject_props.c +++ b/src/duk_hobject_props.c @@ -4353,7 +4353,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, duk_push_tval(ctx, tv_obj); duk_push_tval(ctx, tv_key); - tv_obj = duk_get_tval(ctx, -2); + tv_obj = DUK_GET_TVAL_NEGIDX(ctx, -2); if (DUK_TVAL_IS_OBJECT(tv_obj)) { duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_obj); DUK_ASSERT(obj != NULL); @@ -4745,7 +4745,11 @@ DUK_INTERNAL void duk_hobject_set_length(duk_hthread *thr, duk_hobject *obj, duk duk_push_hobject(ctx, obj); duk_push_hstring_stridx(ctx, DUK_STRIDX_LENGTH); duk_push_u32(ctx, length); - (void) duk_hobject_putprop(thr, duk_get_tval(ctx, -3), duk_get_tval(ctx, -2), duk_get_tval(ctx, -1), 0); + (void) duk_hobject_putprop(thr, + DUK_GET_TVAL_NEGIDX(ctx, -3), + DUK_GET_TVAL_NEGIDX(ctx, -2), + DUK_GET_TVAL_NEGIDX(ctx, -1), + 0); duk_pop_n(ctx, 3); } @@ -4758,7 +4762,9 @@ DUK_INTERNAL duk_uint32_t duk_hobject_get_length(duk_hthread *thr, duk_hobject * duk_double_t val; duk_push_hobject(ctx, obj); duk_push_hstring_stridx(ctx, DUK_STRIDX_LENGTH); - (void) duk_hobject_getprop(thr, duk_get_tval(ctx, -2), duk_get_tval(ctx, -1)); + (void) duk_hobject_getprop(thr, + DUK_GET_TVAL_NEGIDX(ctx, -2), + DUK_GET_TVAL_NEGIDX(ctx, -1)); val = duk_to_number(ctx, -1); duk_pop_n(ctx, 3); if (val >= 0.0 && val < DUK_DOUBLE_2TO32) { diff --git a/src/duk_js_call.c b/src/duk_js_call.c index d70bbeae..ed1d1159 100644 --- a/src/duk_js_call.c +++ b/src/duk_js_call.c @@ -731,7 +731,7 @@ DUK_LOCAL duk_hobject *duk__nonbound_func_lookup(duk_context *ctx, for (;;) { /* Use loop to minimize code size of relookup after bound function case */ - tv_func = duk_get_tval(ctx, idx_func); + tv_func = DUK_GET_TVAL_POSIDX(ctx, idx_func); DUK_ASSERT(tv_func != NULL); if (DUK_TVAL_IS_OBJECT(tv_func)) { diff --git a/src/duk_js_compiler.c b/src/duk_js_compiler.c index 30f59d24..12e45292 100644 --- a/src/duk_js_compiler.c +++ b/src/duk_js_compiler.c @@ -512,23 +512,23 @@ DUK_LOCAL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx) { duk_push_array(ctx); func->consts_idx = entry_top + 1; - func->h_consts = duk_get_hobject(ctx, entry_top + 1); + func->h_consts = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 1); DUK_ASSERT(func->h_consts != NULL); duk_push_array(ctx); func->funcs_idx = entry_top + 2; - func->h_funcs = duk_get_hobject(ctx, entry_top + 2); + func->h_funcs = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 2); DUK_ASSERT(func->h_funcs != NULL); DUK_ASSERT(func->fnum_next == 0); duk_push_array(ctx); func->decls_idx = entry_top + 3; - func->h_decls = duk_get_hobject(ctx, entry_top + 3); + func->h_decls = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 3); DUK_ASSERT(func->h_decls != NULL); duk_push_array(ctx); func->labelnames_idx = entry_top + 4; - func->h_labelnames = duk_get_hobject(ctx, entry_top + 4); + func->h_labelnames = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 4); DUK_ASSERT(func->h_labelnames != NULL); duk_push_dynamic_buffer(ctx, 0); @@ -539,12 +539,12 @@ DUK_LOCAL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx) { duk_push_array(ctx); func->argnames_idx = entry_top + 6; - func->h_argnames = duk_get_hobject(ctx, entry_top + 6); + func->h_argnames = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 6); DUK_ASSERT(func->h_argnames != NULL); duk_push_object_internal(ctx); func->varmap_idx = entry_top + 7; - func->h_varmap = duk_get_hobject(ctx, entry_top + 7); + func->h_varmap = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 7); DUK_ASSERT(func->h_varmap != NULL); } @@ -570,7 +570,7 @@ DUK_LOCAL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx) { /* truncated in case pass 3 needed */ duk_push_object_internal(ctx); duk_replace(ctx, func->varmap_idx); - func->h_varmap = duk_get_hobject(ctx, func->varmap_idx); + func->h_varmap = DUK_GET_HOBJECT_POSIDX(ctx, func->varmap_idx); DUK_ASSERT(func->h_varmap != NULL); } @@ -588,7 +588,7 @@ DUK_LOCAL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx) { /* [ ... varmap ] */ - h_varmap = duk_get_hobject(ctx, -1); + h_varmap = DUK_GET_HOBJECT_NEGIDX(ctx, -1); DUK_ASSERT(h_varmap != NULL); ret = 0; @@ -655,7 +655,7 @@ DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx, duk_boo /* Valstack should suffice here, required on function valstack init */ (void) duk_push_compiledfunction(ctx); - h_res = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); /* XXX: specific getter */ + h_res = (duk_hcompiledfunction *) DUK_GET_HOBJECT_NEGIDX(ctx, -1); /* XXX: specific getter */ DUK_ASSERT(h_res != NULL); if (func->is_function) { @@ -1892,7 +1892,7 @@ DUK_LOCAL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx) { n = (duk_int_t) duk_get_length(ctx, f->consts_idx); - tv1 = duk_get_tval(ctx, -1); + tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1); DUK_ASSERT(tv1 != NULL); #if defined(DUK_USE_FASTINT) @@ -1968,7 +1968,7 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, case DUK_ISPEC_VALUE: { duk_tval *tv; - tv = duk_get_tval(ctx, x->valstack_idx); + tv = DUK_GET_TVAL_POSIDX(ctx, x->valstack_idx); DUK_ASSERT(tv != NULL); switch (DUK_TVAL_GET_TAG(tv)) { @@ -2158,8 +2158,8 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x /* inline arithmetic check for constant values */ /* XXX: use the exactly same arithmetic function here as in executor */ if (x->x1.t == DUK_ISPEC_VALUE && x->x2.t == DUK_ISPEC_VALUE && x->t == DUK_IVAL_ARITH) { - tv1 = duk_get_tval(ctx, x->x1.valstack_idx); - tv2 = duk_get_tval(ctx, x->x2.valstack_idx); + tv1 = DUK_GET_TVAL_POSIDX(ctx, x->x1.valstack_idx); + tv2 = DUK_GET_TVAL_POSIDX(ctx, x->x2.valstack_idx); DUK_ASSERT(tv1 != NULL); DUK_ASSERT(tv2 != NULL); @@ -3593,12 +3593,13 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE && duk_is_number(ctx, res->x1.valstack_idx)) { - /* this optimization is important to handle negative literals (which are not directly - * provided by the lexical grammar + /* this optimization is important to handle negative literals + * (which are not directly provided by the lexical grammar) */ - duk_tval *tv_num = duk_get_tval(ctx, res->x1.valstack_idx); + duk_tval *tv_num; duk_double_union du; + tv_num = DUK_GET_TVAL_POSIDX(ctx, res->x1.valstack_idx); DUK_ASSERT(tv_num != NULL); DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_num)); du.d = DUK_TVAL_GET_NUMBER(tv_num); @@ -3621,8 +3622,9 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { /* Very minimal inlining to handle common idioms '!0' and '!1', * and also boolean arguments like '!false' and '!true'. */ - duk_tval *tv_val = duk_get_tval(ctx, res->x1.valstack_idx); + duk_tval *tv_val; + tv_val = DUK_GET_TVAL_POSIDX(ctx, res->x1.valstack_idx); DUK_ASSERT(tv_val != NULL); if (DUK_TVAL_IS_NUMBER(tv_val)) { duk_double_t d; diff --git a/src/duk_js_executor.c b/src/duk_js_executor.c index 1b809113..ba738500 100644 --- a/src/duk_js_executor.c +++ b/src/duk_js_executor.c @@ -422,6 +422,8 @@ DUK_LOCAL void duk__vm_arith_unary_op(duk_hthread *thr, duk_tval *tv_x, duk_idx_ DUK_ASSERT(thr != NULL); DUK_ASSERT(ctx != NULL); DUK_ASSERT(opcode == DUK_EXTRAOP_UNM || opcode == DUK_EXTRAOP_UNP); + DUK_ASSERT(tv_x != NULL); + DUK_ASSERT(idx_x >= 0); #if defined(DUK_USE_FASTINT) if (DUK_TVAL_IS_FASTINT(tv_x)) { @@ -449,7 +451,7 @@ DUK_LOCAL void duk__vm_arith_unary_op(duk_hthread *thr, duk_tval *tv_x, duk_idx_ if (!DUK_TVAL_IS_NUMBER(tv_x)) { duk_to_number(ctx, idx_x); /* side effects, perform in-place */ - tv_x = duk_get_tval(ctx, idx_x); + tv_x = DUK_GET_TVAL_POSIDX(ctx, idx_x); DUK_ASSERT(tv_x != NULL); DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x)); } @@ -726,7 +728,7 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), act_lex_env); - new_env = duk_get_hobject(ctx, -1); + new_env = DUK_GET_HOBJECT_NEGIDX(ctx, -1); DUK_ASSERT(new_env != NULL); DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env)); @@ -2797,12 +2799,12 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th } else { duk_push_tval(ctx, DUK__REGCONSTP(c)); } - tv1 = duk_get_tval(ctx, -1); + tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1); act = thr->callstack + thr->callstack_top - 1; if (duk_js_declvar_activation(thr, act, name, tv1, prop_flags, is_func_decl)) { /* already declared, must update binding value */ - tv1 = duk_get_tval(ctx, -1); + tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1); duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT()); } @@ -3591,7 +3593,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th DUK_DDD(DUK_DDDPRINT("activating object env: %!iT", (duk_tval *) duk_get_tval(ctx, -1))); DUK_ASSERT(act->lex_env != NULL); - new_env = duk_get_hobject(ctx, -1); + new_env = DUK_GET_HOBJECT_NEGIDX(ctx, -1); DUK_ASSERT(new_env != NULL); act = thr->callstack + thr->callstack_top - 1; /* relookup (side effects) */ @@ -3750,7 +3752,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th } duk_push_number(ctx, y); - tv1 = duk_get_tval(ctx, -1); + tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1); DUK_ASSERT(tv1 != NULL); duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT()); duk_pop(ctx); @@ -3804,7 +3806,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th } duk_push_number(ctx, y); - tv_val = duk_get_tval(ctx, -1); + tv_val = DUK_GET_TVAL_NEGIDX(ctx, -1); DUK_ASSERT(tv_val != NULL); tv_obj = DUK__REGCONSTP(b); tv_key = DUK__REGCONSTP(c); @@ -3944,7 +3946,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th act = thr->callstack + thr->callstack_top - 1; if (duk_js_getvar_activation(thr, act, name, 0 /*throw*/)) { /* -> [... val this] */ - tv = duk_get_tval(ctx, -2); + tv = DUK_GET_TVAL_NEGIDX(ctx, -2); duk_push_hstring(ctx, duk_js_typeof(thr, tv)); duk_replace(ctx, (duk_idx_t) b); duk_pop_2(ctx); diff --git a/src/duk_js_ops.c b/src/duk_js_ops.c index 2b4f52be..5fa9dbea 100644 --- a/src/duk_js_ops.c +++ b/src/duk_js_ops.c @@ -730,7 +730,10 @@ DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_y) == 0 || DUK_TVAL_GET_BOOLEAN(tv_y) == 1); duk_push_tval(ctx, tv_x); duk_push_int(ctx, DUK_TVAL_GET_BOOLEAN(tv_y)); - rc = duk_js_equals_helper(thr, duk_get_tval(ctx, -2), duk_get_tval(ctx, -1), 0 /*flags:nonstrict*/); + rc = duk_js_equals_helper(thr, + DUK_GET_TVAL_NEGIDX(ctx, -2), + DUK_GET_TVAL_NEGIDX(ctx, -1), + 0 /*flags:nonstrict*/); duk_pop_2(ctx); return rc; } @@ -748,7 +751,10 @@ DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d duk_push_tval(ctx, tv_x); duk_push_tval(ctx, tv_y); duk_to_primitive(ctx, -2, DUK_HINT_NONE); /* apparently no hint? */ - rc = duk_js_equals_helper(thr, duk_get_tval(ctx, -2), duk_get_tval(ctx, -1), 0 /*flags:nonstrict*/); + rc = duk_js_equals_helper(thr, + DUK_GET_TVAL_NEGIDX(ctx, -2), + DUK_GET_TVAL_NEGIDX(ctx, -1), + 0 /*flags:nonstrict*/); duk_pop_2(ctx); return rc; } @@ -903,8 +909,8 @@ DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, } /* Note: reuse variables */ - tv_x = duk_get_tval(ctx, -2); - tv_y = duk_get_tval(ctx, -1); + tv_x = DUK_GET_TVAL_NEGIDX(ctx, -2); + tv_y = DUK_GET_TVAL_NEGIDX(ctx, -1); if (DUK_TVAL_IS_STRING(tv_x) && DUK_TVAL_IS_STRING(tv_y)) { duk_hstring *h1 = DUK_TVAL_GET_STRING(tv_x); @@ -1213,7 +1219,9 @@ DUK_INTERNAL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv duk_require_type_mask(ctx, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC); duk_to_string(ctx, -2); /* coerce lval with ToString() */ - retval = duk_hobject_hasprop(thr, duk_get_tval(ctx, -1), duk_get_tval(ctx, -2)); + retval = duk_hobject_hasprop(thr, + DUK_GET_TVAL_NEGIDX(ctx, -1), + DUK_GET_TVAL_NEGIDX(ctx, -2)); duk_pop_2(ctx); return retval;