Browse Source

Enable minimal fast return handling

Enable fast return which allows unwinding of label sites which is needed to
support returning from inside iterator statements.  Fast return is disabled
from inside a try statement.  The fast return conditions can be relaxed
further later.
pull/342/head
Sami Vaarala 9 years ago
parent
commit
7f8618997a
  1. 10
      src/duk_js_compiler.c
  2. 59
      src/duk_js_executor.c

10
src/duk_js_compiler.c

@ -5666,20 +5666,12 @@ DUK_LOCAL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *re
ret_flags = DUK_BC_RETURN_FLAG_HAVE_RETVAL;
}
/* XXX: For now, "fast returns" are disabled. The compiler doesn't track
* label site depth so when it emits a fast return, it doesn't know whether
* label sites exist or not. Label sites are emitted for e.g. for loops,
* so it's probably quite relevant to handle them in the executor's fast
* return handler.
*/
#if 0
if (comp_ctx->curr_func.catch_depth == 0) {
DUK_DDD(DUK_DDDPRINT("fast return allowed -> use fast return"));
ret_flags |= DUK_BC_RETURN_FLAG_FAST;
} else {
DUK_DDD(DUK_DDDPRINT("fast return not allowed -> use slow return"));
}
#endif
duk__emit_a_b(comp_ctx,
DUK_OP_RETURN | DUK__EMIT_FLAG_NO_SHUFFLE_A,
@ -7151,6 +7143,8 @@ DUK_LOCAL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, duk_bool_t expec
* detected; even if the previous instruction is an unconditional jump,
* there may be a previous jump which jumps to current PC (which is the
* case for iteration and conditional statements, for instance).
*
* A final return is always a fast return: there can be no active catchers.
*/
/* XXX: request a "last statement is terminal" from duk__parse_stmt() and duk__parse_stmts();

59
src/duk_js_executor.c

@ -1417,25 +1417,21 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
return retval;
}
/* XXX: Disabled for 1.0 release. This needs to handle unwinding for label
* sites (which are created for explicit labels but also for control statements
* like for-loops). At that point it's quite close to the "slow return" handler
* except for longjmp(). Perhaps all returns could initially be handled as fast
* returns and only converted to longjmp()s when basic handling won't do?
*/
#if 0
/* Try a fast return. Return false if fails, so that a slow return can be done
* instead.
*/
DUK_LOCAL
duk_bool_t duk__handle_fast_return(duk_hthread *thr,
duk_tval *tv_retval,
duk_hthread *entry_thread,
duk_size_t entry_callstack_top) {
DUK_LOCAL DUK_NOINLINE duk_bool_t duk__handle_fast_return(duk_hthread *thr,
duk_tval *tv_retval,
duk_hthread *entry_thread,
duk_size_t entry_callstack_top) {
duk_tval tv_tmp;
duk_tval *tv1;
duk_catcher *cat;
duk_size_t orig_callstack_index;
/* retval == NULL indicates 'undefined' return value */
/* tv_retval == NULL indicates 'undefined' return value */
/* XXX: assert: no finally/TCF catchers */
if (thr == entry_thread && thr->callstack_top == entry_callstack_top) {
DUK_DDD(DUK_DDDPRINT("reject fast return: return would exit bytecode executor to caller"));
@ -1446,6 +1442,22 @@ duk_bool_t duk__handle_fast_return(duk_hthread *thr,
return 0;
}
DUK_ASSERT(thr->ptr_curr_pc == NULL); /* caller ensures */
/* XXX: it'd be more efficient if the returning 'act' indicated the
* proper catchstack level it is using
*/
/* Catchstack */
cat = thr->catchstack + thr->catchstack_top - 1; /* may be < thr->catchstack initially */
DUK_ASSERT(thr->callstack_top > 0); /* ensures callstack_top - 1 >= 0 */
orig_callstack_index = thr->callstack_top - 1;
while (cat >= thr->catchstack) {
if (cat->callstack_index != orig_callstack_index) {
break;
}
cat--;
}
/* There is a caller, and it must be an Ecmascript caller (otherwise
* it would have matched the entry level check).
*/
@ -1456,6 +1468,7 @@ duk_bool_t duk__handle_fast_return(duk_hthread *thr,
DUK_TVAL_SET_TVAL(&tv_tmp, tv1);
if (tv_retval) {
DUK_TVAL_SET_TVAL(tv1, tv_retval);
DUK_TVAL_CHKFAST_INPLACE(tv1);
DUK_TVAL_INCREF(thr, tv1);
} else {
DUK_TVAL_SET_UNDEFINED_ACTUAL(tv1);
@ -1463,17 +1476,13 @@ duk_bool_t duk__handle_fast_return(duk_hthread *thr,
}
DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
/* No catchstack to unwind. */
#if 0
duk_hthread_catchstack_unwind(thr, (cat - thr->catchstack) + 1); /* leave 'cat' as top catcher (also works if catchstack exhausted) */
#endif
duk_hthread_callstack_unwind(thr, thr->callstack_top - 1);
duk__reconfig_valstack(thr, thr->callstack_top - 1, 1); /* new top, i.e. callee */
DUK_DDD(DUK_DDDPRINT("fast return accepted"));
return 1;
}
#endif
/*
* Executor interrupt handling
@ -3237,15 +3246,17 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
* C -> currently unused
*/
/* Sync and NULL early, so that both fast/slow return code paths
* share this. Syncing is not that important for RETURN, but
* NULLing is.
*/
DUK__SYNC_AND_NULL_CURR_PC();
/* A fast return avoids full longjmp handling for a set of
* scenarios which hopefully represents the common cases.
* The compiler is responsible for emitting fast returns
* only when they are safe. Currently this means that there
* is nothing on the catch stack (not even label catchers).
* The speed advantage of fast returns (avoiding longjmp) is
* not very high, around 10-15%.
* only when they are safe.
*/
#if 0 /* XXX: Disabled for 1.0 release */
if (a & DUK_BC_RETURN_FLAG_FAST) {
DUK_DDD(DUK_DDDPRINT("FASTRETURN attempt a=%ld b=%ld", (long) a, (long) b));
@ -3254,11 +3265,9 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
entry_thread,
entry_callstack_top)) {
DUK_DDD(DUK_DDDPRINT("FASTRETURN success a=%ld b=%ld", (long) a, (long) b));
DUK__SYNC_CURR_PC();
goto restart_execution;
}
}
#endif
/* No fast return, slow path. */
DUK_DDD(DUK_DDDPRINT("SLOWRETURN a=%ld b=%ld", (long) a, (long) b));
@ -3282,7 +3291,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_RETURN);
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* in bytecode executor, should always be set */
DUK__SYNC_AND_NULL_CURR_PC();
DUK_ASSERT(thr->ptr_curr_pc == NULL); /* done above */
duk_err_longjmp(thr);
DUK_UNREACHABLE();
break;

Loading…
Cancel
Save