Browse Source

Use local variable "curr_pc" for opcode dispatch

This is slightly faster than using thr->curr_pc.
add-comment-stripped-dist-source
Sami Vaarala 9 years ago
parent
commit
c23916fcd8
  1. 1
      src/duk_hobject_alloc.c
  2. 14
      src/duk_hthread.h
  3. 13
      src/duk_hthread_misc.c
  4. 32
      src/duk_js_call.c
  5. 60
      src/duk_js_executor.c

1
src/duk_hobject_alloc.c

@ -147,6 +147,7 @@ DUK_INTERNAL duk_hthread *duk_hthread_alloc(duk_heap *heap, duk_uint_t hobject_f
duk__init_object_parts(heap, &res->obj, hobject_flags);
#ifdef DUK_USE_EXPLICIT_NULL_INIT
res->ptr_curr_pc = NULL;
res->heap = NULL;
res->valstack = NULL;
res->valstack_end = NULL;

14
src/duk_hthread.h

@ -241,12 +241,16 @@ struct duk_hthread {
/* 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.
/* Pointer to bytecode executor's 'curr_pc' variable. Used to copy
* the current PC back into the topmost activation when activation
* state is about to change (or "syncing" is otherwise needed). This
* is rather awkward but important for performance, see execution.rst.
*
* The pointer here is volatile, and the target pointer is volatile:
* cdecl> explain int * volatile * volatile curr_pc
* declare curr_pc as volatile pointer to volatile pointer to int
*/
duk_instr_t *curr_pc;
duk_instr_t * volatile * volatile ptr_curr_pc;
/* backpointers */
duk_heap *heap;

13
src/duk_hthread_misc.c

@ -79,19 +79,16 @@ DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk
return 0;
}
/* Write thr->curr_pc back to topmost activation (if any). */
/* Write bytecode executor's 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.
*/
if (thr->ptr_curr_pc != NULL) {
/* ptr_curr_pc != NULL only when bytecode executor is active. */
DUK_ASSERT(thr->callstack_top > 0);
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;
act->curr_pc = (duk_instr_t *) *thr->ptr_curr_pc;
}
}

32
src/duk_js_call.c

