Browse Source

Use direct bytecode pointer to improve performance

pull/297/head
Sami Vaarala 10 years ago
parent
commit
3aa7edbb41
  1. 1
      src/duk_api_call.c
  2. 6
      src/duk_api_stack.c
  3. 13
      src/duk_bi_duktape.c
  4. 6
      src/duk_debugger.c
  5. 10
      src/duk_error_augment.c
  6. 2
      src/duk_error_throw.c
  7. 21
      src/duk_hthread.h
  8. 53
      src/duk_hthread_misc.c
  9. 5
      src/duk_js_bytecode.h
  10. 38
      src/duk_js_call.c
  11. 186
      src/duk_js_executor.c

1
src/duk_api_call.c

@ -395,6 +395,7 @@ DUK_EXTERNAL void duk_new(duk_context *ctx, duk_idx_t nargs) {
*/
#ifdef DUK_USE_AUGMENT_ERROR_CREATE
duk_hthread_sync_currpc(thr);
duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/);
#endif

6
src/duk_api_stack.c

@ -4103,6 +4103,12 @@ DUK_EXTERNAL void duk_throw(duk_context *ctx) {
* just before an error is thrown.
*/
/* Sync needed before Duktape.errThrow augmentation, not strictly
* needed otherwise: bytecode longjmp handler syncs at the latest
* if we were executing bytecode.
*/
duk_hthread_sync_currpc(thr);
#if defined(DUK_USE_AUGMENT_ERROR_THROW)
DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(ctx, -1)));
duk_err_augment_error_throw(thr);

13
src/duk_bi_duktape.c

@ -145,14 +145,11 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_context *ctx) {
duk_push_tval(ctx, &act->tv_func);
pc = (duk_uint_fast32_t) act->pc;
if (pc > 0) {
/* Relevant PC is just before current one because PC is
* post-incremented. This should match what error augment
* code does.
*/
pc--;
}
/* Relevant PC is just before current one because PC is
* post-incremented. This should match what error augment
* code does.
*/
pc = duk_hthread_get_act_prev_pc(thr, act);
duk_push_uint(ctx, (duk_uint_t) pc);
#if defined(DUK_USE_PC2LINE)

6
src/duk_debugger.c

@ -804,7 +804,7 @@ DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) {
* has already been executed.)
*/
pc = (duk_uint_fast32_t) act->pc;
pc = duk_hthread_get_act_curr_pc(thr, act);
/* XXX: this should be optimized to be a raw query and avoid valstack
* operations if possible.
@ -840,7 +840,7 @@ DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) {
duk_pop_3(ctx);
/* Report next pc/line to be executed. */
duk_debug_write_uint(thr, (duk_uint32_t) duk_debug_curr_line(thr));
duk_debug_write_uint(thr, (duk_uint32_t) act->pc);
duk_debug_write_uint(thr, (duk_uint32_t) duk_hthread_get_act_curr_pc(thr, act));
}
duk_debug_write_eom(thr);
@ -1165,7 +1165,7 @@ DUK_LOCAL void duk__debug_handle_get_call_stack(duk_hthread *thr, duk_heap *heap
duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME);
duk_safe_to_string(ctx, -1);
duk_debug_write_hstring(thr, duk_get_hstring(ctx, -1));
pc = curr_act->pc;
pc = duk_hthread_get_act_curr_pc(thr, curr_act);
if (i != curr_thr->callstack_top - 1 && pc > 0) {
pc--;
}

10
src/duk_error_augment.c

@ -261,10 +261,7 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack,
* PC points to next instruction, find offending PC. Note that
* PC == 0 for native code.
*/
pc = thr_callstack->callstack[i].pc;
if (pc > 0) {
pc--;
}
pc = duk_hthread_get_act_prev_pc(thr_callstack, thr_callstack->callstack + i);
DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */
DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */
d = ((duk_double_t) thr_callstack->callstack[i].flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc;
@ -346,10 +343,7 @@ DUK_LOCAL void duk__err_augment_builtin_throw(duk_hthread *thr, duk_hthread *thr
/* PC points to next instruction, find offending PC. Note that
* PC == 0 for native code.
*/
pc = act->pc;
if (pc > 0) {
pc--;
}
pc = duk_hthread_get_act_prev_pc(thr, act);
DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */
DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */
act = NULL; /* invalidated by pushes, so get out of the way */

2
src/duk_error_throw.c

@ -41,6 +41,8 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code)
thr->heap->handling_error = 1;
duk_hthread_sync_currpc(thr); /* ensure PC is consistent for traceback */
/*
* Create and push an error object onto the top of stack.
* If a "double error" occurs, use a fixed error instance

21
src/duk_hthread.h

@ -188,11 +188,11 @@ struct duk_activation {
duk_hobject *prev_caller;
#endif
duk_small_uint_t flags;
duk_uint32_t pc; /* next instruction to execute */
duk_instr_t *curr_pc; /* next instruction to execute (points to 'func' bytecode, stable pointer), NULL for native calls */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
duk_uint32_t prev_line; /* needed for stepping */
#endif
duk_small_uint_t flags;
/* idx_bottom and idx_retval are only used for book-keeping of
* Ecmascript-initiated calls, to allow returning to an Ecmascript
@ -231,16 +231,23 @@ struct duk_activation {
struct duk_catcher {
duk_hstring *h_varname; /* borrowed reference to catch variable name (or NULL if none) */
/* (reference is valid as long activation exists) */
duk_instr_t *pc_base; /* resume execution from pc_base or pc_base+1 (points to 'func' bytecode, stable pointer) */
duk_size_t callstack_index; /* callstack index of related activation */
duk_size_t idx_base; /* idx_base and idx_base+1 get completion value and type */
duk_uint32_t pc_base; /* resume execution from pc_base or pc_base+1 */
duk_uint32_t flags; /* type and control flags, label number */
};
struct duk_hthread {
/* shared object part */
/* Shared object part */
duk_hobject obj;
/* Next opcode to execute. Conceptually this matches curr_pc of the
* topmost activation, but having it here is important for opcode
* dispatch performance. The downside is that whenever the activation
* changes this field and activation curr_pc must be carefully managed.
*/
duk_instr_t *curr_pc;
/* backpointers */
duk_heap *heap;
@ -333,4 +340,10 @@ DUK_INTERNAL_DECL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud);
DUK_INTERNAL_DECL void *duk_hthread_get_callstack_ptr(duk_heap *heap, void *ud); /* indirect allocs */
DUK_INTERNAL_DECL void *duk_hthread_get_catchstack_ptr(duk_heap *heap, void *ud); /* indirect allocs */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act);
#endif
DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act);
DUK_INTERNAL_DECL void duk_hthread_sync_currpc(duk_hthread *thr);
#endif /* DUK_HTHREAD_H_INCLUDED */

53
src/duk_hthread_misc.c

@ -42,3 +42,56 @@ DUK_INTERNAL duk_activation *duk_hthread_get_current_activation(duk_hthread *thr
return NULL;
}
}
#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act) {
duk_instr_t *bcode;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(act != NULL);
DUK_UNREF(thr);
/* XXX: store 'bcode' pointer to activation for faster lookup? */
if (act->func && DUK_HOBJECT_IS_COMPILEDFUNCTION(act->func)) {
bcode = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) (act->func));
return (duk_uint_fast32_t) (act->curr_pc - bcode);
}
return 0;
}
#endif /* DUK_USE_DEBUGGER_SUPPORT */
DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act) {
duk_instr_t *bcode;
duk_uint_fast32_t ret;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(act != NULL);
DUK_UNREF(thr);
if (act->func && DUK_HOBJECT_IS_COMPILEDFUNCTION(act->func)) {
bcode = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) (act->func));
ret = (duk_uint_fast32_t) (act->curr_pc - bcode);
if (ret > 0) {
ret--;
}
return ret;
}
return 0;
}
/* Write thr->curr_pc back to topmost activation (if any). */
DUK_INTERNAL void duk_hthread_sync_currpc(duk_hthread *thr) {
duk_activation *act;
DUK_ASSERT(thr != NULL);
if (thr->callstack_top > 0) {
/* For native calls the assignment is OK as long as thr->curr_pc
* is NULL for the duration of a native call.
*/
act = thr->callstack + thr->callstack_top - 1;
DUK_ASSERT(((act->func == NULL || DUK_HOBJECT_HAS_COMPILEDFUNCTION(act->func)) && thr->curr_pc != NULL) || \
((act->func != NULL && DUK_HOBJECT_HAS_NATIVEFUNCTION(act->func)) && thr->curr_pc == NULL));
act->curr_pc = thr->curr_pc;
}
}