@ -788,11 +788,11 @@ void duk__adjust_valstack_and_top(duk_hthread *thr, duk_idx_t num_stack_args, du
* for errhandler calls
* DUK_CALL_FLAG_CONSTRUCTOR_CALL <--> for 'new Foo()' calls
*
* Input stack:
* Input stack (thr):
*
* [ func this arg1 ... argN ]
*
* Output stack:
* Output stack (thr):
*
* [ retval ] (DUK_EXEC_SUCCESS)
* [ errobj ] (DUK_EXEC_ERROR (normal error), protected call)
@ -832,7 +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;
duk_instr_t * volatile * volatile entry_ptr_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) */
@ -874,10 +874,8 @@ 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;
}
entry_ptr_curr_pc = thr->ptr_curr_pc; /* may be NULL */
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) */
@ -1006,6 +1004,9 @@ duk_int_t duk_handle_call(duk_hthread *thr,
DUK_DDD(DUK_DDDPRINT("call is not protected -> clean up and rethrow"));
/* Restore entry thread executor curr_pc stack frame pointer. */
thr->ptr_curr_pc = entry_ptr_curr_pc;
DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */
thr->state = entry_thread_state;
DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) || /* first call */
@ -1370,7 +1371,8 @@ duk_int_t duk_handle_call(duk_hthread *thr,
* other invalid
*/
thr->curr_pc = NULL;
/* For native calls must be NULL so we don't sync back */
thr->ptr_curr_pc = NULL;
if (func) {
rc = ((duk_hnativefunction *) func)->func((duk_context *) thr);
@ -1483,6 +1485,7 @@ duk_int_t duk_handle_call(duk_hthread *thr,
*
*/
/* thr->ptr_curr_pc is set by bytecode executor early on entry */
DUK_DDD(DUK_DDDPRINT("entering bytecode execution"));
duk_js_execute_bytecode(thr);
DUK_DDD(DUK_DDDPRINT("returned from bytecode execution"));
@ -1582,12 +1585,8 @@ 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;
}
/* Restore entry thread executor curr_pc stack frame pointer. */
thr->ptr_curr_pc = entry_ptr_curr_pc;
DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */
thr->state = (duk_uint8_t) entry_thread_state;
@ -1721,6 +1720,7 @@ duk_int_t duk_handle_safe_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 * volatile * volatile entry_ptr_curr_pc;
duk_jmpbuf *old_jmpbuf_ptr = NULL;
duk_jmpbuf our_jmpbuf;
duk_tval tv_tmp;
@ -1738,6 +1738,7 @@ duk_int_t duk_handle_safe_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_ptr_curr_pc = thr->ptr_curr_pc; /* may be NULL */
idx_retbase = duk_get_top(ctx) - num_stack_args; /* Note: not a valid stack index if num_stack_args == 0 */
/* Note: cannot portably debug print a function pointer, hence 'func' not printed! */
@ -1957,6 +1958,9 @@ duk_int_t duk_handle_safe_call(duk_hthread *thr,
DUK_DDD(DUK_DDDPRINT("setjmp catchpoint torn down"));
/* Restore entry thread executor curr_pc stack frame pointer. */
thr->ptr_curr_pc = entry_ptr_curr_pc;
/* XXX: because we unwind stacks above, thr->heap->curr_thread is at
* risk of pointing to an already freed thread. This was indeed the
* case in test-bug-multithread-valgrind.c, until duk_handle_call()

60
src/duk_js_executor.c

@ -1970,6 +1970,14 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
volatile duk_int_t entry_call_recursion_depth;
duk_jmpbuf * volatile entry_jmpbuf_ptr;
/* current PC, volatile because it is accessed by other functions
* through thr->ptr_to_curr_pc. Critical for performance.
*
* cdecl> explain int * volatile curr_pc
* declare curr_pc as volatile pointer to int
*/
duk_instr_t * volatile curr_pc; /* stable */
/* "hot" variables for interpretation -- not volatile, value not guaranteed in setjmp error handling */
duk_hthread *thr; /* stable */
duk_hcompiledfunction *fun; /* stable */
@ -2112,8 +2120,8 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
* 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.
* ensure 'curr_pc' is synced back to act->curr_pc before the goto
* takes place.
*/
restart_execution:
@ -2128,6 +2136,8 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
thr->ptr_curr_pc = (duk_instr_t * volatile * volatile) &curr_pc;
/* Assume interrupt init/counter are properly initialized here. */
/* assume that thr->valstack_bottom has been set-up before getting here */
@ -2204,11 +2214,11 @@ 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. */
/* Set up curr_pc for opcode dispatch. */
{
duk_activation *act;
act = thr->callstack + thr->callstack_top - 1;
thr->curr_pc = act->curr_pc;
curr_pc = act->curr_pc;
}
for (;;) {
@ -2235,12 +2245,12 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
{
duk_activation *act;
act = thr->callstack + thr->callstack_top - 1;
act->curr_pc = thr->curr_pc;
act->curr_pc = (duk_instr_t *) curr_pc;
}
exec_int_ret = duk__executor_interrupt(thr);
if (exec_int_ret == DUK__INT_RESTART) {
/* thr->curr_pc synced back above */
/* curr_pc synced back above */
goto restart_execution;
}
}
@ -2253,18 +2263,18 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
{
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_ASSERT(curr_pc >= DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun));
DUK_ASSERT(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) (curr_pc - DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun)),
(unsigned long) *curr_pc,
(long) DUK_DEC_OP(*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));
(duk_instr_t) *curr_pc));
}
#endif
@ -2284,7 +2294,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
}
#endif
ins = *thr->curr_pc++;
ins = *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
@ -2548,7 +2558,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
* even if the constructor is an Ecmascript function.
*/
/* Don't need to sync thr->curr_pc here; duk_new() will do that
/* Don't need to sync curr_pc here; duk_new() will do that
* when it augments the created error.
*/
@ -3166,7 +3176,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 */
thr->curr_pc++;
curr_pc++;
} else {
;
}
@ -3176,7 +3186,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);
thr->curr_pc += abc - DUK_BC_JUMP_BIAS;
curr_pc += abc - DUK_BC_JUMP_BIAS;
break;
}
@ -3327,7 +3337,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() */
/* curr_pc synced by duk_handle_ecma_call_setup() */
goto restart_execution;
}
@ -3407,7 +3417,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 */
/* call handling has synced curr_pc */
goto restart_execution;
#endif
break;
@ -3556,7 +3566,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 = thr->curr_pc; /* pre-incremented, points to first jump slot */
cat->pc_base = (duk_instr_t *) 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, "
@ -3564,7 +3574,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));
thr->curr_pc += 2; /* skip jump slots */
curr_pc += 2; /* skip jump slots */
break;
}
@ -3964,7 +3974,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)));
thr->curr_pc++;
curr_pc++;
} else {
/* [ ... enum ] -> [ ... ] */
DUK_DDD(DUK_DDDPRINT("enum finished, execute jump slot"));
@ -4084,7 +4094,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
/* no need to unwind callstack */
}
thr->curr_pc = cat->pc_base + 1;
curr_pc = cat->pc_base + 1;
break;
}
@ -4143,7 +4153,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
/* no need to unwind callstack */
}
thr->curr_pc = cat->pc_base + 1;
curr_pc = cat->pc_base + 1;
break;
}
@ -4356,7 +4366,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 = thr->curr_pc; /* pre-incremented, points to first jump slot */
cat->pc_base = (duk_instr_t *) curr_pc; /* pre-incremented, points to first jump slot */
cat->idx_base = 0; /* unused for label */
cat->h_varname = NULL;
@ -4365,7 +4375,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)));
thr->curr_pc += 2; /* skip jump slots */
curr_pc += 2; /* skip jump slots */
break;
}

Loading…
Cancel
Save