5
src/duk_js_bytecode.h

@ -177,11 +177,6 @@ typedef duk_uint32_t duk_instr_t;
#define DUK_EXTRAOP_LABEL 32
#define DUK_EXTRAOP_ENDLABEL 33
/* DUK_OP_EXTRA for debugging */
#define DUK_EXTRAOP_DUMPREG 128
#define DUK_EXTRAOP_DUMPREGS 129
#define DUK_EXTRAOP_LOGMARK 130
/* DUK_OP_CALL flags in A */
#define DUK_BC_CALL_FLAG_TAILCALL (1 << 0)
#define DUK_BC_CALL_FLAG_EVALCALL (1 << 1)

38
src/duk_js_call.c

@ -832,6 +832,7 @@ duk_int_t duk_handle_call(duk_hthread *thr,
duk_int_t entry_call_recursion_depth;
duk_hthread *entry_curr_thread;
duk_uint_fast8_t entry_thread_state;
duk_instr_t *entry_thr_curr_pc;
volatile duk_bool_t need_setjmp;
duk_jmpbuf * volatile old_jmpbuf_ptr = NULL; /* ptr is volatile (not the target) */
duk_idx_t idx_func; /* valstack index of 'func' and retval (relative to entry valstack_bottom) */
@ -873,6 +874,10 @@ duk_int_t duk_handle_call(duk_hthread *thr,
entry_call_recursion_depth = thr->heap->call_recursion_depth;
entry_curr_thread = thr->heap->curr_thread; /* Note: may be NULL if first call */
entry_thread_state = thr->state;
entry_thr_curr_pc = NULL; /* not actually needed, avoid warning */
if (entry_curr_thread) {
entry_thr_curr_pc = entry_curr_thread->curr_pc;
}
idx_func = duk_normalize_index(ctx, -num_stack_args - 2); /* idx_func must be valid, note: non-throwing! */
idx_args = idx_func + 2; /* idx_args is not necessarily valid if num_stack_args == 0 (idx_args then equals top) */
@ -966,6 +971,11 @@ duk_int_t duk_handle_call(duk_hthread *thr,
DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
/* We don't need to sync back thr->curr_pc here because the
* bytecode executor always has a setjmp catchpoint which
* does that before errors propagate to here.
*/
/*
* Restore previous setjmp catchpoint
*/
@ -1217,7 +1227,7 @@ duk_int_t duk_handle_call(duk_hthread *thr,
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
act->prev_caller = NULL;
#endif
act->pc = 0;
act->curr_pc = NULL;
#if defined(DUK_USE_DEBUGGER_SUPPORT)
act->prev_line = 0;
#endif
@ -1360,6 +1370,8 @@ duk_int_t duk_handle_call(duk_hthread *thr,
* other invalid
*/
thr->curr_pc = NULL;
if (func) {
rc = ((duk_hnativefunction *) func)->func((duk_context *) thr);
} else {
@ -1446,6 +1458,10 @@ duk_int_t duk_handle_call(duk_hthread *thr,
* Shift to new valstack_bottom.
*/
DUK_ASSERT(func != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func));
act->curr_pc = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) func);
thr->valstack_bottom = thr->valstack_bottom + idx_args;
/* keep current valstack_top */
DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
@ -1566,6 +1582,13 @@ duk_int_t duk_handle_call(duk_hthread *thr,
DUK_DDD(DUK_DDDPRINT("setjmp catchpoint torn down"));
}
/* Restore entry thread curr_pc (could just reset from topmost
* activation too).
*/
if (entry_curr_thread) {
entry_curr_thread->curr_pc = entry_thr_curr_pc;
}
DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */
thr->state = (duk_uint8_t) entry_thread_state;
@ -2020,6 +2043,9 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
(thr->state == DUK_HTHREAD_STATE_RUNNING &&
thr->heap->curr_thread == thr));
/* store current pc if there's a running ecmascript function */
duk_hthread_sync_currpc(thr);
/* if a tail call:
* - an Ecmascript activation must be on top of the callstack
* - there cannot be any active catchstack entries
@ -2203,7 +2229,10 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
act->prev_caller = NULL;
#endif
act->pc = 0; /* don't want an intermediate exposed state with invalid pc */
DUK_ASSERT(func != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func));
/* don't want an intermediate exposed state with invalid pc */
act->curr_pc = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) func);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
act->prev_line = 0;
#endif
@ -2232,7 +2261,6 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
DUK_ASSERT(DUK_ACT_GET_FUNC(act) == func); /* already updated */
DUK_ASSERT(act->var_env == NULL); /* already NULLed (by unwind) */
DUK_ASSERT(act->lex_env == NULL); /* already NULLed (by unwind) */
DUK_ASSERT(act->pc == 0); /* already zeroed */
act->idx_bottom = entry_valstack_bottom_index; /* tail call -> reuse current "frame" */
DUK_ASSERT(nregs >= 0);
#if 0 /* topmost activation idx_retval is considered garbage, no need to init */
@ -2307,7 +2335,9 @@ duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
act->prev_caller = NULL;
#endif
act->pc = 0;
DUK_ASSERT(func != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func));
act->curr_pc = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, (duk_hcompiledfunction *) func);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
act->prev_line = 0;
#endif

186
src/duk_js_executor.c

@ -690,7 +690,7 @@ DUK_LOCAL void duk__handle_catch_or_finally(duk_hthread *thr, duk_size_t cat_idx
* Reset PC: resume execution from catch or finally jump slot.
*/
(thr->callstack + thr->callstack_top - 1)->pc =
(thr->callstack + thr->callstack_top - 1)->curr_pc =
thr->catchstack[cat_idx].pc_base + (is_finally ? 1 : 0);
/*
@ -784,7 +784,7 @@ DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx) {
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(act)));
/* +0 = break, +1 = continue */
act->pc = thr->catchstack[cat_idx].pc_base + (thr->heap->lj.type == DUK_LJ_TYPE_CONTINUE ? 1 : 0);
act->curr_pc = thr->catchstack[cat_idx].pc_base + (thr->heap->lj.type == DUK_LJ_TYPE_CONTINUE ? 1 : 0);
act = NULL; /* invalidated */
duk_hthread_catchstack_unwind(thr, cat_idx + 1); /* keep label catcher */
@ -1534,9 +1534,6 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_
line = duk_debug_curr_line(thr);
if (act->prev_line != line) {
DUK_DDD(DUK_DDDPRINT("PC=%ld line=%ld; line transition: %ld -> %ld",
(long) act->pc, (long) line, (long) act->prev_line, (long) line));
/* Stepped? Step out is handled by callstack unwind. */
if ((thr->heap->dbg_step_type == DUK_STEP_TYPE_INTO ||
thr->heap->dbg_step_type == DUK_STEP_TYPE_OVER) &&
@ -1576,7 +1573,7 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_
}
}
} else {
DUK_DDD(DUK_DDDPRINT("PC=%ld line=%ld", (long) act->pc, (long) line));
;
}
act->prev_line = line;
@ -1770,9 +1767,6 @@ DUK_LOCAL duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) {
ctr = 1;
}
DUK_DDD(DUK_DDDPRINT("executor interrupt finished, cstop=%ld, pc=%ld, nextctr=%ld",
(long) thr->callstack_top, (long) act->pc, (long) ctr));
/* The counter value is one less than the init value: init value should
* indicate how many instructions are executed before interrupt. To
* execute 1 instruction (after interrupt handler return), counter must
@ -1978,9 +1972,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
/* "hot" variables for interpretation -- not volatile, value not guaranteed in setjmp error handling */
duk_hthread *thr; /* stable */
duk_activation *act; /* semi-stable (ok as long as callstack not resized) */
duk_hcompiledfunction *fun; /* stable */
duk_instr_t *bcode; /* stable */
/* 'consts' is computed on-the-fly */
/* 'funcs' is quite rarely used, so no local for it */
@ -2058,6 +2050,9 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
DUK_ASSERT(entry_thread != NULL);
thr = entry_thread->heap->curr_thread;
DUK_ASSERT(thr->callstack_top > 0); /* we were executing bytecode */
duk_hthread_sync_currpc(thr);
/* XXX: signalling the need to shrink check (only if unwound) */
/* Must be restored here to handle e.g. yields properly. */
@ -2098,7 +2093,6 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
* Return from bytecode executor with a return value.
*/
DUK_ASSERT(lj_ret == DUK__LONGJMP_FINISHED);
/* XXX: return assertions for valstack, callstack, catchstack */
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr);
@ -2116,6 +2110,10 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
* The number of local variables should be kept to a minimum: if
* the variables are spilled, they will need to be loaded from
* memory anyway.
*
* Any 'goto restart_execution;' code path in opcode dispatch must
* ensure thr->curr_pc is synced back to act->curr_pc before the
* goto takes place.
*/
restart_execution:
@ -2133,15 +2131,21 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
/* Assume interrupt init/counter are properly initialized here. */
/* assume that thr->valstack_bottom has been set-up before getting here */
act = thr->callstack + thr->callstack_top - 1;
fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act);
DUK_ASSERT(fun != NULL);
DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs);
bcode = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun);
{
duk_activation *act;
act = thr->callstack + thr->callstack_top - 1;
fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act);
DUK_ASSERT(fun != NULL);
DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs);
}
#if defined(DUK_USE_DEBUGGER_SUPPORT)
if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap) && !thr->heap->dbg_processing) {
duk_activation *act;
thr->heap->dbg_processing = 1;
act = thr->callstack + thr->callstack_top - 1;
duk__executor_handle_debugger(thr, act, fun);
thr->heap->dbg_processing = 0;
}
@ -2178,18 +2182,14 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
*/
DUK_ASSERT(thr != NULL);
DUK_ASSERT(act != NULL);
DUK_ASSERT(fun != NULL);
DUK_ASSERT(bcode != NULL);
DUK_DD(DUK_DDPRINT("restarting execution, thr %p, act %p (idx %ld), fun %p, bcode %p, "
DUK_DD(DUK_DDPRINT("restarting execution, thr %p, act idx %ld, fun %p,"
"consts %p, funcs %p, lev %ld, regbot %ld, regtop %ld, catchstack_top=%ld, "
"preventcount=%ld",
(void *) thr,
(void *) act,
(long) (thr->callstack_top - 1),
(void *) fun,
(void *) bcode,
(void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, fun),
(void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, fun),
(long) (thr->callstack_top - 1),
@ -2202,6 +2202,13 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
valstack_top_base = (duk_size_t) (thr->valstack_top - thr->valstack);
#endif
/* Set up thr->curr_pc for opcode dispatch. */
{
duk_activation *act;
act = thr->callstack + thr->callstack_top - 1;
thr->curr_pc = act->curr_pc;
}
for (;;) {
DUK_ASSERT(thr->callstack_top >= 1);
DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs);
@ -2221,8 +2228,17 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
/* Trigger at zero or below */
duk_small_uint_t exec_int_ret;
/* Write curr_pc back for the debugger. */
DUK_ASSERT(thr->callstack_top > 0);
{
duk_activation *act;
act = thr->callstack + thr->callstack_top - 1;
act->curr_pc = thr->curr_pc;
}
exec_int_ret = duk__executor_interrupt(thr);
if (exec_int_ret == DUK__INT_RESTART) {
/* thr->curr_pc synced back above */
goto restart_execution;
}
}
@ -2231,31 +2247,24 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
thr->heap->inst_count_exec++;
#endif
/* Because ANY DECREF potentially invalidates 'act' now (through
* finalization), we need to re-lookup 'act' in almost every case.
*
* XXX: future work for performance optimization:
* This is not nice; it would be nice if the program counter was a
* behind a stable pointer. For instance, put a raw bytecode pointer
* into duk_hthread struct (not into the callstack); since bytecode
* has a stable pointer this would work nicely. Whenever a call is
* made, the bytecode pointer could be backed up as an integer index
* to the calling activation. Perhaps add a macro for setting up a
* new activation (same as for setting up / switching threads)?
*/
act = thr->callstack + thr->callstack_top - 1;
DUK_ASSERT(bcode + act->pc >= DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun));
DUK_ASSERT(bcode + act->pc < DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, fun));
DUK_DDD(DUK_DDDPRINT("executing bytecode: pc=%ld ins=0x%08lx, op=%ld, valstack_top=%ld/%ld, nregs=%ld --> %!I",
(long) act->pc,
(unsigned long) bcode[act->pc],
(long) DUK_DEC_OP(bcode[act->pc]),
(long) (thr->valstack_top - thr->valstack),
(long) (thr->valstack_end - thr->valstack),
(long) (fun ? fun->nregs : -1),
(duk_instr_t) bcode[act->pc]));
#if defined(DUK_USE_ASSERTIONS) || defined(DUK_USE_DEBUG)
{
duk_activation *act;
act = thr->callstack + thr->callstack_top - 1;
DUK_ASSERT(thr->curr_pc >= DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun));
DUK_ASSERT(thr->curr_pc < DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, fun));
DUK_UNREF(act); /* if debugging disabled */
DUK_DDD(DUK_DDDPRINT("executing bytecode: pc=%ld, ins=0x%08lx, op=%ld, valstack_top=%ld/%ld, nregs=%ld --> %!I",
(long) (thr->curr_pc - DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun)),
(unsigned long) *thr->curr_pc,
(long) DUK_DEC_OP(*thr->curr_pc),
(long) (thr->valstack_top - thr->valstack),
(long) (thr->valstack_end - thr->valstack),
(long) (fun ? fun->nregs : -1),
(duk_instr_t) *thr->curr_pc));
}
#endif
#if defined(DUK_USE_ASSERTIONS)
/* Quite heavy assert: check that valstack is in correctly
@ -2273,7 +2282,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
}
#endif
ins = bcode[act->pc++];
ins = *thr->curr_pc++;
/* Typing: use duk_small_(u)int_fast_t when decoding small
* opcode fields (op, A, B, C) and duk_(u)int_fast_t when
@ -2532,11 +2541,15 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
* C -> num args (N)
*/
/* Note: duk_new() will call the constuctor using duk_handle_call().
/* duk_new() will call the constuctor using duk_handle_call().
* A constructor call prevents a yield from inside the constructor,
* even if the constructor is an Ecmascript function.
*/
/* Don't need to sync thr->curr_pc here; duk_new() will do that
* when it augments the created error.
*/
/* XXX: unnecessary copying of values? Just set 'top' to
* b + c, and let the return handling fix up the stack frame?
*/
@ -2641,6 +2654,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
case DUK_OP_GETVAR: {
duk_context *ctx = (duk_context *) thr;
duk_activation *act;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk_tval *tv1;
@ -2651,6 +2665,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
name = DUK_TVAL_GET_STRING(tv1);
DUK_ASSERT(name != NULL);
DUK_DDD(DUK_DDDPRINT("GETVAR: '%!O'", (duk_heaphdr *) name));
act = thr->callstack + thr->callstack_top - 1;
(void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */
duk_pop(ctx); /* 'this' binding is not needed here */
@ -2659,6 +2674,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
}
case DUK_OP_PUTVAR: {
duk_activation *act;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk_tval *tv1;
@ -2674,11 +2690,13 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
*/
tv1 = DUK__REGP(a); /* val */
act = thr->callstack + thr->callstack_top - 1;
duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT());
break;
}
case DUK_OP_DECLVAR: {
duk_activation *act;
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_small_uint_fast_t b = DUK_DEC_B(ins);
@ -2713,6 +2731,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
}
tv1 = duk_get_tval(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);
@ -2724,6 +2743,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
}
case DUK_OP_DELVAR: {
duk_activation *act;
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_small_uint_fast_t b = DUK_DEC_B(ins);
@ -2736,6 +2756,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
name = DUK_TVAL_GET_STRING(tv1);
DUK_ASSERT(name != NULL);
DUK_DDD(DUK_DDDPRINT("DELVAR '%!O'", (duk_heaphdr *) name));
act = thr->callstack + thr->callstack_top - 1;
rc = duk_js_delvar_activation(thr, act, name);
duk_push_boolean(ctx, rc);
@ -2755,6 +2776,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
*/
duk_context *ctx = (duk_context *) thr;
duk_activation *act;
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_uint_fast_t idx;
duk_tval *tv1;
@ -2764,6 +2786,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
name = DUK_TVAL_GET_STRING(tv1);
DUK_ASSERT(name != NULL);
act = thr->callstack + thr->callstack_top - 1;
(void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */
/* Note: target registers a and a+1 may overlap with DUK__REGCONSTP(b)
@ -2792,6 +2815,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
case DUK_OP_CLOSURE: {
duk_context *ctx = (duk_context *) thr;
duk_activation *act;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk_hobject *fun_temp;
@ -2812,6 +2836,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
DUK_DDD(DUK_DDDPRINT("CLOSURE: function template is: %p -> %!O",
(void *) fun_temp, (duk_heaphdr *) fun_temp));
act = thr->callstack + thr->callstack_top - 1;
if (act->lex_env == NULL) {
DUK_ASSERT(act->var_env == NULL);
duk_js_init_activation_environment_records_delayed(thr, act);
@ -3139,7 +3164,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
tmp = duk_js_toboolean(DUK__REGCONSTP(b));
if (tmp == (duk_bool_t) a) {
/* if boolean matches A, skip next inst */
act->pc++;
thr->curr_pc++;
} else {
;
}
@ -3149,7 +3174,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
case DUK_OP_JUMP: {
duk_int_fast_t abc = DUK_DEC_ABC(ins);
act->pc += abc - DUK_BC_JUMP_BIAS;
thr->curr_pc += abc - DUK_BC_JUMP_BIAS;
break;
}
@ -3182,6 +3207,7 @@ 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_hthread_sync_currpc(thr); /* must sync explicitly */
goto restart_execution;
}
}
@ -3299,6 +3325,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
* Avoid C recursion by being clever.
*/
DUK_DDD(DUK_DDDPRINT("ecma-to-ecma call setup possible, restart execution"));
/* thr->curr_pc synced by duk_handle_ecma_call_setup() */
goto restart_execution;
}
@ -3378,6 +3405,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
/* When debugger is enabled, we need to recheck the activation
* status after returning.
*/
/* call handling has synced thr->curr_pc */
goto restart_execution;
#endif
break;
@ -3385,6 +3413,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
case DUK_OP_TRYCATCH: {
duk_context *ctx = (duk_context *) thr;
duk_activation *act;
duk_catcher *cat;
duk_tval *tv1;
duk_small_uint_fast_t a;
@ -3431,6 +3460,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
a = DUK_DEC_A(ins);
bc = DUK_DEC_BC(ins);
act = thr->callstack + thr->callstack_top - 1;
DUK_ASSERT(thr->callstack_top >= 1);
/* 'with' target must be created first, in case we run out of memory */
@ -3524,7 +3554,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
cat = thr->catchstack + thr->catchstack_top - 1; /* relookup (side effects) */
cat->callstack_index = thr->callstack_top - 1;
cat->pc_base = act->pc; /* pre-incremented, points to first jump slot */
cat->pc_base = thr->curr_pc; /* pre-incremented, points to first jump slot */
cat->idx_base = (duk_size_t) (thr->valstack_bottom - thr->valstack) + bc;
DUK_DDD(DUK_DDDPRINT("TRYCATCH catcher: flags=0x%08lx, callstack_index=%ld, pc_base=%ld, "
@ -3532,7 +3562,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
(unsigned long) cat->flags, (long) cat->callstack_index,
(long) cat->pc_base, (long) cat->idx_base, (duk_heaphdr *) cat->h_varname));
act->pc += 2; /* skip jump slots */
thr->curr_pc += 2; /* skip jump slots */
break;
}
@ -3624,6 +3654,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
case DUK_OP_POSTINCV:
case DUK_OP_POSTDECV: {
duk_context *ctx = (duk_context *) thr;
duk_activation *act;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk_double_t x, y;
@ -3642,6 +3673,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
name = DUK_TVAL_GET_STRING(tv1);
DUK_ASSERT(name != NULL);
act = thr->callstack + thr->callstack_top - 1;
(void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */
/* XXX: fastint fast path would be very useful here */
@ -3846,6 +3878,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
case DUK_EXTRAOP_TYPEOFID: {
duk_context *ctx = (duk_context *) thr;
duk_activation *act;
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_small_uint_fast_t c = DUK_DEC_C(ins);
duk_hstring *name;
@ -3858,6 +3891,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
tv = DUK__REGCONSTP(c); /* XXX: this could be a DUK__CONSTP instead */
DUK_ASSERT(DUK_TVAL_IS_STRING(tv));
name = DUK_TVAL_GET_STRING(tv);
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);
@ -3928,7 +3962,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
/* [ ... enum ] -> [ ... next_key ] */
DUK_DDD(DUK_DDDPRINT("enum active, next key is %!T, skip jump slot ",
(duk_tval *) duk_get_tval(ctx, -1)));
act->pc++;;
thr->curr_pc++;
} else {
/* [ ... enum ] -> [ ... ] */
DUK_DDD(DUK_DDDPRINT("enum finished, execute jump slot"));
@ -4048,11 +4082,12 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
/* no need to unwind callstack */
}
act->pc = cat->pc_base + 1;
thr->curr_pc = cat->pc_base + 1;
break;
}
case DUK_EXTRAOP_ENDCATCH: {
duk_activation *act;
duk_catcher *cat;
duk_tval tv_tmp;
duk_tval *tv1;
@ -4064,6 +4099,8 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
cat = thr->catchstack + thr->catchstack_top - 1;
DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat)); /* cleared before entering catch part */
act = thr->callstack + thr->callstack_top - 1;
if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) {
duk_hobject *prev_env;
@ -4104,7 +4141,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
/* no need to unwind callstack */
}
act->pc = cat->pc_base + 1;
thr->curr_pc = cat->pc_base + 1;
break;
}
@ -4168,6 +4205,12 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
* break re-throwing for instance.
*/
/* Sync needed before Duktape.errThrow augmentation, not
* strictly needed otherwise: bytecode longjmp handler
* syncs at the latest.
*/
duk_hthread_sync_currpc(thr);
duk_dup(ctx, (duk_idx_t) bc);
DUK_DDD(DUK_DDDPRINT("THROW ERROR (BYTECODE): %!dT (before throw augment)",
(duk_tval *) duk_get_tval(ctx, -1)));
@ -4210,6 +4253,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
DUK_D(DUK_DPRINT("DEBUGGER statement encountered, halt execution"));
if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
DUK_HEAP_SET_PAUSED(thr->heap);
duk_hthread_sync_currpc(thr); /* must sync explicitly */
goto restart_execution;
}
#else
@ -4310,7 +4354,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
cat->flags = DUK_CAT_TYPE_LABEL | (bc << DUK_CAT_LABEL_SHIFT);
cat->callstack_index = thr->callstack_top - 1;
cat->pc_base = act->pc; /* pre-incremented, points to first jump slot */
cat->pc_base = thr->curr_pc; /* pre-incremented, points to first jump slot */
cat->idx_base = 0; /* unused for label */
cat->h_varname = NULL;
@ -4319,7 +4363,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
(long) cat->flags, (long) cat->callstack_index, (long) cat->pc_base,
(long) cat->idx_base, (duk_heaphdr *) cat->h_varname, (long) DUK_CAT_GET_LABEL(cat)));
act->pc += 2; /* skip jump slots */
thr->curr_pc += 2; /* skip jump slots */
break;
}
@ -4344,32 +4388,6 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
break;
}
#ifdef DUK_USE_DEBUG
case DUK_EXTRAOP_DUMPREG: {
DUK_D(DUK_DPRINT("DUMPREG: %ld -> %!T",
(long) DUK_DEC_BC(ins),
(duk_tval *) duk_get_tval((duk_context *) thr, (duk_idx_t) DUK_DEC_BC(ins))));
break;
}
case DUK_EXTRAOP_DUMPREGS: {
duk_idx_t i, i_top;
i_top = duk_get_top((duk_context *) thr);
DUK_D(DUK_DPRINT("DUMPREGS: %ld regs", (long) i_top));
for (i = 0; i < i_top; i++) {
DUK_D(DUK_DPRINT(" r%ld -> %!dT",
(long) i,
(duk_tval *) duk_get_tval((duk_context *) thr, i)));
}
break;
}
case DUK_EXTRAOP_LOGMARK: {
DUK_D(DUK_DPRINT("LOGMARK: mark %ld at pc %ld", (long) DUK_DEC_BC(ins), (long) (act->pc - 1))); /* -1, autoinc */
break;
}
#endif /* DUK_USE_DEBUG */
default: {
DUK__INTERNAL_ERROR("invalid extra opcode");
}

Loading…
Cancel
Save