ruby/vm.c

3387 строки
88 KiB
C
Исходник Обычный вид История

/**********************************************************************
vm.c -
$Author$
Copyright (C) 2004-2007 Koichi Sasada
**********************************************************************/
#include "internal.h"
#include "ruby/vm.h"
#include "ruby/st.h"
#include "gc.h"
#include "vm_core.h"
#include "iseq.h"
#include "eval_intern.h"
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-13 01:52:12 +04:00
#include "probes.h"
#include "probes_helper.h"
VALUE rb_str_concat_literals(size_t, const VALUE*);
PUREFUNC(static inline const VALUE *VM_EP_LEP(const VALUE *));
static inline const VALUE *
VM_EP_LEP(const VALUE *ep)
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
{
while (!VM_ENV_LOCAL_P(ep)) {
ep = VM_ENV_PREV_EP(ep);
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
}
return ep;
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
}
static inline const rb_control_frame_t *
rb_vm_search_cf_from_ep(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE * const ep)
{
if (!ep) {
return NULL;
}
else {
const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
while (cfp < eocfp) {
if (cfp->ep == ep) {
return cfp;
}
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
return NULL;
}
}
const VALUE *
rb_vm_ep_local_ep(const VALUE *ep)
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
{
return VM_EP_LEP(ep);
}
PUREFUNC(static inline const VALUE *VM_CF_LEP(const rb_control_frame_t * const cfp));
static inline const VALUE *
VM_CF_LEP(const rb_control_frame_t * const cfp)
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
{
return VM_EP_LEP(cfp->ep);
}
static inline const VALUE *
VM_CF_PREV_EP(const rb_control_frame_t * const cfp)
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
{
return VM_ENV_PREV_EP(cfp->ep);
}
PUREFUNC(static inline VALUE VM_CF_BLOCK_HANDLER(const rb_control_frame_t * const cfp));
static inline VALUE
VM_CF_BLOCK_HANDLER(const rb_control_frame_t * const cfp)
{
const VALUE *ep = VM_CF_LEP(cfp);
return VM_ENV_BLOCK_HANDLER(ep);
}
VALUE
rb_vm_frame_block_handler(const rb_control_frame_t *cfp)
{
return VM_CF_BLOCK_HANDLER(cfp);
}
#if VM_CHECK_MODE > 0
static int
VM_CFP_IN_HEAP_P(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
const VALUE *start = ec->vm_stack;
const VALUE *end = (VALUE *)ec->vm_stack + ec->vm_stack_size;
VM_ASSERT(start != NULL);
if (start <= (VALUE *)cfp && (VALUE *)cfp < end) {
return FALSE;
}
else {
return TRUE;
}
}
static int
VM_EP_IN_HEAP_P(const rb_execution_context_t *ec, const VALUE *ep)
{
const VALUE *start = ec->vm_stack;
const VALUE *end = (VALUE *)ec->cfp;
VM_ASSERT(start != NULL);
if (start <= ep && ep < end) {
return FALSE;
}
else {
return TRUE;
}
}
int
vm_ep_in_heap_p_(const rb_execution_context_t *ec, const VALUE *ep)
{
if (VM_EP_IN_HEAP_P(ec, ep)) {
VALUE envval = ep[VM_ENV_DATA_INDEX_ENV]; /* VM_ENV_ENVVAL(ep); */
if (envval != Qundef) {
const rb_env_t *env = (const rb_env_t *)envval;
VM_ASSERT(vm_assert_env(envval));
VM_ASSERT(VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED));
VM_ASSERT(env->ep == ep);
}
return TRUE;
}
else {
return FALSE;
}
}
int
rb_vm_ep_in_heap_p(const VALUE *ep)
{
const rb_execution_context_t *ec = GET_EC();
if (ec->vm_stack == NULL) return TRUE;
return vm_ep_in_heap_p_(ec, ep);
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
}
#endif
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
static struct rb_captured_block *
VM_CFP_TO_CAPTURED_BLOCK(const rb_control_frame_t *cfp)
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
{
VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_EC(), cfp));
return (struct rb_captured_block *)&cfp->self;
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
}
static rb_control_frame_t *
VM_CAPTURED_BLOCK_TO_CFP(const struct rb_captured_block *captured)
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
{
rb_control_frame_t *cfp = ((rb_control_frame_t *)((VALUE *)(captured) - 3));
VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_EC(), cfp));
VM_ASSERT(sizeof(rb_control_frame_t)/sizeof(VALUE) == 6 + VM_DEBUG_BP_CHECK ? 1 : 0);
return cfp;
}
static int
VM_BH_FROM_CFP_P(VALUE block_handler, const rb_control_frame_t *cfp)
{
const struct rb_captured_block *captured = VM_CFP_TO_CAPTURED_BLOCK(cfp);
return VM_TAGGED_PTR_REF(block_handler, 0x03) == captured;
}
static VALUE
vm_passed_block_handler(rb_execution_context_t *ec)
{
VALUE block_handler = ec->passed_block_handler;
ec->passed_block_handler = VM_BLOCK_HANDLER_NONE;
vm_block_handler_verify(block_handler);
return block_handler;
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
}
static rb_cref_t *
vm_cref_new0(VALUE klass, rb_method_visibility_t visi, int module_func, rb_cref_t *prev_cref, int pushed_by_eval, int use_prev_prev)
{
VALUE refinements = Qnil;
int omod_shared = FALSE;
rb_cref_t *cref;
/* scope */
union {
rb_scope_visibility_t visi;
VALUE value;
} scope_visi;
scope_visi.visi.method_visi = visi;
scope_visi.visi.module_func = module_func;
/* refinements */
if (prev_cref != NULL && prev_cref != (void *)1 /* TODO: why CREF_NEXT(cref) is 1? */) {
refinements = CREF_REFINEMENTS(prev_cref);
if (!NIL_P(refinements)) {
omod_shared = TRUE;
CREF_OMOD_SHARED_SET(prev_cref);
}
}
cref = (rb_cref_t *)rb_imemo_new(imemo_cref, klass, (VALUE)(use_prev_prev ? CREF_NEXT(prev_cref) : prev_cref), scope_visi.value, refinements);
if (pushed_by_eval) CREF_PUSHED_BY_EVAL_SET(cref);
if (omod_shared) CREF_OMOD_SHARED_SET(cref);
return cref;
}
static rb_cref_t *
vm_cref_new(VALUE klass, rb_method_visibility_t visi, int module_func, rb_cref_t *prev_cref, int pushed_by_eval)
{
return vm_cref_new0(klass, visi, module_func, prev_cref, pushed_by_eval, FALSE);
}
static rb_cref_t *
vm_cref_new_use_prev(VALUE klass, rb_method_visibility_t visi, int module_func, rb_cref_t *prev_cref, int pushed_by_eval)
{
return vm_cref_new0(klass, visi, module_func, prev_cref, pushed_by_eval, TRUE);
}
static rb_cref_t *
vm_cref_dup(const rb_cref_t *cref)
{
VALUE klass = CREF_CLASS(cref);
const rb_scope_visibility_t *visi = CREF_SCOPE_VISI(cref);
rb_cref_t *next_cref = CREF_NEXT(cref), *new_cref;
int pushed_by_eval = CREF_PUSHED_BY_EVAL(cref);
new_cref = vm_cref_new(klass, visi->method_visi, visi->module_func, next_cref, pushed_by_eval);
if (!NIL_P(CREF_REFINEMENTS(cref))) {
CREF_REFINEMENTS_SET(new_cref, rb_hash_dup(CREF_REFINEMENTS(cref)));
CREF_OMOD_SHARED_UNSET(new_cref);
}
return new_cref;
}
static rb_cref_t *
vm_cref_new_toplevel(rb_execution_context_t *ec)
{
rb_cref_t *cref = vm_cref_new(rb_cObject, METHOD_VISI_PRIVATE /* toplevel visibility is private */, FALSE, NULL, FALSE);
VALUE top_wrapper = rb_ec_thread_ptr(ec)->top_wrapper;
if (top_wrapper) {
cref = vm_cref_new(top_wrapper, METHOD_VISI_PRIVATE, FALSE, cref, FALSE);
}
return cref;
}
rb_cref_t *
rb_vm_cref_new_toplevel(void)
{
return vm_cref_new_toplevel(GET_EC());
}
static void
vm_cref_dump(const char *mesg, const rb_cref_t *cref)
{
fprintf(stderr, "vm_cref_dump: %s (%p)\n", mesg, cref);
while (cref) {
fprintf(stderr, "= cref| klass: %s\n", RSTRING_PTR(rb_class_path(CREF_CLASS(cref))));
cref = CREF_NEXT(cref);
}
}
void
rb_vm_block_ep_update(VALUE obj, const struct rb_block *dst, const VALUE *ep)
{
*((const VALUE **)&dst->as.captured.ep) = ep;
RB_OBJ_WRITTEN(obj, Qundef, VM_ENV_ENVVAL(ep));
}
static void
vm_bind_update_env(VALUE bindval, rb_binding_t *bind, VALUE envval)
{
const rb_env_t *env = (rb_env_t *)envval;
RB_OBJ_WRITE(bindval, &bind->block.as.captured.code.iseq, env->iseq);
rb_vm_block_ep_update(bindval, &bind->block, env->ep);
}
#if VM_COLLECT_USAGE_DETAILS
static void vm_collect_usage_operand(int insn, int n, VALUE op);
static void vm_collect_usage_insn(int insn);
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-13 01:52:12 +04:00
static void vm_collect_usage_register(int reg, int isset);
#endif
static VALUE vm_make_env_object(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
static VALUE vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, VALUE block_handler);
static VALUE vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, VALUE block_handler);
#include "vm_insnhelper.h"
#include "vm_exec.h"
#include "vm_insnhelper.c"
#include "vm_exec.c"
#include "vm_method.c"
#include "vm_eval.c"
#define PROCDEBUG 0
rb_serial_t
rb_next_class_serial(void)
{
return NEXT_CLASS_SERIAL();
}
VALUE rb_cRubyVM;
VALUE rb_cThread;
VALUE rb_mRubyVMFrozenCore;
#define ruby_vm_redefined_flag GET_VM()->redefined_flag
VALUE ruby_vm_const_missing_count = 0;
rb_vm_t *ruby_current_vm_ptr = NULL;
rb_execution_context_t *ruby_current_execution_context_ptr = NULL;
* vm_trace.c, vm_core.h: simplify tracing mechanism. (1) add rb_hook_list_t data structure which includes hooks, events (flag) and `need_clean' flag. If the last flag is true, then clean the hooks list. In other words, deleted hooks are contained by `hooks'. Cleanup process should run before traversing the list. (2) Change check mechanism See EXEC_EVENT_HOOK() in vm_core.h. (3) Add `raw' hooks APIs Normal hooks are guarded from exception by rb_protect(). However, this protection is overhead for too simple functions which never cause exceptions. `raw' hooks are executed without protection and faster. Now, we only provide registration APIs. All `raw' hooks are kicked under protection (same as normal hooks). * include/ruby/ruby.h: remove internal data definition and macros. * internal.h (ruby_suppress_tracing), vm_trace.c: rename ruby_suppress_tracing() to rb_suppress_tracing() and remove unused function parameter. * parse.y: fix to use renamed rb_suppress_tracing(). * thread.c (thread_create_core): no need to set RUBY_VM_VM. * vm.c (mark_event_hooks): move definition to vm_trace.c. * vm.c (ruby_vm_event_flags): add a global variable. This global variable represents all of Threads and VM's event masks (T1#events | T2#events | ... | VM#events). You can check the possibility kick trace func or not with ruby_vm_event_flags. ruby_vm_event_flags is maintained by vm_trace.c. * cont.c (fiber_switch, rb_cont_call): restore tracing status. [Feature #4347] * test/ruby/test_continuation.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36715 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-08-16 15:41:24 +04:00
rb_event_flag_t ruby_vm_event_flags;
rb_event_flag_t ruby_vm_event_enabled_flags;
rb_serial_t ruby_vm_global_method_state = 1;
rb_serial_t ruby_vm_global_constant_state = 1;
rb_serial_t ruby_vm_class_serial = 1;
static void thread_free(void *ptr);
void
rb_vm_inc_const_missing_count(void)
{
ruby_vm_const_missing_count +=1;
}
VALUE rb_class_path_no_cache(VALUE _klass);
int
rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id,
struct ruby_dtrace_method_hook_args *args)
{
enum ruby_value_type type;
if (!klass) {
if (!ec) ec = GET_EC();
if (!rb_ec_frame_method_id_and_class(ec, &id, 0, &klass) || !klass)
return FALSE;
}
if (RB_TYPE_P(klass, T_ICLASS)) {
klass = RBASIC(klass)->klass;
}
else if (FL_TEST(klass, FL_SINGLETON)) {
klass = rb_attr_get(klass, id__attached__);
if (NIL_P(klass)) return FALSE;
}
type = BUILTIN_TYPE(klass);
if (type == T_CLASS || type == T_ICLASS || type == T_MODULE) {
VALUE name = rb_class_path_no_cache(klass);
const char *classname, *filename;
const char *methodname = rb_id2name(id);
if (methodname && (filename = rb_source_location_cstr(&args->line_no)) != 0) {
if (NIL_P(name) || !(classname = StringValuePtr(name)))
classname = "<unknown>";
args->classname = classname;
args->methodname = methodname;
args->filename = filename;
args->klass = klass;
args->name = name;
return TRUE;
}
}
return FALSE;
}
/*
* call-seq:
* RubyVM.stat -> Hash
* RubyVM.stat(hsh) -> hsh
* RubyVM.stat(Symbol) -> Numeric
*
* Returns a Hash containing implementation-dependent counters inside the VM.
*
* This hash includes information about method/constant cache serials:
*
* {
* :global_method_state=>251,
* :global_constant_state=>481,
* :class_serial=>9029
* }
*
* The contents of the hash are implementation specific and may be changed in
* the future.
*
* This method is only expected to work on C Ruby.
*/
static VALUE
vm_stat(int argc, VALUE *argv, VALUE self)
{
static VALUE sym_global_method_state, sym_global_constant_state, sym_class_serial;
VALUE arg = Qnil;
VALUE hash = Qnil, key = Qnil;
if (rb_scan_args(argc, argv, "01", &arg) == 1) {
if (SYMBOL_P(arg))
key = arg;
else if (RB_TYPE_P(arg, T_HASH))
hash = arg;
else
rb_raise(rb_eTypeError, "non-hash or symbol given");
}
else {
hash = rb_hash_new();
}
if (sym_global_method_state == 0) {
#define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
S(global_method_state);
S(global_constant_state);
S(class_serial);
#undef S
}
#define SET(name, attr) \
if (key == sym_##name) \
return SERIALT2NUM(attr); \
else if (hash != Qnil) \
rb_hash_aset(hash, sym_##name, SERIALT2NUM(attr));
SET(global_method_state, ruby_vm_global_method_state);
SET(global_constant_state, ruby_vm_global_constant_state);
SET(class_serial, ruby_vm_class_serial);
#undef SET
if (!NIL_P(key)) { /* matched key should return above */
rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key));
}
return hash;
}
/* control stack frame */
static void
vm_set_top_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq)
{
2015-07-22 01:52:59 +03:00
if (iseq->body->type != ISEQ_TYPE_TOP) {
rb_raise(rb_eTypeError, "Not a toplevel InstructionSequence");
}
/* for return */
vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH, rb_ec_thread_ptr(ec)->top_self,
VM_BLOCK_HANDLER_NONE,
(VALUE)vm_cref_new_toplevel(ec), /* cref or me */
iseq->body->iseq_encoded, ec->cfp->sp,
iseq->body->local_table_size, iseq->body->stack_max);
}
static void
vm_set_eval_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_cref_t *cref, const struct rb_block *base_block)
{
vm_push_frame(ec, iseq, VM_FRAME_MAGIC_EVAL | VM_FRAME_FLAG_FINISH,
vm_block_self(base_block), VM_GUARDED_PREV_EP(vm_block_ep(base_block)),
(VALUE)cref, /* cref or me */
2015-07-22 01:52:59 +03:00
iseq->body->iseq_encoded,
ec->cfp->sp, iseq->body->local_table_size,
iseq->body->stack_max);
}
static void
vm_set_main_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq)
{
VALUE toplevel_binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
rb_binding_t *bind;
GetBindingPtr(toplevel_binding, bind);
RUBY_ASSERT_MESG(bind, "TOPLEVEL_BINDING is not built");
2015-07-22 01:52:59 +03:00
vm_set_eval_stack(ec, iseq, 0, &bind->block);
/* save binding */
if (iseq->body->local_table_size > 0) {
vm_bind_update_env(toplevel_binding, bind, vm_make_env_object(ec, ec->cfp));
}
}
rb_control_frame_t *
rb_vm_get_binding_creatable_next_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
if (cfp->iseq) {
return (rb_control_frame_t *)cfp;
}
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
return 0;
}
rb_control_frame_t *
rb_vm_get_ruby_level_next_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) bp();
while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
if (VM_FRAME_RUBYFRAME_P(cfp)) {
return (rb_control_frame_t *)cfp;
}
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
return 0;
}
static rb_control_frame_t *
vm_get_ruby_level_caller_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
if (VM_FRAME_RUBYFRAME_P(cfp)) {
return (rb_control_frame_t *)cfp;
}
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
if (VM_FRAME_RUBYFRAME_P(cfp)) {
return (rb_control_frame_t *)cfp;
}
if (VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_PASSED) == FALSE) {
break;
}
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
return 0;
}
void
rb_vm_pop_cfunc_frame(void)
{
rb_execution_context_t *ec = GET_EC();
rb_control_frame_t *cfp = ec->cfp;
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, cfp->self, me->def->original_id, me->called_id, me->owner, Qnil);
RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
vm_pop_frame(ec, cfp, cfp->ep);
}
void
rb_vm_rewind_cfp(rb_execution_context_t *ec, rb_control_frame_t *cfp)
{
/* check skipped frame */
while (ec->cfp != cfp) {
#if VMDEBUG
printf("skipped frame: %s\n", vm_frametype_name(ec->cfp));
#endif
if (VM_FRAME_TYPE(ec->cfp) != VM_FRAME_MAGIC_CFUNC) {
rb_vm_pop_frame(ec);
}
else { /* unlikely path */
rb_vm_pop_cfunc_frame();
}
}
}
/* at exit */
void
ruby_vm_at_exit(void (*func)(rb_vm_t *))
{
rb_vm_t *vm = GET_VM();
rb_at_exit_list *nl = ALLOC(rb_at_exit_list);
nl->func = func;
nl->next = vm->at_exit;
vm->at_exit = nl;
}
static void
ruby_vm_run_at_exit_hooks(rb_vm_t *vm)
{
rb_at_exit_list *l = vm->at_exit;
while (l) {
rb_at_exit_list* t = l->next;
rb_vm_at_exit_func *func = l->func;
ruby_xfree(l);
l = t;
(*func)(vm);
}
}
/* Env */
static VALUE check_env_value(const rb_env_t *env);
static int
check_env(const rb_env_t *env)
{
fprintf(stderr, "---\n");
fprintf(stderr, "envptr: %p\n", (void *)&env->ep[0]);
fprintf(stderr, "envval: %10p ", (void *)env->ep[1]);
dp(env->ep[1]);
fprintf(stderr, "ep: %10p\n", (void *)env->ep);
if (rb_vm_env_prev_env(env)) {
fprintf(stderr, ">>\n");
check_env_value(rb_vm_env_prev_env(env));
fprintf(stderr, "<<\n");
}
return 1;
}
static VALUE
check_env_value(const rb_env_t *env)
{
if (check_env(env)) {
return (VALUE)env;
}
rb_bug("invalid env");
return Qnil; /* unreachable */
}
static void
vm_block_handler_escape(const rb_execution_context_t *ec, VALUE block_handler, VALUE *procvalptr)
{
switch (vm_block_handler_type(block_handler)) {
case block_handler_type_ifunc:
case block_handler_type_iseq:
*procvalptr = rb_vm_make_proc(ec, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
return;
case block_handler_type_symbol:
case block_handler_type_proc:
*procvalptr = block_handler;
return;
}
VM_UNREACHABLE(vm_block_handler_escape);
return;
}
static VALUE
vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *const cfp)
{
VALUE blockprocval = Qfalse;
const VALUE * const ep = cfp->ep;
const rb_env_t *env;
const rb_iseq_t *env_iseq;
VALUE *env_body, *env_ep;
int local_size, env_size;
if (VM_ENV_ESCAPED_P(ep)) {
return VM_ENV_ENVVAL(ep);
}
if (!VM_ENV_LOCAL_P(ep)) {
const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
if (!VM_ENV_ESCAPED_P(prev_ep)) {
rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
while (prev_cfp->ep != prev_ep) {
prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(prev_cfp);
VM_ASSERT(prev_cfp->ep != NULL);
}
vm_make_env_each(ec, prev_cfp);
VM_FORCE_WRITE_SPECIAL_CONST(&ep[VM_ENV_DATA_INDEX_SPECVAL], VM_GUARDED_PREV_EP(prev_cfp->ep));
}
}
else {
VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep);
if (block_handler != VM_BLOCK_HANDLER_NONE) {
vm_block_handler_escape(ec, block_handler, &blockprocval);
VM_STACK_ENV_WRITE(ep, VM_ENV_DATA_INDEX_SPECVAL, blockprocval);
}
}
if (!VM_FRAME_RUBYFRAME_P(cfp)) {
local_size = VM_ENV_DATA_SIZE;
}
else {
local_size = cfp->iseq->body->local_table_size + VM_ENV_DATA_SIZE;
}
/*
* # local variables on a stack frame (N == local_size)
* [lvar1, lvar2, ..., lvarN, SPECVAL]
* ^
* ep[0]
*
* # moved local variables
* [lvar1, lvar2, ..., lvarN, SPECVAL, Envval, BlockProcval (if needed)]
* ^ ^
* env->env[0] ep[0]
*/
env_size = local_size +
1 /* envval */ +
(blockprocval ? 1 : 0) /* blockprocval */;
env_body = ALLOC_N(VALUE, env_size);
MEMCPY(env_body, ep - (local_size - 1 /* specval */), VALUE, local_size);
#if 0
for (i = 0; i < local_size; i++) {
if (VM_FRAME_RUBYFRAME_P(cfp)) {
/* clear value stack for GC */
ep[-local_size + i] = 0;
}
}
#endif
env_iseq = VM_FRAME_RUBYFRAME_P(cfp) ? cfp->iseq : NULL;
env_ep = &env_body[local_size - 1 /* specval */];
env = vm_env_new(env_ep, env_body, env_size, env_iseq);
if (blockprocval) RB_OBJ_WRITE(env, &env_ep[2], blockprocval);
cfp->ep = env_ep;
VM_ENV_FLAGS_SET(env_ep, VM_ENV_FLAG_ESCAPED | VM_ENV_FLAG_WB_REQUIRED);
VM_STACK_ENV_WRITE(ep, 0, (VALUE)env); /* GC mark */
return (VALUE)env;
}
static VALUE
vm_make_env_object(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
{
VALUE envval = vm_make_env_each(ec, cfp);
if (PROCDEBUG) {
check_env_value((const rb_env_t *)envval);
}
return envval;
}
void
rb_vm_stack_to_heap(rb_execution_context_t *ec)
{
rb_control_frame_t *cfp = ec->cfp;
while ((cfp = rb_vm_get_binding_creatable_next_cfp(ec, cfp)) != 0) {
vm_make_env_object(ec, cfp);
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
}
const rb_env_t *
rb_vm_env_prev_env(const rb_env_t *env)
{
const VALUE *ep = env->ep;
if (VM_ENV_LOCAL_P(ep)) {
return NULL;
}
else {
return VM_ENV_ENVVAL_PTR(VM_ENV_PREV_EP(ep));
}
}
static int
collect_local_variables_in_iseq(const rb_iseq_t *iseq, const struct local_var_list *vars)
{
unsigned int i;
if (!iseq) return 0;
2015-07-22 01:52:59 +03:00
for (i = 0; i < iseq->body->local_table_size; i++) {
local_var_list_add(vars, iseq->body->local_table[i]);
}
return 1;
}
static void
collect_local_variables_in_env(const rb_env_t *env, const struct local_var_list *vars)
{
do {
collect_local_variables_in_iseq(env->iseq, vars);
} while ((env = rb_vm_env_prev_env(env)) != NULL);
}
static int
vm_collect_local_variables_in_heap(const VALUE *ep, const struct local_var_list *vars)
{
if (VM_ENV_ESCAPED_P(ep)) {
collect_local_variables_in_env(VM_ENV_ENVVAL_PTR(ep), vars);
return 1;
}
else {
return 0;
}
}
VALUE
rb_vm_env_local_variables(const rb_env_t *env)
{
struct local_var_list vars;
local_var_list_init(&vars);
collect_local_variables_in_env(env, &vars);
return local_var_list_finish(&vars);
}
VALUE
rb_iseq_local_variables(const rb_iseq_t *iseq)
{
struct local_var_list vars;
local_var_list_init(&vars);
while (collect_local_variables_in_iseq(iseq, &vars)) {
iseq = iseq->body->parent_iseq;
}
return local_var_list_finish(&vars);
}
/* Proc */
static VALUE
vm_proc_create_from_captured(VALUE klass,
const struct rb_captured_block *captured,
enum rb_block_type block_type,
int8_t safe_level, int8_t is_from_method, int8_t is_lambda)
{
VALUE procval = rb_proc_alloc(klass);
rb_proc_t *proc = RTYPEDDATA_DATA(procval);
VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), captured->ep));
/* copy block */
RB_OBJ_WRITE(procval, &proc->block.as.captured.self, captured->self);
RB_OBJ_WRITE(procval, &proc->block.as.captured.code.val, captured->code.val);
rb_vm_block_ep_update(procval, &proc->block, captured->ep);
vm_block_type_set(&proc->block, block_type);
proc->safe_level = safe_level;
proc->is_from_method = is_from_method;
proc->is_lambda = is_lambda;
return procval;
}
void
rb_vm_block_copy(VALUE obj, const struct rb_block *dst, const struct rb_block *src)
{
/* copy block */
switch (vm_block_type(src)) {
case block_type_iseq:
case block_type_ifunc:
RB_OBJ_WRITE(obj, &dst->as.captured.self, src->as.captured.self);
RB_OBJ_WRITE(obj, &dst->as.captured.code.val, src->as.captured.code.val);
rb_vm_block_ep_update(obj, dst, src->as.captured.ep);
break;
case block_type_symbol:
RB_OBJ_WRITE(obj, &dst->as.symbol, src->as.symbol);
break;
case block_type_proc:
RB_OBJ_WRITE(obj, &dst->as.proc, src->as.proc);
break;
}
}
VALUE
rb_proc_create(VALUE klass, const struct rb_block *block,
int8_t safe_level, int8_t is_from_method, int8_t is_lambda)
{
VALUE procval = rb_proc_alloc(klass);
rb_proc_t *proc = RTYPEDDATA_DATA(procval);
VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), vm_block_ep(block)));
rb_vm_block_copy(procval, &proc->block, block);
vm_block_type_set(&proc->block, block->type);
proc->safe_level = safe_level;
proc->is_from_method = is_from_method;
proc->is_lambda = is_lambda;
return procval;
}
VALUE
rb_vm_make_proc_lambda(const rb_execution_context_t *ec, const struct rb_captured_block *captured, VALUE klass, int8_t is_lambda)
{
VALUE procval;
if (!VM_ENV_ESCAPED_P(captured->ep)) {
rb_control_frame_t *cfp = VM_CAPTURED_BLOCK_TO_CFP(captured);
vm_make_env_object(ec, cfp);
}
VM_ASSERT(VM_EP_IN_HEAP_P(ec, captured->ep));
VM_ASSERT(imemo_type_p(captured->code.val, imemo_iseq) ||
imemo_type_p(captured->code.val, imemo_ifunc));
procval = vm_proc_create_from_captured(klass, captured,
imemo_type(captured->code.val) == imemo_iseq ? block_type_iseq : block_type_ifunc,
(int8_t)ec->safe_level, FALSE, is_lambda);
return procval;
}
/* Binding */
VALUE
rb_vm_make_binding(const rb_execution_context_t *ec, const rb_control_frame_t *src_cfp)
{
rb_control_frame_t *cfp = rb_vm_get_binding_creatable_next_cfp(ec, src_cfp);
rb_control_frame_t *ruby_level_cfp = rb_vm_get_ruby_level_next_cfp(ec, src_cfp);
VALUE bindval, envval;
rb_binding_t *bind;
if (cfp == 0 || ruby_level_cfp == 0) {
rb_raise(rb_eRuntimeError, "Can't create Binding Object on top of Fiber.");
}
while (1) {
envval = vm_make_env_object(ec, cfp);
if (cfp == ruby_level_cfp) {
break;
}
cfp = rb_vm_get_binding_creatable_next_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
}
bindval = rb_binding_alloc(rb_cBinding);
GetBindingPtr(bindval, bind);
vm_bind_update_env(bindval, bind, envval);
RB_OBJ_WRITE(bindval, &bind->block.as.captured.self, cfp->self);
RB_OBJ_WRITE(bindval, &bind->block.as.captured.code.iseq, cfp->iseq);
RB_OBJ_WRITE(bindval, &bind->pathobj, ruby_level_cfp->iseq->body->location.pathobj);
bind->first_lineno = rb_vm_get_sourceline(ruby_level_cfp);
return bindval;
}
const VALUE *
rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const ID *dynvars)
{
VALUE envval, pathobj = bind->pathobj;
VALUE path = pathobj_path(pathobj);
VALUE realpath = pathobj_realpath(pathobj);
const struct rb_block *base_block;
const rb_env_t *env;
rb_execution_context_t *ec = GET_EC();
2015-07-22 01:52:59 +03:00
const rb_iseq_t *base_iseq, *iseq;
NODE *node = 0, tmp_node;
ID minibuf[4], *dyns = minibuf;
VALUE idtmp = 0;
if (dyncount < 0) return 0;
base_block = &bind->block;
base_iseq = vm_block_iseq(base_block);
if (dyncount >= numberof(minibuf)) dyns = ALLOCV_N(ID, idtmp, dyncount + 1);
dyns[0] = dyncount;
MEMCPY(dyns + 1, dynvars, ID, dyncount);
node = &tmp_node;
rb_node_init(node, NODE_SCOPE, (VALUE)dyns, 0, 0);
if (base_iseq) {
iseq = rb_iseq_new(node, base_iseq->body->location.label, path, realpath, base_iseq, ISEQ_TYPE_EVAL);
}
else {
VALUE tempstr = rb_fstring_cstr("<temp>");
2015-07-22 01:52:59 +03:00
iseq = rb_iseq_new_top(node, tempstr, tempstr, tempstr, NULL);
}
node->nd_tbl = 0; /* reset table */
ALLOCV_END(idtmp);
vm_set_eval_stack(ec, iseq, 0, base_block);
vm_bind_update_env(bindval, bind, envval = vm_make_env_object(ec, ec->cfp));
rb_vm_pop_frame(ec);
env = (const rb_env_t *)envval;
return env->env;
}
/* C -> Ruby: block */
static inline VALUE
invoke_block(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_cref_t *cref, VALUE type, int opt_pc)
{
int arg_size = iseq->body->param.size;
vm_push_frame(ec, iseq, type | VM_FRAME_FLAG_FINISH, self,
VM_GUARDED_PREV_EP(captured->ep),
(VALUE)cref, /* cref or method */
iseq->body->iseq_encoded + opt_pc,
ec->cfp->sp + arg_size,
iseq->body->local_table_size - arg_size,
iseq->body->stack_max);
return vm_exec(ec);
}
static VALUE
invoke_bmethod(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_callable_method_entry_t *me, VALUE type, int opt_pc)
{
/* bmethod */
int arg_size = iseq->body->param.size;
VALUE ret;
vm_push_frame(ec, iseq, type | VM_FRAME_FLAG_BMETHOD, self,
VM_GUARDED_PREV_EP(captured->ep),
(VALUE)me,
iseq->body->iseq_encoded + opt_pc,
ec->cfp->sp + arg_size,
iseq->body->local_table_size - arg_size,
iseq->body->stack_max);
RUBY_DTRACE_METHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
EXEC_EVENT_HOOK(ec, RUBY_EVENT_CALL, self, me->def->original_id, me->called_id, me->owner, Qnil);
VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);
ret = vm_exec(ec);
EXEC_EVENT_HOOK(ec, RUBY_EVENT_RETURN, self, me->def->original_id, me->called_id, me->owner, ret);
RUBY_DTRACE_METHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
return ret;
}
static inline VALUE
invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_block *captured,
VALUE self, int argc, const VALUE *argv, VALUE passed_block_handler,
const rb_cref_t *cref, int is_lambda)
{
const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
int i, opt_pc;
VALUE type = VM_FRAME_MAGIC_BLOCK | (is_lambda ? VM_FRAME_FLAG_LAMBDA : 0);
rb_control_frame_t *cfp = ec->cfp;
VALUE *sp = cfp->sp;
const rb_callable_method_entry_t *me = ec->passed_bmethod_me;
ec->passed_bmethod_me = NULL;
stack_check(ec);
CHECK_VM_STACK_OVERFLOW(cfp, argc);
cfp->sp = sp + argc;
for (i=0; i<argc; i++) {
sp[i] = argv[i];
}
opt_pc = vm_yield_setup_args(ec, iseq, argc, sp, passed_block_handler,
(is_lambda ? arg_setup_method : arg_setup_block));
cfp->sp = sp;
if (me == NULL) {
return invoke_block(ec, iseq, self, captured, cref, type, opt_pc);
}
else {
return invoke_bmethod(ec, iseq, self, captured, me, type, opt_pc);
}
}
static inline VALUE
invoke_block_from_c_bh(rb_execution_context_t *ec, VALUE block_handler,
int argc, const VALUE *argv,
VALUE passed_block_handler, const rb_cref_t *cref,
int is_lambda, int force_blockarg)
{
again:
switch (vm_block_handler_type(block_handler)) {
case block_handler_type_iseq:
{
const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(block_handler);
return invoke_iseq_block_from_c(ec, captured, captured->self,
argc, argv, passed_block_handler,
cref, is_lambda);
}
case block_handler_type_ifunc:
return vm_yield_with_cfunc(ec, VM_BH_TO_IFUNC_BLOCK(block_handler),
VM_BH_TO_IFUNC_BLOCK(block_handler)->self,
argc, argv, passed_block_handler);
case block_handler_type_symbol:
return vm_yield_with_symbol(ec, VM_BH_TO_SYMBOL(block_handler),
argc, argv, passed_block_handler);
case block_handler_type_proc:
if (force_blockarg == FALSE) {
is_lambda = block_proc_is_lambda(VM_BH_TO_PROC(block_handler));
}
block_handler = vm_proc_to_block_handler(VM_BH_TO_PROC(block_handler));
goto again;
}
VM_UNREACHABLE(invoke_block_from_c_splattable);
return Qundef;
}
static inline VALUE
check_block_handler(rb_execution_context_t *ec)
{
VALUE block_handler = VM_CF_BLOCK_HANDLER(ec->cfp);
vm_block_handler_verify(block_handler);
if (UNLIKELY(block_handler == VM_BLOCK_HANDLER_NONE)) {
rb_vm_localjump_error("no block given", Qnil, 0);
}
return block_handler;
}
static VALUE
vm_yield_with_cref(rb_execution_context_t *ec, int argc, const VALUE *argv, const rb_cref_t *cref, int is_lambda)
{
return invoke_block_from_c_bh(ec, check_block_handler(ec),
argc, argv, VM_BLOCK_HANDLER_NONE,
cref, is_lambda, FALSE);
}
static VALUE
vm_yield(rb_execution_context_t *ec, int argc, const VALUE *argv)
{
return invoke_block_from_c_bh(ec, check_block_handler(ec),
argc, argv, VM_BLOCK_HANDLER_NONE,
NULL, FALSE, FALSE);
}
static VALUE
vm_yield_with_block(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE block_handler)
{
return invoke_block_from_c_bh(ec, check_block_handler(ec),
argc, argv, block_handler,
NULL, FALSE, FALSE);
}
static VALUE
vm_yield_force_blockarg(rb_execution_context_t *ec, VALUE args)
{
return invoke_block_from_c_bh(ec, check_block_handler(ec), 1, &args,
VM_BLOCK_HANDLER_NONE, NULL, FALSE, TRUE);
}
static inline VALUE
invoke_block_from_c_proc(rb_execution_context_t *ec, const rb_proc_t *proc,
VALUE self, int argc, const VALUE *argv,
VALUE passed_block_handler, int is_lambda)
{
const struct rb_block *block = &proc->block;
again:
switch (vm_block_type(block)) {
case block_type_iseq:
return invoke_iseq_block_from_c(ec, &block->as.captured, self, argc, argv, passed_block_handler, NULL, is_lambda);
case block_type_ifunc:
return vm_yield_with_cfunc(ec, &block->as.captured, self, argc, argv, passed_block_handler);
case block_type_symbol:
return vm_yield_with_symbol(ec, block->as.symbol, argc, argv, passed_block_handler);
case block_type_proc:
is_lambda = block_proc_is_lambda(block->as.proc);
block = vm_proc_block(block->as.proc);
goto again;
}
VM_UNREACHABLE(invoke_block_from_c_proc);
return Qundef;
}
static VALUE
vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
int argc, const VALUE *argv, VALUE passed_block_handler)
{
VALUE val = Qundef;
enum ruby_tag_type state;
volatile int stored_safe = ec->safe_level;
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
ec->safe_level = proc->safe_level;
val = invoke_block_from_c_proc(ec, proc, self, argc, argv, passed_block_handler, proc->is_lambda);
}
EC_POP_TAG();
ec->safe_level = stored_safe;
if (state) {
EC_JUMP_TAG(ec, state);
}
return val;
}
static VALUE
vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
int argc, const VALUE *argv, VALUE block_handler)
{
return invoke_block_from_c_proc(ec, proc, self, argc, argv, block_handler, TRUE);
}
VALUE
rb_vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc,
int argc, const VALUE *argv, VALUE passed_block_handler)
{
VALUE self = vm_block_self(&proc->block);
vm_block_handler_verify(passed_block_handler);
if (proc->is_from_method) {
return vm_invoke_bmethod(ec, proc, self, argc, argv, passed_block_handler);
}
else {
return vm_invoke_proc(ec, proc, self, argc, argv, passed_block_handler);
}
}
/* special variable */
static rb_control_frame_t *
vm_normal_frame(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
{
while (cfp->pc == 0) {
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
return 0;
}
}
return cfp;
}
static VALUE
vm_cfp_svar_get(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key)
{
cfp = vm_normal_frame(ec, cfp);
return lep_svar_get(ec, cfp ? VM_CF_LEP(cfp) : 0, key);
}
static void
vm_cfp_svar_set(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key, const VALUE val)
{
cfp = vm_normal_frame(ec, cfp);
lep_svar_set(ec, cfp ? VM_CF_LEP(cfp) : 0, key, val);
}
static VALUE
vm_svar_get(const rb_execution_context_t *ec, VALUE key)
{
return vm_cfp_svar_get(ec, ec->cfp, key);
}
static void
vm_svar_set(const rb_execution_context_t *ec, VALUE key, VALUE val)
{
vm_cfp_svar_set(ec, ec->cfp, key, val);
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
}
VALUE
rb_backref_get(void)
{
return vm_svar_get(GET_EC(), VM_SVAR_BACKREF);
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
}
void
rb_backref_set(VALUE val)
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
{
vm_svar_set(GET_EC(), VM_SVAR_BACKREF, val);
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
}
VALUE
rb_lastline_get(void)
{
return vm_svar_get(GET_EC(), VM_SVAR_LASTLINE);
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
}
void
rb_lastline_set(VALUE val)
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
{
vm_svar_set(GET_EC(), VM_SVAR_LASTLINE, val);
}
/* misc */
/* in intern.h */
const char *
rb_sourcefile(void)
{
const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp) {
return RSTRING_PTR(rb_iseq_path(cfp->iseq));
}
else {
return 0;
}
}
/* in intern.h */
int
rb_sourceline(void)
{
const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp) {
return rb_vm_get_sourceline(cfp);
}
else {
return 0;
}
}
VALUE
rb_source_location(int *pline)
{
const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp) {
if (pline) *pline = rb_vm_get_sourceline(cfp);
return rb_iseq_path(cfp->iseq);
}
else {
if (pline) *pline = 0;
return Qnil;
}
}
const char *
rb_source_location_cstr(int *pline)
{
VALUE path = rb_source_location(pline);
if (NIL_P(path)) return NULL;
return RSTRING_PTR(path);
}
rb_cref_t *
rb_vm_cref(void)
{
const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp == NULL) {
return NULL;
}
return rb_vm_get_cref(cfp->ep);
}
rb_cref_t *
rb_vm_cref_replace_with_duplicated_cref(void)
{
const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
rb_cref_t *cref = vm_cref_replace_with_duplicated_cref(cfp->ep);
return cref;
}
const rb_cref_t *
rb_vm_cref_in_context(VALUE self, VALUE cbase)
{
const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
const rb_cref_t *cref;
if (cfp->self != self) return NULL;
if (!vm_env_cref_by_cref(cfp->ep)) return NULL;
cref = rb_vm_get_cref(cfp->ep);
if (CREF_CLASS(cref) != cbase) return NULL;
return cref;
}
#if 0
void
debug_cref(rb_cref_t *cref)
{
while (cref) {
dp(CREF_CLASS(cref));
printf("%ld\n", CREF_VISI(cref));
cref = CREF_NEXT(cref);
}
}
#endif
VALUE
rb_vm_cbase(void)
{
const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
if (cfp == 0) {
rb_raise(rb_eRuntimeError, "Can't call on top of Fiber or Thread");
}
return vm_get_cbase(cfp->ep);
}
/* jump */
static VALUE
make_localjump_error(const char *mesg, VALUE value, int reason)
{
extern VALUE rb_eLocalJumpError;
VALUE exc = rb_exc_new2(rb_eLocalJumpError, mesg);
ID id;
switch (reason) {
case TAG_BREAK:
CONST_ID(id, "break");
break;
case TAG_REDO:
CONST_ID(id, "redo");
break;
case TAG_RETRY:
CONST_ID(id, "retry");
break;
case TAG_NEXT:
CONST_ID(id, "next");
break;
case TAG_RETURN:
CONST_ID(id, "return");
break;
default:
CONST_ID(id, "noreason");
break;
}
rb_iv_set(exc, "@exit_value", value);
rb_iv_set(exc, "@reason", ID2SYM(id));
return exc;
}
void
rb_vm_localjump_error(const char *mesg, VALUE value, int reason)
{
VALUE exc = make_localjump_error(mesg, value, reason);
rb_exc_raise(exc);
}
VALUE
rb_vm_make_jump_tag_but_local_jump(int state, VALUE val)
{
const char *mesg;
switch (state) {
case TAG_RETURN:
mesg = "unexpected return";
break;
case TAG_BREAK:
mesg = "unexpected break";
break;
case TAG_NEXT:
mesg = "unexpected next";
break;
case TAG_REDO:
mesg = "unexpected redo";
val = Qnil;
break;
case TAG_RETRY:
mesg = "retry outside of rescue clause";
val = Qnil;
break;
default:
return Qnil;
}
if (val == Qundef) {
val = GET_EC()->tag->retval;
}
return make_localjump_error(mesg, val, state);
}
#if 0
void
rb_vm_jump_tag_but_local_jump(int state)
{
VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
if (!NIL_P(exc)) rb_exc_raise(exc);
EC_JUMP_TAG(GET_EC(), state);
}
#endif
static rb_control_frame_t *
next_not_local_frame(rb_control_frame_t *cfp)
{
while (VM_ENV_LOCAL_P(cfp->ep)) {
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
return cfp;
}
NORETURN(static void vm_iter_break(rb_execution_context_t *ec, VALUE val));
static void
vm_iter_break(rb_execution_context_t *ec, VALUE val)
{
rb_control_frame_t *cfp = next_not_local_frame(ec->cfp);
const VALUE *ep = VM_CF_PREV_EP(cfp);
const rb_control_frame_t *target_cfp = rb_vm_search_cf_from_ep(ec, cfp, ep);
#if 0 /* raise LocalJumpError */
if (!target_cfp) {
rb_vm_localjump_error("unexpected break", val, TAG_BREAK);
}
#endif
ec->errinfo = (VALUE)THROW_DATA_NEW(val, target_cfp, TAG_BREAK);
EC_JUMP_TAG(ec, TAG_BREAK);
}
void
rb_iter_break(void)
{
vm_iter_break(GET_EC(), Qnil);
}
void
rb_iter_break_value(VALUE val)
{
vm_iter_break(GET_EC(), val);
}
/* optimization: redefine management */
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
static st_table *vm_opt_method_table = 0;
static int
vm_redefinition_check_flag(VALUE klass)
{
if (klass == rb_cInteger) return INTEGER_REDEFINED_OP_FLAG;
if (klass == rb_cFloat) return FLOAT_REDEFINED_OP_FLAG;
if (klass == rb_cString) return STRING_REDEFINED_OP_FLAG;
if (klass == rb_cArray) return ARRAY_REDEFINED_OP_FLAG;
if (klass == rb_cHash) return HASH_REDEFINED_OP_FLAG;
if (klass == rb_cSymbol) return SYMBOL_REDEFINED_OP_FLAG;
if (klass == rb_cTime) return TIME_REDEFINED_OP_FLAG;
if (klass == rb_cRegexp) return REGEXP_REDEFINED_OP_FLAG;
if (klass == rb_cNilClass) return NIL_REDEFINED_OP_FLAG;
if (klass == rb_cTrueClass) return TRUE_REDEFINED_OP_FLAG;
if (klass == rb_cFalseClass) return FALSE_REDEFINED_OP_FLAG;
return 0;
}
static void
rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass)
{
st_data_t bop;
if (RB_TYPE_P(klass, T_ICLASS) && FL_TEST(klass, RICLASS_IS_ORIGIN)) {
klass = RBASIC_CLASS(klass);
}
if (me->def->type == VM_METHOD_TYPE_CFUNC) {
if (st_lookup(vm_opt_method_table, (st_data_t)me, &bop)) {
int flag = vm_redefinition_check_flag(klass);
ruby_vm_redefined_flag[bop] |= flag;
}
}
}
static enum rb_id_table_iterator_result
check_redefined_method(ID mid, VALUE value, void *data)
{
VALUE klass = (VALUE)data;
* method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions. * rb_method_entry(VALUE klass, ID id); * rb_method_entry_with_refinements(VALUE klass, ID id); * rb_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions. * rb_callable_method_entry(VALUE klass, ID id); * rb_callable_method_entry_with_refinements(VALUE klass, ID id); * rb_callable_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class. * method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner. * internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS. * class.c (method_entry_i): ditto. * class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr. * gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS. * cont.c (fiber_init): rb_control_frame_t::klass is removed. * proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information. * vm_core.h: remove several fields. * rb_control_frame_t::klass. * rb_block_t::klass. And catch up changes. * eval.c: catch up changes. * gc.c: ditto. * insns.def: ditto. * vm.c: ditto. * vm_args.c: ditto. * vm_backtrace.c: ditto. * vm_dump.c: ditto. * vm_eval.c: ditto. * vm_insnhelper.c: ditto. * vm_method.c: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
const rb_method_entry_t *me = (rb_method_entry_t *)value;
const rb_method_entry_t *newme = rb_method_entry(klass, mid);
if (newme != me) rb_vm_check_redefinition_opt_method(me, me->owner);
return ID_TABLE_CONTINUE;
}
void
rb_vm_check_redefinition_by_prepend(VALUE klass)
{
if (!vm_redefinition_check_flag(klass)) return;
rb_id_table_foreach(RCLASS_M_TBL(RCLASS_ORIGIN(klass)), check_redefined_method, (void *)klass);
}
static void
add_opt_method(VALUE klass, ID mid, VALUE bop)
{
* method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions. * rb_method_entry(VALUE klass, ID id); * rb_method_entry_with_refinements(VALUE klass, ID id); * rb_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions. * rb_callable_method_entry(VALUE klass, ID id); * rb_callable_method_entry_with_refinements(VALUE klass, ID id); * rb_callable_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class. * method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner. * internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS. * class.c (method_entry_i): ditto. * class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr. * gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS. * cont.c (fiber_init): rb_control_frame_t::klass is removed. * proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information. * vm_core.h: remove several fields. * rb_control_frame_t::klass. * rb_block_t::klass. And catch up changes. * eval.c: catch up changes. * gc.c: ditto. * insns.def: ditto. * vm.c: ditto. * vm_args.c: ditto. * vm_backtrace.c: ditto. * vm_dump.c: ditto. * vm_eval.c: ditto. * vm_insnhelper.c: ditto. * vm_method.c: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
const rb_method_entry_t *me = rb_method_entry_at(klass, mid);
if (me && me->def->type == VM_METHOD_TYPE_CFUNC) {
st_insert(vm_opt_method_table, (st_data_t)me, (st_data_t)bop);
}
else {
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
rb_bug("undefined optimized method: %s", rb_id2name(mid));
}
}
static void
vm_init_redefined_flag(void)
{
ID mid;
VALUE bop;
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
vm_opt_method_table = st_init_numtable();
#define OP(mid_, bop_) (mid = id##mid_, bop = BOP_##bop_, ruby_vm_redefined_flag[bop] = 0)
#define C(k) add_opt_method(rb_c##k, mid, bop)
OP(PLUS, PLUS), (C(Integer), C(Float), C(String), C(Array));
OP(MINUS, MINUS), (C(Integer), C(Float));
OP(MULT, MULT), (C(Integer), C(Float));
OP(DIV, DIV), (C(Integer), C(Float));
OP(MOD, MOD), (C(Integer), C(Float));
OP(Eq, EQ), (C(Integer), C(Float), C(String), C(Symbol));
OP(Eqq, EQQ), (C(Integer), C(Float), C(Symbol), C(String),
C(NilClass), C(TrueClass), C(FalseClass));
OP(LT, LT), (C(Integer), C(Float));
OP(LE, LE), (C(Integer), C(Float));
OP(GT, GT), (C(Integer), C(Float));
OP(GE, GE), (C(Integer), C(Float));
OP(LTLT, LTLT), (C(String), C(Array));
OP(AREF, AREF), (C(Array), C(Hash));
OP(ASET, ASET), (C(Array), C(Hash));
OP(Length, LENGTH), (C(Array), C(String), C(Hash));
OP(Size, SIZE), (C(Array), C(String), C(Hash));
OP(EmptyP, EMPTY_P), (C(Array), C(String), C(Hash));
OP(Succ, SUCC), (C(Integer), C(String), C(Time));
OP(EqTilde, MATCH), (C(Regexp), C(String));
OP(Freeze, FREEZE), (C(String));
OP(UMinus, UMINUS), (C(String));
OP(Max, MAX), (C(Array));
OP(Min, MIN), (C(Array));
#undef C
#undef OP
}
/* for vm development */
#if VMDEBUG
static const char *
vm_frametype_name(const rb_control_frame_t *cfp)
{
switch (VM_FRAME_TYPE(cfp)) {
case VM_FRAME_MAGIC_METHOD: return "method";
case VM_FRAME_MAGIC_BLOCK: return "block";
case VM_FRAME_MAGIC_CLASS: return "class";
case VM_FRAME_MAGIC_TOP: return "top";
case VM_FRAME_MAGIC_CFUNC: return "cfunc";
case VM_FRAME_MAGIC_IFUNC: return "ifunc";
case VM_FRAME_MAGIC_EVAL: return "eval";
case VM_FRAME_MAGIC_RESCUE: return "rescue";
default:
rb_bug("unknown frame");
}
}
#endif
static VALUE
frame_return_value(const struct vm_throw_data *err)
{
if (THROW_DATA_P(err) &&
THROW_DATA_STATE(err) == TAG_BREAK &&
THROW_DATA_CONSUMED_P(err) == FALSE) {
return THROW_DATA_VAL(err);
}
else {
return Qnil;
}
}
#if 0
/* for debug */
static const char *
frame_name(const rb_control_frame_t *cfp)
{
unsigned long type = VM_FRAME_TYPE(cfp);
#define C(t) if (type == VM_FRAME_MAGIC_##t) return #t
C(METHOD);
C(BLOCK);
C(CLASS);
C(TOP);
C(CFUNC);
C(PROC);
C(IFUNC);
C(EVAL);
C(LAMBDA);
C(RESCUE);
C(DUMMY);
#undef C
return "unknown";
}
#endif
static void
hook_before_rewind(rb_execution_context_t *ec, const rb_control_frame_t *cfp, int will_finish_vm_exec, int state, struct vm_throw_data *err)
{
if (state == TAG_RAISE && RBASIC_CLASS(err) == rb_eSysStackError) {
return;
}
switch (VM_FRAME_TYPE(ec->cfp)) {
case VM_FRAME_MAGIC_METHOD:
RUBY_DTRACE_METHOD_RETURN_HOOK(ec, 0, 0);
EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_RETURN, ec->cfp->self, 0, 0, 0, frame_return_value(err));
THROW_DATA_CONSUMED_SET(err);
break;
case VM_FRAME_MAGIC_BLOCK:
if (VM_FRAME_BMETHOD_P(ec->cfp)) {
EXEC_EVENT_HOOK(ec, RUBY_EVENT_B_RETURN, ec->cfp->self, 0, 0, 0, frame_return_value(err));
if (!will_finish_vm_exec) {
/* kick RUBY_EVENT_RETURN at invoke_block_from_c() for bmethod */
EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_RETURN, ec->cfp->self,
rb_vm_frame_method_entry(ec->cfp)->def->original_id,
rb_vm_frame_method_entry(ec->cfp)->called_id,
rb_vm_frame_method_entry(ec->cfp)->owner,
frame_return_value(err));
}
THROW_DATA_CONSUMED_SET(err);
}
else {
EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_B_RETURN, ec->cfp->self, 0, 0, 0, frame_return_value(err));
THROW_DATA_CONSUMED_SET(err);
}
break;
case VM_FRAME_MAGIC_CLASS:
EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_END, ec->cfp->self, 0, 0, 0, Qnil);
break;
}
}
/* evaluator body */
/* finish
VMe (h1) finish
VM finish F1 F2
cfunc finish F1 F2 C1
rb_funcall finish F1 F2 C1
VMe finish F1 F2 C1
VM finish F1 F2 C1 F3
F1 - F3 : pushed by VM
C1 : pushed by send insn (CFUNC)
struct CONTROL_FRAME {
VALUE *pc; // cfp[0], program counter
VALUE *sp; // cfp[1], stack pointer
rb_iseq_t *iseq; // cfp[2], iseq
VALUE self; // cfp[3], self
const VALUE *ep; // cfp[4], env pointer
const void *block_code; // cfp[5], blcok code
};
struct rb_captured_blcok {
VALUE self;
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
VALUE *ep;
union code;
};
struct METHOD_ENV {
VALUE param0;
...
VALUE paramN;
VALUE lvar1;
...
VALUE lvarM;
VALUE cref; // ep[-2]
VALUE special; // ep[-1]
VALUE flags; // ep[ 0] == lep[0]
};
struct BLOCK_ENV {
VALUE block_param0;
...
VALUE block_paramN;
VALUE block_lvar1;
...
VALUE block_lvarM;
VALUE cref; // ep[-2]
VALUE special; // ep[-1]
VALUE flags; // ep[ 0]
};
struct CLASS_ENV {
VALUE class_lvar0;
...
VALUE class_lvarN;
VALUE cref;
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
VALUE prev_ep; // for frame jump
VALUE flags;
};
struct C_METHOD_CONTROL_FRAME {
VALUE *pc; // 0
VALUE *sp; // stack pointer
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
rb_iseq_t *iseq; // cmi
VALUE self; // ?
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
VALUE *ep; // ep == lep
void *code; //
};
struct C_BLOCK_CONTROL_FRAME {
VALUE *pc; // point only "finish" insn
VALUE *sp; // sp
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
rb_iseq_t *iseq; // ?
VALUE self; //
* vm_core.h: remove lfp (local frame pointer) and rename dfp (dynamic frame pointer) to ep (environment pointer). This change make VM `normal' (similar to other interpreters). Before this commit: Each frame has two env pointers lfp and dfp. lfp points local environment which is method/class/toplevel frame. lfp[0] is block pointer. dfp is block local frame. dfp[0] points previous (parent) environment pointer. lfp == dfp when frame is method/class/toplevel. You can get lfp from dfp by traversing previous environment pointers. After this commit: Each frame has only `ep' to point respective enviornoment. If there is parent environment, then ep[0] points parent envioenment (as dfp). If there are no more environment, then ep[0] points block pointer (as lfp). We call such ep as `LEP' (local EP). We add some macros to get LEP and to detect LEP or not. In short, we replace dfp and lfp with ep and LEP. rb_block_t and rb_binding_t member `lfp' and `dfp' are removed and member `ep' is added. rename rb_thread_t's member `local_lfp' and `local_svar' to `root_lep' and `root_svar'. (VM_EP_PREV_EP(ep)): get previous environment pointer. This macro assume that ep is not LEP. (VM_EP_BLOCK_PTR(ep)): get block pointer. This macro assume that ep is LEP. (VM_EP_LEP_P(ep)): detect ep is LEP or not. (VM_ENVVAL_BLOCK_PTR(ptr)): make block pointer. (VM_ENVVAL_BLOCK_PTR_P(v)): detect v is block pointer. (VM_ENVVAL_PREV_EP_PTR(ptr)): make prev environment pointer. (VM_ENVVAL_PREV_EP_PTR_P(v)): detect v is prev env pointer. * vm.c: apply above changes. (VM_EP_LEP(ep)): get LEP. (VM_CF_LEP(cfp)): get LEP of cfp->ep. (VM_CF_PREV_EP(cfp)): utility function VM_EP_PREV_EP(cfp->ep). (VM_CF_BLOCK_PTR(cfp)): utility function VM_EP_BLOCK_PTR(cfp->ep). * vm.c, vm_eval.c, vm_insnhelper.c, vm_insnhelper.h, insns.def: apply above changes. * cont.c: ditto. * eval.c, eval_intern.h: ditto. * proc.c: ditto. * thread.c: ditto. * vm_dump.c: ditto. * vm_exec.h: fix function name (on vm debug mode). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-11 07:14:59 +04:00
VALUE *ep; // ep
void *code; //
};
*/
static VALUE
vm_exec(rb_execution_context_t *ec)
{
enum ruby_tag_type state;
VALUE result;
VALUE initial = 0;
struct vm_throw_data *err;
EC_PUSH_TAG(ec);
_tag.retval = Qnil;
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
vm_loop_start:
result = vm_exec_core(ec, initial);
VM_ASSERT(ec->tag == &_tag);
if ((state = _tag.state) != TAG_NONE) {
err = (struct vm_throw_data *)result;
_tag.state = TAG_NONE;
goto exception_handler;
}
}
else {
unsigned int i;
const struct iseq_catch_table_entry *entry;
const struct iseq_catch_table *ct;
unsigned long epc, cont_pc, cont_sp;
2015-07-22 01:52:59 +03:00
const rb_iseq_t *catch_iseq;
rb_control_frame_t *cfp;
VALUE type;
const rb_control_frame_t *escape_cfp;
err = (struct vm_throw_data *)ec->errinfo;
rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW);
exception_handler:
2015-07-22 01:52:59 +03:00
cont_pc = cont_sp = 0;
catch_iseq = NULL;
while (ec->cfp->pc == 0 || ec->cfp->iseq == 0) {
if (UNLIKELY(VM_FRAME_TYPE(ec->cfp) == VM_FRAME_MAGIC_CFUNC)) {
EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, ec->cfp->self,
rb_vm_frame_method_entry(ec->cfp)->def->original_id,
rb_vm_frame_method_entry(ec->cfp)->called_id,
rb_vm_frame_method_entry(ec->cfp)->owner, Qnil);
RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec,
rb_vm_frame_method_entry(ec->cfp)->owner,
rb_vm_frame_method_entry(ec->cfp)->def->original_id);
}
rb_vm_pop_frame(ec);
}
cfp = ec->cfp;
2015-07-22 01:52:59 +03:00
epc = cfp->pc - cfp->iseq->body->iseq_encoded;
escape_cfp = NULL;
if (state == TAG_BREAK || state == TAG_RETURN) {
escape_cfp = THROW_DATA_CATCH_FRAME(err);
if (cfp == escape_cfp) {
if (state == TAG_RETURN) {
if (!VM_FRAME_FINISHED_P(cfp)) {
THROW_DATA_CATCH_FRAME_SET(err, cfp + 1);
THROW_DATA_STATE_SET(err, state = TAG_BREAK);
}
else {
2015-07-22 01:52:59 +03:00
ct = cfp->iseq->body->catch_table;
if (ct) for (i = 0; i < ct->size; i++) {
entry = &ct->entries[i];
if (entry->start < epc && entry->end >= epc) {
if (entry->type == CATCH_TYPE_ENSURE) {
2015-07-22 01:52:59 +03:00
catch_iseq = entry->iseq;
cont_pc = entry->cont;
cont_sp = entry->sp;
break;
}
}
}
if (catch_iseq == NULL) {
ec->errinfo = Qnil;
result = THROW_DATA_VAL(err);
THROW_DATA_CATCH_FRAME_SET(err, cfp + 1);
hook_before_rewind(ec, ec->cfp, TRUE, state, err);
rb_vm_pop_frame(ec);
goto finish_vme;
}
}
/* through */
}
else {
/* TAG_BREAK */
#if OPT_STACK_CACHING
initial = THROW_DATA_VAL(err);
#else
*ec->cfp->sp++ = THROW_DATA_VAL(err);
#endif
ec->errinfo = Qnil;
goto vm_loop_start;
}
}
}
if (state == TAG_RAISE) {
2015-07-22 01:52:59 +03:00
ct = cfp->iseq->body->catch_table;
if (ct) for (i = 0; i < ct->size; i++) {
entry = &ct->entries[i];
if (entry->start < epc && entry->end >= epc) {
if (entry->type == CATCH_TYPE_RESCUE ||
entry->type == CATCH_TYPE_ENSURE) {
2015-07-22 01:52:59 +03:00
catch_iseq = entry->iseq;
cont_pc = entry->cont;
cont_sp = entry->sp;
break;
}
}
}
}
else if (state == TAG_RETRY) {
2015-07-22 01:52:59 +03:00
ct = cfp->iseq->body->catch_table;
if (ct) for (i = 0; i < ct->size; i++) {
entry = &ct->entries[i];
if (entry->start < epc && entry->end >= epc) {
if (entry->type == CATCH_TYPE_ENSURE) {
2015-07-22 01:52:59 +03:00
catch_iseq = entry->iseq;
cont_pc = entry->cont;
cont_sp = entry->sp;
break;
}
else if (entry->type == CATCH_TYPE_RETRY) {
const rb_control_frame_t *escape_cfp;
escape_cfp = THROW_DATA_CATCH_FRAME(err);
if (cfp == escape_cfp) {
2015-07-22 01:52:59 +03:00
cfp->pc = cfp->iseq->body->iseq_encoded + entry->cont;
ec->errinfo = Qnil;
goto vm_loop_start;
}
}
}
}
}
else if (state == TAG_BREAK && !escape_cfp) {
type = CATCH_TYPE_BREAK;
search_restart_point:
2015-07-22 01:52:59 +03:00
ct = cfp->iseq->body->catch_table;
if (ct) for (i = 0; i < ct->size; i++) {
entry = &ct->entries[i];
if (entry->start < epc && entry->end >= epc) {
if (entry->type == CATCH_TYPE_ENSURE) {
2015-07-22 01:52:59 +03:00
catch_iseq = entry->iseq;
cont_pc = entry->cont;
cont_sp = entry->sp;
break;
}
else if (entry->type == type) {
2015-07-22 01:52:59 +03:00
cfp->pc = cfp->iseq->body->iseq_encoded + entry->cont;
cfp->sp = vm_base_ptr(cfp) + entry->sp;
if (state != TAG_REDO) {
#if OPT_STACK_CACHING
initial = THROW_DATA_VAL(err);
#else
*ec->cfp->sp++ = THROW_DATA_VAL(err);
#endif
}
ec->errinfo = Qnil;
VM_ASSERT(ec->tag->state == TAG_NONE);
goto vm_loop_start;
}
}
}
}
else if (state == TAG_REDO) {
type = CATCH_TYPE_REDO;
goto search_restart_point;
}
else if (state == TAG_NEXT) {
type = CATCH_TYPE_NEXT;
goto search_restart_point;
}
else {
2015-07-22 01:52:59 +03:00
ct = cfp->iseq->body->catch_table;
if (ct) for (i = 0; i < ct->size; i++) {
entry = &ct->entries[i];
if (entry->start < epc && entry->end >= epc) {
if (entry->type == CATCH_TYPE_ENSURE) {
2015-07-22 01:52:59 +03:00
catch_iseq = entry->iseq;
cont_pc = entry->cont;
cont_sp = entry->sp;
break;
}
}
}
}
if (catch_iseq != NULL) { /* found catch table */
/* enter catch scope */
const int arg_size = 1;
* introduce new ISeq binary format serializer/de-serializer and a pre-compilation/runtime loader sample. [Feature #11788] * iseq.c: add new methods: * RubyVM::InstructionSequence#to_binary_format(extra_data = nil) * RubyVM::InstructionSequence.from_binary_format(binary) * RubyVM::InstructionSequence.from_binary_format_extra_data(binary) * compile.c: implement body of this new feature. * load.c (rb_load_internal0), iseq.c (rb_iseq_load_iseq): call RubyVM::InstructionSequence.load_iseq(fname) with loading script name if this method is defined. We can return any ISeq object as a result value. Otherwise loading will be continue as usual. This interface is not matured and is not extensible. So that we don't guarantee the future compatibility of this method. Basically, you should'nt use this method. * iseq.h: move ISEQ_MAJOR/MINOR_VERSION (and some definitions) from iseq.c. * encoding.c (rb_data_is_encoding), internal.h: added. * vm_core.h: add several supports for lazy load. * add USE_LAZY_LOAD macro to specify enable or disable of this feature. * add several fields to rb_iseq_t. * introduce new macro rb_iseq_check(). * insns.def: some check for lazy loading feature. * vm_insnhelper.c: ditto. * proc.c: ditto. * vm.c: ditto. * test/lib/iseq_loader_checker.rb: enabled iff suitable environment variables are provided. * test/runner.rb: enable lib/iseq_loader_checker.rb. * sample/iseq_loader.rb: add sample compiler and loader. $ ruby sample/iseq_loader.rb [dir] will compile all ruby scripts in [dir]. With default setting, this compile creates *.rb.yarb files in same directory of target .rb scripts. $ ruby -r sample/iseq_loader.rb [app] will run with enable to load compiled binary data. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52949 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-12-08 16:58:50 +03:00
rb_iseq_check(catch_iseq);
cfp->sp = vm_base_ptr(cfp) + cont_sp;
2015-07-22 01:52:59 +03:00
cfp->pc = cfp->iseq->body->iseq_encoded + cont_pc;
/* push block frame */
cfp->sp[0] = (VALUE)err;
vm_push_frame(ec, catch_iseq, VM_FRAME_MAGIC_RESCUE,
* method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions. * rb_method_entry(VALUE klass, ID id); * rb_method_entry_with_refinements(VALUE klass, ID id); * rb_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions. * rb_callable_method_entry(VALUE klass, ID id); * rb_callable_method_entry_with_refinements(VALUE klass, ID id); * rb_callable_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class. * method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner. * internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS. * class.c (method_entry_i): ditto. * class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr. * gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS. * cont.c (fiber_init): rb_control_frame_t::klass is removed. * proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information. * vm_core.h: remove several fields. * rb_control_frame_t::klass. * rb_block_t::klass. And catch up changes. * eval.c: catch up changes. * gc.c: ditto. * insns.def: ditto. * vm.c: ditto. * vm_args.c: ditto. * vm_backtrace.c: ditto. * vm_dump.c: ditto. * vm_eval.c: ditto. * vm_insnhelper.c: ditto. * vm_method.c: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
cfp->self,
VM_GUARDED_PREV_EP(cfp->ep),
0, /* cref or me */
2015-07-22 01:52:59 +03:00
catch_iseq->body->iseq_encoded,
cfp->sp + arg_size /* push value */,
catch_iseq->body->local_table_size - arg_size,
2015-07-22 01:52:59 +03:00
catch_iseq->body->stack_max);
state = 0;
ec->tag->state = TAG_NONE;
ec->errinfo = Qnil;
goto vm_loop_start;
}
else {
hook_before_rewind(ec, ec->cfp, FALSE, state, err);
if (VM_FRAME_FINISHED_P(ec->cfp)) {
rb_vm_pop_frame(ec);
ec->errinfo = (VALUE)err;
EC_TMPPOP_TAG();
EC_JUMP_TAG(ec, state);
}
* vm_core.h: remove VM_FRAME_MAGIC_FINISH (finish frame type). Before this commit: `finish frame' was place holder which indicates that VM loop needs to return function. If a C method calls a Ruby methods (a method written by Ruby), then VM loop will be (re-)invoked. When the Ruby method returns, then also VM loop should be escaped. `finish frame' has only one instruction `finish', which returns VM loop function. VM loop function executes `finish' instruction, then VM loop function returns itself. With such mechanism, `leave' instruction (which returns one frame from current scope) doesn't need to check that this `leave' should also return from VM loop function. Strictly, one branch can be removed from `leave' instructon. Consideration: However, pushing the `finish frame' needs costs because it needs several memory accesses. The number of pushing `finish frame' is greater than I had assumed. Of course, pushing `finish frame' consumes additional control frame. Moreover, recent processors has good branch prediction, with which we can ignore such trivial checking. After this commit: Finally, I decide to remove `finish frame' and `finish' instruction. Some parts of VM depend on `finish frame', so the new frame flag VM_FRAME_FLAG_FINISH is introduced. If this frame should escape from VM function loop, then the result of VM_FRAME_TYPE_FINISH_P(cfp) is true. `leave' instruction checks this flag every time. I measured performance on it. However on my environments, it improves some benchmarks and slows some benchmarks down. Maybe it is because of C compiler optimization parameters. I'll re-visit here if this cause problems. * insns.def (leave, finish): remove finish instruction. * vm.c, vm_eval.c, vm_exec.c, vm_backtrace.c, vm_dump.c: apply above changes. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36099 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-15 14:22:34 +04:00
else {
rb_vm_pop_frame(ec);
* vm_core.h: remove VM_FRAME_MAGIC_FINISH (finish frame type). Before this commit: `finish frame' was place holder which indicates that VM loop needs to return function. If a C method calls a Ruby methods (a method written by Ruby), then VM loop will be (re-)invoked. When the Ruby method returns, then also VM loop should be escaped. `finish frame' has only one instruction `finish', which returns VM loop function. VM loop function executes `finish' instruction, then VM loop function returns itself. With such mechanism, `leave' instruction (which returns one frame from current scope) doesn't need to check that this `leave' should also return from VM loop function. Strictly, one branch can be removed from `leave' instructon. Consideration: However, pushing the `finish frame' needs costs because it needs several memory accesses. The number of pushing `finish frame' is greater than I had assumed. Of course, pushing `finish frame' consumes additional control frame. Moreover, recent processors has good branch prediction, with which we can ignore such trivial checking. After this commit: Finally, I decide to remove `finish frame' and `finish' instruction. Some parts of VM depend on `finish frame', so the new frame flag VM_FRAME_FLAG_FINISH is introduced. If this frame should escape from VM function loop, then the result of VM_FRAME_TYPE_FINISH_P(cfp) is true. `leave' instruction checks this flag every time. I measured performance on it. However on my environments, it improves some benchmarks and slows some benchmarks down. Maybe it is because of C compiler optimization parameters. I'll re-visit here if this cause problems. * insns.def (leave, finish): remove finish instruction. * vm.c, vm_eval.c, vm_exec.c, vm_backtrace.c, vm_dump.c: apply above changes. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36099 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-15 14:22:34 +04:00
goto exception_handler;
}
}
}
finish_vme:
EC_POP_TAG();
return result;
}
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
/* misc */
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
VALUE
2015-07-22 01:52:59 +03:00
rb_iseq_eval(const rb_iseq_t *iseq)
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
{
rb_execution_context_t *ec = GET_EC();
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
VALUE val;
vm_set_top_stack(ec, iseq);
val = vm_exec(ec);
return val;
}
VALUE
2015-07-22 01:52:59 +03:00
rb_iseq_eval_main(const rb_iseq_t *iseq)
{
rb_execution_context_t *ec = GET_EC();
VALUE val;
vm_set_main_stack(ec, iseq);
val = vm_exec(ec);
* this commit is a result of refactoring. only renaming functions, moving definitions place, add/remove prototypes, deleting unused variables and removing yarv.h. This commit doesn't change any behavior of ruby/vm. * yarv.h, common.mk: remove yarv.h (contents are moved to yarvcore.h). * error.c, eval_intern.h: include yarvcore.h instead yarv.h * rename some functions: * debug.[ch]: debug_*() -> ruby_debug_*() * iseq.c: iseq_*() -> rb_iseq_*(), ruby_iseq_disasm() * iseq.c: node_name() -> ruby_node_name() * vm.c: yarv_check_redefinition_opt_method() -> rb_vm_check_redefinition_opt_method() * some refactoring with checking -Wall. * array.c: remove rb_ary_ptr() (unused) and remove unused local variables. * object.c: add a prototype of rb_mod_module_exec(). * eval_intern.h (ruby_cref): set it inline. * eval_load.c (rb_load), yarvcore.c: yarv_load() -> rb_load_internal(). * parse.y: add a prototype of rb_parse_in_eval() (in eval.c). * process.c: add a prototype of rb_thread_stop_timer_thread() (in thread.c). * thread.c: remove raw_gets() function (unused) and fix some format mismatch (format mismatchs have remained yet. this is todo). * thread.c (rb_thread_wait_fd_rw): fix typo on label name. * thread_pthread.ci: comment out codes with USE_THREAD_CACHE. * vm.c (rb_svar, rb_backref_get, rb_backref_get, rb_lastline_get, rb_lastline_set) : moved from yarvcore.c. * vm.c (yarv_init_redefined_flag): add a prototype and rename yarv_opt_method_table to vm_opt_method_table. * vm.c (rb_thread_eval): moved from yarvcore.c. * yarvcore.c: remove unused global variables and fix to use nsdr(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11652 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-07 04:25:05 +03:00
return val;
}
int
rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, ID *called_idp, VALUE *klassp)
{
* method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions. * rb_method_entry(VALUE klass, ID id); * rb_method_entry_with_refinements(VALUE klass, ID id); * rb_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions. * rb_callable_method_entry(VALUE klass, ID id); * rb_callable_method_entry_with_refinements(VALUE klass, ID id); * rb_callable_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class. * method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner. * internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS. * class.c (method_entry_i): ditto. * class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr. * gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS. * cont.c (fiber_init): rb_control_frame_t::klass is removed. * proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information. * vm_core.h: remove several fields. * rb_control_frame_t::klass. * rb_block_t::klass. And catch up changes. * eval.c: catch up changes. * gc.c: ditto. * insns.def: ditto. * vm.c: ditto. * vm_args.c: ditto. * vm_backtrace.c: ditto. * vm_dump.c: ditto. * vm_eval.c: ditto. * vm_insnhelper.c: ditto. * vm_method.c: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
if (me) {
if (idp) *idp = me->def->original_id;
if (called_idp) *called_idp = me->called_id;
* method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions. * rb_method_entry(VALUE klass, ID id); * rb_method_entry_with_refinements(VALUE klass, ID id); * rb_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions. * rb_callable_method_entry(VALUE klass, ID id); * rb_callable_method_entry_with_refinements(VALUE klass, ID id); * rb_callable_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class. * method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner. * internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS. * class.c (method_entry_i): ditto. * class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr. * gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS. * cont.c (fiber_init): rb_control_frame_t::klass is removed. * proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information. * vm_core.h: remove several fields. * rb_control_frame_t::klass. * rb_block_t::klass. And catch up changes. * eval.c: catch up changes. * gc.c: ditto. * insns.def: ditto. * vm.c: ditto. * vm_args.c: ditto. * vm_backtrace.c: ditto. * vm_dump.c: ditto. * vm_eval.c: ditto. * vm_insnhelper.c: ditto. * vm_method.c: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
if (klassp) *klassp = me->owner;
return TRUE;
}
else {
return FALSE;
}
}
int
rb_ec_frame_method_id_and_class(const rb_execution_context_t *ec, ID *idp, ID *called_idp, VALUE *klassp)
{
return rb_vm_control_frame_id_and_class(ec->cfp, idp, called_idp, klassp);
}
int
rb_frame_method_id_and_class(ID *idp, VALUE *klassp)
{
return rb_ec_frame_method_id_and_class(GET_EC(), idp, 0, klassp);
}
VALUE
rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg,
VALUE block_handler, VALUE filename)
{
rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *reg_cfp = ec->cfp;
2015-07-22 01:52:59 +03:00
const rb_iseq_t *iseq = rb_iseq_new(0, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
VALUE val;
vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH,
recv, block_handler,
(VALUE)vm_cref_new_toplevel(ec), /* cref or me */
0, reg_cfp->sp, 0, 0);
val = (*func)(arg);
rb_vm_pop_frame(ec);
return val;
}
/* vm */
void rb_vm_trace_mark_event_hooks(rb_hook_list_t *hooks);
void
rb_vm_mark(void *ptr)
{
RUBY_MARK_ENTER("vm");
RUBY_GC_INFO("-------------------------------------------------\n");
if (ptr) {
rb_vm_t *vm = ptr;
rb_thread_t *th = 0;
list_for_each(&vm->living_threads, th, vmlt_node) {
rb_gc_mark(th->self);
}
rb_gc_mark(vm->thgroup_default);
rb_gc_mark(vm->mark_object_ary);
rb_gc_mark(vm->load_path);
rb_gc_mark(vm->load_path_snapshot);
Fix compatibility of cached expanded load path * file.c (rb_get_path_check_to_string): extract from rb_get_path_check(). We change the spec not to call to_path of String object. * file.c (rb_get_path_check_convert): extract from rb_get_path_check(). * file.c (rb_get_path_check): follow the above change. * file.c (rb_file_expand_path_fast): remove check_expand_path_args(). Instead we call it in load.c. * file.c (rb_find_file_ext_safe): use rb_get_expanded_load_path() to reduce expand cost. * file.c (rb_find_file_safe): ditto. * internal.h (rb_get_expanded_load_path): add a declaration. * internal.h (rb_get_path_check_to_string, rb_get_path_check_convert): add declarations. * load.c (rb_construct_expanded_load_path): fix for compatibility. Same checks in rb_get_path_check() are added. We don't replace $LOAD_PATH and ensure that String object of $LOAD_PATH are frozen. We don't freeze non String object and expand it every times. We add arguments for expanding load path partially and checking if load path have relative paths or non String objects. * load.c (load_path_getcwd): get current working directory for checking if it's changed when getting load path. * load.c (rb_get_expanded_load_path): fix for rebuilding cache properly. We check if current working directory is changed and rebuild expanded load path cache. We expand paths which start with ~ (User HOME) and non String objects every times for compatibility. We make this accessible from other source files. * load.c (rb_feature_provided): call rb_get_path() since we changed rb_file_expand_path_fast() not to call it. * load.c (Init_load): initialize vm->load_path_check_cache. * vm.c (rb_vm_mark): mark vm->load_path_check_cache for GC. * vm_core.h (rb_vm_struct): add vm->load_path_check_cache to store data to check load path cache validity. * test/ruby/test_require.rb (TestRequire): add tests for require compatibility related to cached expanded load path. [ruby-core:47970] [Bug #7158] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37482 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-05 19:27:08 +04:00
RUBY_MARK_UNLESS_NULL(vm->load_path_check_cache);
rb_gc_mark(vm->expanded_load_path);
rb_gc_mark(vm->loaded_features);
rb_gc_mark(vm->loaded_features_snapshot);
rb_gc_mark(vm->top_self);
RUBY_MARK_UNLESS_NULL(vm->coverages);
rb_gc_mark(vm->defined_module_hash);
if (vm->loading_table) {
rb_mark_tbl(vm->loading_table);
}
rb_vm_trace_mark_event_hooks(&vm->event_hooks);
rb_gc_mark_values(RUBY_NSIG, vm->trap_list.cmd);
}
RUBY_MARK_LEAVE("vm");
}
#undef rb_vm_register_special_exception
void
rb_vm_register_special_exception_str(enum ruby_special_exceptions sp, VALUE cls, VALUE mesg)
{
rb_vm_t *vm = GET_VM();
VALUE exc = rb_exc_new3(cls, rb_obj_freeze(mesg));
OBJ_TAINT(exc);
OBJ_FREEZE(exc);
((VALUE *)vm->special_exceptions)[sp] = exc;
rb_gc_register_mark_object(exc);
}
int
rb_vm_add_root_module(ID id, VALUE module)
{
rb_vm_t *vm = GET_VM();
rb_hash_aset(vm->defined_module_hash, ID2SYM(id), module);
return TRUE;
}
static int
free_loading_table_entry(st_data_t key, st_data_t value, st_data_t arg)
{
xfree((char *)key);
return ST_DELETE;
}
int
ruby_vm_destruct(rb_vm_t *vm)
{
RUBY_FREE_ENTER("vm");
if (vm) {
rb_thread_t *th = vm->main_thread;
struct rb_objspace *objspace = vm->objspace;
vm->main_thread = 0;
if (th) {
rb_fiber_reset_root_local_storage(th->self);
thread_free(th);
}
vm*: doubly-linked list from ccan to manage vm->living_threads A doubly-linked list for tracking living threads guarantees constant-time insert/delete performance with no corner cases of a hash table. I chose this ccan implementation of doubly-linked lists over the BSD sys/queue.h implementation since: 1) insertion and removal are both branchless 2) locality is improved if a struct may be a member of multiple lists (0002 patch in Feature 9632 will introduce a secondary list for waiting FDs) This also increases cache locality during iteration: improving performance in a new IO#close benchmark with many sleeping threads while still scanning the same number of threads. vm_thread_close 1.762 * vm_core.h (rb_vm_t): list_head and counter for living_threads (rb_thread_t): vmlt_node for living_threads linkage (rb_vm_living_threads_init): new function wrapper (rb_vm_living_threads_insert): ditto (rb_vm_living_threads_remove): ditto * vm.c (rb_vm_living_threads_foreach): new function wrapper * thread.c (terminate_i, thread_start_func_2, thread_create_core, thread_fd_close_i, thread_fd_close): update to use new APIs * vm.c (vm_mark_each_thread_func, rb_vm_mark, ruby_vm_destruct, vm_memsize, vm_init2, Init_VM): ditto * vm_trace.c (clear_trace_func_i, rb_clear_trace_func): ditto * benchmark/bm_vm_thread_close.rb: added to show improvement * ccan/build_assert/build_assert.h: added as a dependency of list.h * ccan/check_type/check_type.h: ditto * ccan/container_of/container_of.h: ditto * ccan/licenses/BSD-MIT: ditto * ccan/licenses/CC0: ditto * ccan/str/str.h: ditto (stripped of unused macros) * ccan/list/list.h: ditto * common.mk: add CCAN_LIST_INCLUDES [ruby-core:61871][Feature 9632 (part 1)] Apologies for the size of this commit, but I think a good doubly-linked list will be useful for future features, too. This may be used to add ordering to a container_of-based hash table to preserve compatibility if required (e.g. feature 9614). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@45913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-05-11 03:48:51 +04:00
rb_vm_living_threads_init(vm);
ruby_vm_run_at_exit_hooks(vm);
if (vm->loading_table) {
st_foreach(vm->loading_table, free_loading_table_entry, 0);
st_free_table(vm->loading_table);
vm->loading_table = 0;
}
if (vm->frozen_strings) {
st_free_table(vm->frozen_strings);
vm->frozen_strings = 0;
}
rb_vm_gvl_destroy(vm);
if (objspace) {
rb_objspace_free(objspace);
}
/* after freeing objspace, you *can't* use ruby_xfree() */
ruby_mimfree(vm);
ruby_current_vm_ptr = NULL;
}
RUBY_FREE_LEAVE("vm");
return 0;
}
static size_t
vm_memsize(const void *ptr)
{
const rb_vm_t *vmobj = ptr;
size_t size = sizeof(rb_vm_t);
vm*: doubly-linked list from ccan to manage vm->living_threads A doubly-linked list for tracking living threads guarantees constant-time insert/delete performance with no corner cases of a hash table. I chose this ccan implementation of doubly-linked lists over the BSD sys/queue.h implementation since: 1) insertion and removal are both branchless 2) locality is improved if a struct may be a member of multiple lists (0002 patch in Feature 9632 will introduce a secondary list for waiting FDs) This also increases cache locality during iteration: improving performance in a new IO#close benchmark with many sleeping threads while still scanning the same number of threads. vm_thread_close 1.762 * vm_core.h (rb_vm_t): list_head and counter for living_threads (rb_thread_t): vmlt_node for living_threads linkage (rb_vm_living_threads_init): new function wrapper (rb_vm_living_threads_insert): ditto (rb_vm_living_threads_remove): ditto * vm.c (rb_vm_living_threads_foreach): new function wrapper * thread.c (terminate_i, thread_start_func_2, thread_create_core, thread_fd_close_i, thread_fd_close): update to use new APIs * vm.c (vm_mark_each_thread_func, rb_vm_mark, ruby_vm_destruct, vm_memsize, vm_init2, Init_VM): ditto * vm_trace.c (clear_trace_func_i, rb_clear_trace_func): ditto * benchmark/bm_vm_thread_close.rb: added to show improvement * ccan/build_assert/build_assert.h: added as a dependency of list.h * ccan/check_type/check_type.h: ditto * ccan/container_of/container_of.h: ditto * ccan/licenses/BSD-MIT: ditto * ccan/licenses/CC0: ditto * ccan/str/str.h: ditto (stripped of unused macros) * ccan/list/list.h: ditto * common.mk: add CCAN_LIST_INCLUDES [ruby-core:61871][Feature 9632 (part 1)] Apologies for the size of this commit, but I think a good doubly-linked list will be useful for future features, too. This may be used to add ordering to a container_of-based hash table to preserve compatibility if required (e.g. feature 9614). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@45913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-05-11 03:48:51 +04:00
size += vmobj->living_thread_num * sizeof(rb_thread_t);
vm*: doubly-linked list from ccan to manage vm->living_threads A doubly-linked list for tracking living threads guarantees constant-time insert/delete performance with no corner cases of a hash table. I chose this ccan implementation of doubly-linked lists over the BSD sys/queue.h implementation since: 1) insertion and removal are both branchless 2) locality is improved if a struct may be a member of multiple lists (0002 patch in Feature 9632 will introduce a secondary list for waiting FDs) This also increases cache locality during iteration: improving performance in a new IO#close benchmark with many sleeping threads while still scanning the same number of threads. vm_thread_close 1.762 * vm_core.h (rb_vm_t): list_head and counter for living_threads (rb_thread_t): vmlt_node for living_threads linkage (rb_vm_living_threads_init): new function wrapper (rb_vm_living_threads_insert): ditto (rb_vm_living_threads_remove): ditto * vm.c (rb_vm_living_threads_foreach): new function wrapper * thread.c (terminate_i, thread_start_func_2, thread_create_core, thread_fd_close_i, thread_fd_close): update to use new APIs * vm.c (vm_mark_each_thread_func, rb_vm_mark, ruby_vm_destruct, vm_memsize, vm_init2, Init_VM): ditto * vm_trace.c (clear_trace_func_i, rb_clear_trace_func): ditto * benchmark/bm_vm_thread_close.rb: added to show improvement * ccan/build_assert/build_assert.h: added as a dependency of list.h * ccan/check_type/check_type.h: ditto * ccan/container_of/container_of.h: ditto * ccan/licenses/BSD-MIT: ditto * ccan/licenses/CC0: ditto * ccan/str/str.h: ditto (stripped of unused macros) * ccan/list/list.h: ditto * common.mk: add CCAN_LIST_INCLUDES [ruby-core:61871][Feature 9632 (part 1)] Apologies for the size of this commit, but I think a good doubly-linked list will be useful for future features, too. This may be used to add ordering to a container_of-based hash table to preserve compatibility if required (e.g. feature 9614). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@45913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-05-11 03:48:51 +04:00
if (vmobj->defined_strings) {
size += DEFINED_EXPR * sizeof(VALUE);
}
return size;
}
static const rb_data_type_t vm_data_type = {
"VM",
{NULL, NULL, vm_memsize,},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
static VALUE
vm_default_params(void)
{
rb_vm_t *vm = GET_VM();
VALUE result = rb_hash_new();
#define SET(name) rb_hash_aset(result, ID2SYM(rb_intern(#name)), SIZET2NUM(vm->default_params.name));
SET(thread_vm_stack_size);
SET(thread_machine_stack_size);
SET(fiber_vm_stack_size);
SET(fiber_machine_stack_size);
#undef SET
rb_obj_freeze(result);
return result;
}
static size_t
get_param(const char *name, size_t default_value, size_t min_value)
{
const char *envval;
size_t result = default_value;
if ((envval = getenv(name)) != 0) {
long val = atol(envval);
if (val < (long)min_value) {
val = (long)min_value;
}
result = (size_t)(((val -1 + RUBY_VM_SIZE_ALIGN) / RUBY_VM_SIZE_ALIGN) * RUBY_VM_SIZE_ALIGN);
}
if (0) fprintf(stderr, "%s: %"PRIuSIZE"\n", name, result); /* debug print */
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
return result;
}
static void
check_machine_stack_size(size_t *sizep)
{
#ifdef PTHREAD_STACK_MIN
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
size_t size = *sizep;
#endif
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
#ifdef PTHREAD_STACK_MIN
if (size < PTHREAD_STACK_MIN) {
*sizep = PTHREAD_STACK_MIN * 2;
}
#endif
}
static void
vm_default_params_setup(rb_vm_t *vm)
{
vm->default_params.thread_vm_stack_size =
get_param("RUBY_THREAD_VM_STACK_SIZE",
RUBY_VM_THREAD_VM_STACK_SIZE,
RUBY_VM_THREAD_VM_STACK_SIZE_MIN);
vm->default_params.thread_machine_stack_size =
get_param("RUBY_THREAD_MACHINE_STACK_SIZE",
RUBY_VM_THREAD_MACHINE_STACK_SIZE,
RUBY_VM_THREAD_MACHINE_STACK_SIZE_MIN);
vm->default_params.fiber_vm_stack_size =
get_param("RUBY_FIBER_VM_STACK_SIZE",
RUBY_VM_FIBER_VM_STACK_SIZE,
RUBY_VM_FIBER_VM_STACK_SIZE_MIN);
vm->default_params.fiber_machine_stack_size =
get_param("RUBY_FIBER_MACHINE_STACK_SIZE",
RUBY_VM_FIBER_MACHINE_STACK_SIZE,
RUBY_VM_FIBER_MACHINE_STACK_SIZE_MIN);
/* environment dependent check */
check_machine_stack_size(&vm->default_params.thread_machine_stack_size);
check_machine_stack_size(&vm->default_params.fiber_machine_stack_size);
}
static void
vm_init2(rb_vm_t *vm)
{
MEMZERO(vm, rb_vm_t, 1);
vm*: doubly-linked list from ccan to manage vm->living_threads A doubly-linked list for tracking living threads guarantees constant-time insert/delete performance with no corner cases of a hash table. I chose this ccan implementation of doubly-linked lists over the BSD sys/queue.h implementation since: 1) insertion and removal are both branchless 2) locality is improved if a struct may be a member of multiple lists (0002 patch in Feature 9632 will introduce a secondary list for waiting FDs) This also increases cache locality during iteration: improving performance in a new IO#close benchmark with many sleeping threads while still scanning the same number of threads. vm_thread_close 1.762 * vm_core.h (rb_vm_t): list_head and counter for living_threads (rb_thread_t): vmlt_node for living_threads linkage (rb_vm_living_threads_init): new function wrapper (rb_vm_living_threads_insert): ditto (rb_vm_living_threads_remove): ditto * vm.c (rb_vm_living_threads_foreach): new function wrapper * thread.c (terminate_i, thread_start_func_2, thread_create_core, thread_fd_close_i, thread_fd_close): update to use new APIs * vm.c (vm_mark_each_thread_func, rb_vm_mark, ruby_vm_destruct, vm_memsize, vm_init2, Init_VM): ditto * vm_trace.c (clear_trace_func_i, rb_clear_trace_func): ditto * benchmark/bm_vm_thread_close.rb: added to show improvement * ccan/build_assert/build_assert.h: added as a dependency of list.h * ccan/check_type/check_type.h: ditto * ccan/container_of/container_of.h: ditto * ccan/licenses/BSD-MIT: ditto * ccan/licenses/CC0: ditto * ccan/str/str.h: ditto (stripped of unused macros) * ccan/list/list.h: ditto * common.mk: add CCAN_LIST_INCLUDES [ruby-core:61871][Feature 9632 (part 1)] Apologies for the size of this commit, but I think a good doubly-linked list will be useful for future features, too. This may be used to add ordering to a container_of-based hash table to preserve compatibility if required (e.g. feature 9614). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@45913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-05-11 03:48:51 +04:00
rb_vm_living_threads_init(vm);
vm->thread_report_on_exception = 1;
vm->src_encoding_index = -1;
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
vm_default_params_setup(vm);
}
/* Thread */
#define USE_THREAD_DATA_RECYCLE 1
#if USE_THREAD_DATA_RECYCLE
#define RECYCLE_MAX 64
static VALUE *thread_recycle_stack_slot[RECYCLE_MAX];
static int thread_recycle_stack_count = 0;
static VALUE *
thread_recycle_stack(size_t size)
{
if (thread_recycle_stack_count > 0) {
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
/* TODO: check stack size if stack sizes are variable */
return thread_recycle_stack_slot[--thread_recycle_stack_count];
}
else {
return ALLOC_N(VALUE, size);
}
}
#else
#define thread_recycle_stack(size) ALLOC_N(VALUE, (size))
#endif
void
rb_thread_recycle_stack_release(VALUE *stack)
{
VM_ASSERT(stack != NULL);
#if USE_THREAD_DATA_RECYCLE
if (thread_recycle_stack_count < RECYCLE_MAX) {
thread_recycle_stack_slot[thread_recycle_stack_count++] = stack;
return;
}
#endif
ruby_xfree(stack);
}
void
rb_execution_context_mark(const rb_execution_context_t *ec)
{
#if VM_CHECK_MODE > 0
void rb_ec_verify(const rb_execution_context_t *ec); /* cont.c */
rb_ec_verify(ec);
#endif
/* mark VM stack */
if (ec->vm_stack) {
VALUE *p = ec->vm_stack;
VALUE *sp = ec->cfp->sp;
rb_control_frame_t *cfp = ec->cfp;
rb_control_frame_t *limit_cfp = (void *)(ec->vm_stack + ec->vm_stack_size);
rb_gc_mark_values((long)(sp - p), p);
while (cfp != limit_cfp) {
#if VM_CHECK_MODE > 0
const VALUE *ep = cfp->ep;
VM_ASSERT(!!VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED) == vm_ep_in_heap_p_(ec, ep));
#endif
rb_gc_mark(cfp->self);
2015-07-22 01:52:59 +03:00
rb_gc_mark((VALUE)cfp->iseq);
rb_gc_mark((VALUE)cfp->block_code);
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
}
/* mark machine stack */
if (ec->machine.stack_start && ec->machine.stack_end &&
ec != GET_EC() /* marked for current ec at the first stage of marking */
) {
rb_gc_mark_machine_stack(ec);
rb_gc_mark_locations((VALUE *)&ec->machine.regs,
(VALUE *)(&ec->machine.regs) +
sizeof(ec->machine.regs) / sizeof(VALUE));
}
RUBY_MARK_UNLESS_NULL(ec->errinfo);
RUBY_MARK_UNLESS_NULL(ec->root_svar);
rb_mark_tbl(ec->local_storage);
RUBY_MARK_UNLESS_NULL(ec->local_storage_recursive_hash);
RUBY_MARK_UNLESS_NULL(ec->local_storage_recursive_hash_for_trace);
}
void rb_fiber_mark_self(rb_fiber_t *fib);
void rb_threadptr_root_fiber_setup(rb_thread_t *th);
void rb_threadptr_root_fiber_release(rb_thread_t *th);
static void
thread_mark(void *ptr)
{
rb_thread_t *th = ptr;
RUBY_MARK_ENTER("thread");
rb_fiber_mark_self(th->ec->fiber_ptr);
/* mark ruby objects */
RUBY_MARK_UNLESS_NULL(th->first_proc);
if (th->first_proc) RUBY_MARK_UNLESS_NULL(th->first_args);
RUBY_MARK_UNLESS_NULL(th->thgroup);
RUBY_MARK_UNLESS_NULL(th->value);
RUBY_MARK_UNLESS_NULL(th->pending_interrupt_queue);
RUBY_MARK_UNLESS_NULL(th->pending_interrupt_mask_stack);
RUBY_MARK_UNLESS_NULL(th->top_self);
RUBY_MARK_UNLESS_NULL(th->top_wrapper);
if (th->root_fiber) rb_fiber_mark_self(th->root_fiber);
RUBY_MARK_UNLESS_NULL(th->stat_insn_usage);
RUBY_MARK_UNLESS_NULL(th->last_status);
RUBY_MARK_UNLESS_NULL(th->locking_mutex);
RUBY_MARK_UNLESS_NULL(th->name);
RUBY_MARK_LEAVE("thread");
}
static void
thread_free(void *ptr)
{
rb_thread_t *th = ptr;
RUBY_FREE_ENTER("thread");
if (th->locking_mutex != Qfalse) {
rb_bug("thread_free: locking_mutex must be NULL (%p:%p)", (void *)th, (void *)th->locking_mutex);
}
if (th->keeping_mutexes != NULL) {
rb_bug("thread_free: keeping_mutexes must be NULL (%p:%p)", (void *)th, (void *)th->keeping_mutexes);
}
rb_threadptr_root_fiber_release(th);
if (th->vm && th->vm->main_thread == th) {
RUBY_GC_INFO("main thread\n");
}
else {
#ifdef USE_SIGALTSTACK
if (th->altstack) {
free(th->altstack);
}
#endif
ruby_xfree(ptr);
}
RUBY_FREE_LEAVE("thread");
}
static size_t
thread_memsize(const void *ptr)
{
const rb_thread_t *th = ptr;
size_t size = sizeof(rb_thread_t);
if (!th->root_fiber) {
size += th->ec->vm_stack_size * sizeof(VALUE);
}
if (th->ec->local_storage) {
size += st_memsize(th->ec->local_storage);
}
return size;
}
#define thread_data_type ruby_threadptr_data_type
const rb_data_type_t ruby_threadptr_data_type = {
"VM/thread",
{
thread_mark,
thread_free,
thread_memsize,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
VALUE
rb_obj_is_thread(VALUE obj)
{
if (rb_typeddata_is_kind_of(obj, &thread_data_type)) {
return Qtrue;
}
else {
return Qfalse;
}
}
static VALUE
thread_alloc(VALUE klass)
{
VALUE obj;
rb_thread_t *th;
obj = TypedData_Make_Struct(klass, rb_thread_t, &thread_data_type, th);
return obj;
}
static void
th_init(rb_thread_t *th, VALUE self)
{
th->self = self;
rb_threadptr_root_fiber_setup(th);
/* allocate thread stack */
#ifdef USE_SIGALTSTACK
/* altstack of main thread is reallocated in another place */
th->altstack = malloc(rb_sigaltstack_size());
#endif
{
/* vm_stack_size is word number.
* th->vm->default_params.thread_vm_stack_size is byte size. */
size_t size = th->vm->default_params.thread_vm_stack_size / sizeof(VALUE);
ec_set_vm_stack(th->ec, thread_recycle_stack(size), size);
}
th->ec->cfp = (void *)(th->ec->vm_stack + th->ec->vm_stack_size);
vm_push_frame(th->ec, 0 /* dummy iseq */, VM_FRAME_MAGIC_DUMMY | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_CFRAME /* dummy frame */,
Qnil /* dummy self */, VM_BLOCK_HANDLER_NONE /* dummy block ptr */,
0 /* dummy cref/me */,
0 /* dummy pc */, th->ec->vm_stack, 0, 0);
th->status = THREAD_RUNNABLE;
th->last_status = Qnil;
th->ec->errinfo = Qnil;
th->ec->root_svar = Qfalse;
th->ec->local_storage_recursive_hash = Qnil;
th->ec->local_storage_recursive_hash_for_trace = Qnil;
#ifdef NON_SCALAR_THREAD_ID
th->thread_id_string[0] = '\0';
#endif
#if OPT_CALL_THREADED_CODE
th->retval = Qundef;
#endif
th->name = Qnil;
th->report_on_exception = th->vm->thread_report_on_exception;
}
static VALUE
ruby_thread_init(VALUE self)
{
rb_thread_t *th = rb_thread_ptr(self);
rb_vm_t *vm = GET_THREAD()->vm;
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
th->vm = vm;
th_init(th, self);
rb_ivar_set(self, rb_intern("locals"), rb_hash_new());
th->top_wrapper = 0;
th->top_self = rb_vm_top_self();
th->ec->root_svar = Qfalse;
return self;
}
VALUE
rb_thread_alloc(VALUE klass)
{
VALUE self = thread_alloc(klass);
ruby_thread_init(self);
return self;
}
static void
vm_define_method(VALUE obj, ID id, VALUE iseqval, int is_singleton)
{
VALUE klass;
rb_method_visibility_t visi;
rb_cref_t *cref = rb_vm_cref();
if (!is_singleton) {
klass = CREF_CLASS(cref);
visi = rb_scope_visibility_get();
}
else { /* singleton */
klass = rb_singleton_class(obj); /* class and frozen checked in this API */
visi = METHOD_VISI_PUBLIC;
}
if (NIL_P(klass)) {
rb_raise(rb_eTypeError, "no class/module to add method");
}
2015-07-22 01:52:59 +03:00
rb_add_method_iseq(klass, id, (const rb_iseq_t *)iseqval, cref, visi);
if (!is_singleton && rb_scope_module_func_check()) {
klass = rb_singleton_class(klass);
2015-07-22 01:52:59 +03:00
rb_add_method_iseq(klass, id, (const rb_iseq_t *)iseqval, cref, METHOD_VISI_PUBLIC);
}
}
#define REWIND_CFP(expr) do { \
rb_execution_context_t *ec__ = GET_EC(); \
VALUE *const curr_sp = (ec__->cfp++)->sp; \
VALUE *const saved_sp = ec__->cfp->sp; \
ec__->cfp->sp = curr_sp; \
expr; \
(ec__->cfp--)->sp = saved_sp; \
} while (0)
static VALUE
m_core_define_method(VALUE self, VALUE sym, VALUE iseqval)
{
REWIND_CFP({
vm_define_method(Qnil, SYM2ID(sym), iseqval, FALSE);
});
return sym;
}
static VALUE
m_core_define_singleton_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval)
{
REWIND_CFP({
vm_define_method(cbase, SYM2ID(sym), iseqval, TRUE);
});
return sym;
}
static VALUE
m_core_set_method_alias(VALUE self, VALUE cbase, VALUE sym1, VALUE sym2)
{
REWIND_CFP({
rb_alias(cbase, SYM2ID(sym1), SYM2ID(sym2));
});
return Qnil;
}
static VALUE
m_core_set_variable_alias(VALUE self, VALUE sym1, VALUE sym2)
{
REWIND_CFP({
rb_alias_variable(SYM2ID(sym1), SYM2ID(sym2));
});
return Qnil;
}
static VALUE
m_core_undef_method(VALUE self, VALUE cbase, VALUE sym)
{
REWIND_CFP({
rb_undef(cbase, SYM2ID(sym));
rb_clear_method_cache_by_class(self);
});
return Qnil;
}
static VALUE
m_core_set_postexe(VALUE self)
{
rb_set_end_proc(rb_call_end_proc, rb_block_proc());
return Qnil;
}
static VALUE core_hash_merge_ary(VALUE hash, VALUE ary);
static VALUE core_hash_from_ary(VALUE ary);
static VALUE core_hash_merge_kwd(int argc, VALUE *argv);
static VALUE
core_hash_merge(VALUE hash, long argc, const VALUE *argv)
{
Check_Type(hash, T_HASH);
VM_ASSERT(argc % 2 == 0);
rb_hash_bulk_insert(argc, argv, hash);
return hash;
}
static VALUE
m_core_hash_from_ary(VALUE self, VALUE ary)
{
VALUE hash;
REWIND_CFP(hash = core_hash_from_ary(ary));
return hash;
}
static VALUE
core_hash_from_ary(VALUE ary)
{
VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
RUBY_DTRACE_CREATE_HOOK(HASH, (Check_Type(ary, T_ARRAY), RARRAY_LEN(ary)));
return core_hash_merge_ary(hash, ary);
}
#if 0
static VALUE
m_core_hash_merge_ary(VALUE self, VALUE hash, VALUE ary)
{
REWIND_CFP(core_hash_merge_ary(hash, ary));
return hash;
}
#endif
static VALUE
core_hash_merge_ary(VALUE hash, VALUE ary)
{
Check_Type(ary, T_ARRAY);
core_hash_merge(hash, RARRAY_LEN(ary), RARRAY_CONST_PTR(ary));
return hash;
}
static VALUE
m_core_hash_merge_ptr(int argc, VALUE *argv, VALUE recv)
{
VALUE hash = argv[0];
REWIND_CFP(core_hash_merge(hash, argc-1, argv+1));
return hash;
}
static int
kwmerge_i(VALUE key, VALUE value, VALUE hash)
{
Check_Type(key, T_SYMBOL);
rb_hash_aset(hash, key, value);
return ST_CONTINUE;
}
static int
kwcheck_i(VALUE key, VALUE value, VALUE hash)
{
Check_Type(key, T_SYMBOL);
return ST_CONTINUE;
}
static VALUE
m_core_hash_merge_kwd(int argc, VALUE *argv, VALUE recv)
{
VALUE hash;
REWIND_CFP(hash = core_hash_merge_kwd(argc, argv));
return hash;
}
static VALUE
core_hash_merge_kwd(int argc, VALUE *argv)
{
VALUE hash, kw;
rb_check_arity(argc, 1, 2);
hash = argv[0];
kw = rb_to_hash_type(argv[argc-1]);
if (argc < 2) hash = kw;
rb_hash_foreach(kw, argc < 2 ? kwcheck_i : kwmerge_i, hash);
return hash;
}
extern VALUE *rb_gc_stack_start;
extern size_t rb_gc_stack_maxsize;
#ifdef __ia64
extern VALUE *rb_gc_register_stack_start;
#endif
/* debug functions */
/* :nodoc: */
static VALUE
sdr(void)
{
rb_vm_bugreport(NULL);
return Qnil;
}
/* :nodoc: */
static VALUE
nsdr(void)
{
VALUE ary = rb_ary_new();
#if HAVE_BACKTRACE
#include <execinfo.h>
#define MAX_NATIVE_TRACE 1024
static void *trace[MAX_NATIVE_TRACE];
int n = (int)backtrace(trace, MAX_NATIVE_TRACE);
char **syms = backtrace_symbols(trace, n);
int i;
if (syms == 0) {
rb_memerror();
}
for (i=0; i<n; i++) {
rb_ary_push(ary, rb_str_new2(syms[i]));
}
free(syms); /* OK */
#endif
return ary;
}
#if VM_COLLECT_USAGE_DETAILS
static VALUE usage_analysis_insn_stop(VALUE self);
static VALUE usage_analysis_operand_stop(VALUE self);
static VALUE usage_analysis_register_stop(VALUE self);
#endif
void
Init_VM(void)
{
VALUE opts;
VALUE klass;
VALUE fcore;
/* ::RubyVM */
rb_cRubyVM = rb_define_class("RubyVM", rb_cObject);
rb_undef_alloc_func(rb_cRubyVM);
rb_undef_method(CLASS_OF(rb_cRubyVM), "new");
rb_define_singleton_method(rb_cRubyVM, "stat", vm_stat, -1);
/* FrozenCore (hidden) */
fcore = rb_class_new(rb_cBasicObject);
RBASIC(fcore)->flags = T_ICLASS;
klass = rb_singleton_class(fcore);
rb_define_method_id(klass, id_core_set_method_alias, m_core_set_method_alias, 3);
rb_define_method_id(klass, id_core_set_variable_alias, m_core_set_variable_alias, 2);
rb_define_method_id(klass, id_core_undef_method, m_core_undef_method, 2);
rb_define_method_id(klass, id_core_define_method, m_core_define_method, 2);
rb_define_method_id(klass, id_core_define_singleton_method, m_core_define_singleton_method, 3);
rb_define_method_id(klass, id_core_set_postexe, m_core_set_postexe, 0);
rb_define_method_id(klass, id_core_hash_from_ary, m_core_hash_from_ary, 1);
#if 0
rb_define_method_id(klass, id_core_hash_merge_ary, m_core_hash_merge_ary, 2);
#endif
rb_define_method_id(klass, id_core_hash_merge_ptr, m_core_hash_merge_ptr, -1);
rb_define_method_id(klass, id_core_hash_merge_kwd, m_core_hash_merge_kwd, -1);
rb_define_method_id(klass, idProc, rb_block_proc, 0);
rb_define_method_id(klass, idLambda, rb_block_lambda, 0);
rb_obj_freeze(fcore);
RBASIC_CLEAR_CLASS(klass);
rb_obj_freeze(klass);
rb_gc_register_mark_object(fcore);
rb_mRubyVMFrozenCore = fcore;
/*
* Document-class: Thread
*
* Threads are the Ruby implementation for a concurrent programming model.
*
* Programs that require multiple threads of execution are a perfect
* candidate for Ruby's Thread class.
*
* For example, we can create a new thread separate from the main thread's
* execution using ::new.
*
* thr = Thread.new { puts "Whats the big deal" }
*
* Then we are able to pause the execution of the main thread and allow
* our new thread to finish, using #join:
*
* thr.join #=> "Whats the big deal"
*
* If we don't call +thr.join+ before the main thread terminates, then all
* other threads including +thr+ will be killed.
*
* Alternatively, you can use an array for handling multiple threads at
* once, like in the following example:
*
* threads = []
* threads << Thread.new { puts "Whats the big deal" }
* threads << Thread.new { 3.times { puts "Threads are fun!" } }
*
* After creating a few threads we wait for them all to finish
* consecutively.
*
* threads.each { |thr| thr.join }
*
* === Thread initialization
*
* In order to create new threads, Ruby provides ::new, ::start, and
* ::fork. A block must be provided with each of these methods, otherwise
* a ThreadError will be raised.
*
* When subclassing the Thread class, the +initialize+ method of your
* subclass will be ignored by ::start and ::fork. Otherwise, be sure to
* call super in your +initialize+ method.
*
* === Thread termination
*
* For terminating threads, Ruby provides a variety of ways to do this.
*
* The class method ::kill, is meant to exit a given thread:
*
* thr = Thread.new { ... }
* Thread.kill(thr) # sends exit() to thr
*
* Alternatively, you can use the instance method #exit, or any of its
* aliases #kill or #terminate.
*
* thr.exit
*
* === Thread status
*
* Ruby provides a few instance methods for querying the state of a given
* thread. To get a string with the current thread's state use #status
*
* thr = Thread.new { sleep }
* thr.status # => "sleep"
* thr.exit
* thr.status # => false
*
* You can also use #alive? to tell if the thread is running or sleeping,
* and #stop? if the thread is dead or sleeping.
*
* === Thread variables and scope
*
* Since threads are created with blocks, the same rules apply to other
* Ruby blocks for variable scope. Any local variables created within this
* block are accessible to only this thread.
*
* ==== Fiber-local vs. Thread-local
*
* Each fiber has its own bucket for Thread#[] storage. When you set a
* new fiber-local it is only accessible within this Fiber. To illustrate:
*
* Thread.new {
* Thread.current[:foo] = "bar"
* Fiber.new {
* p Thread.current[:foo] # => nil
* }.resume
* }.join
*
* This example uses #[] for getting and #[]= for setting fiber-locals,
* you can also use #keys to list the fiber-locals for a given
* thread and #key? to check if a fiber-local exists.
*
* When it comes to thread-locals, they are accessible within the entire
* scope of the thread. Given the following example:
*
* Thread.new{
* Thread.current.thread_variable_set(:foo, 1)
* p Thread.current.thread_variable_get(:foo) # => 1
* Fiber.new{
* Thread.current.thread_variable_set(:foo, 2)
* p Thread.current.thread_variable_get(:foo) # => 2
* }.resume
* p Thread.current.thread_variable_get(:foo) # => 2
* }.join
*
* You can see that the thread-local +:foo+ carried over into the fiber
* and was changed to +2+ by the end of the thread.
*
* This example makes use of #thread_variable_set to create new
* thread-locals, and #thread_variable_get to reference them.
*
* There is also #thread_variables to list all thread-locals, and
* #thread_variable? to check if a given thread-local exists.
*
* === Exception handling
*
* Any thread can raise an exception using the #raise instance method,
* which operates similarly to Kernel#raise.
*
* However, it's important to note that an exception that occurs in any
* thread except the main thread depends on #abort_on_exception. This
* option is +false+ by default, meaning that any unhandled exception will
* cause the thread to terminate silently when waited on by either #join
* or #value. You can change this default by either #abort_on_exception=
* +true+ or setting $DEBUG to +true+.
*
* With the addition of the class method ::handle_interrupt, you can now
* handle exceptions asynchronously with threads.
*
* === Scheduling
*
* Ruby provides a few ways to support scheduling threads in your program.
*
* The first way is by using the class method ::stop, to put the current
* running thread to sleep and schedule the execution of another thread.
*
* Once a thread is asleep, you can use the instance method #wakeup to
* mark your thread as eligible for scheduling.
*
* You can also try ::pass, which attempts to pass execution to another
* thread but is dependent on the OS whether a running thread will switch
* or not. The same goes for #priority, which lets you hint to the thread
* scheduler which threads you want to take precedence when passing
* execution. This method is also dependent on the OS and may be ignored
* on some platforms.
*
*/
rb_cThread = rb_define_class("Thread", rb_cObject);
rb_undef_alloc_func(rb_cThread);
#if VM_COLLECT_USAGE_DETAILS
/* ::RubyVM::USAGE_ANALYSIS_* */
#define define_usage_analysis_hash(name) /* shut up rdoc -C */ \
rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_" #name, rb_hash_new())
define_usage_analysis_hash(INSN);
define_usage_analysis_hash(REGS);
define_usage_analysis_hash(INSN_BIGRAM);
rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_INSN_STOP", usage_analysis_insn_stop, 0);
rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_OPERAND_STOP", usage_analysis_operand_stop, 0);
rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_REGISTER_STOP", usage_analysis_register_stop, 0);
#endif
/* ::RubyVM::OPTS, which shows vm build options */
rb_define_const(rb_cRubyVM, "OPTS", opts = rb_ary_new());
#if OPT_DIRECT_THREADED_CODE
rb_ary_push(opts, rb_str_new2("direct threaded code"));
#elif OPT_TOKEN_THREADED_CODE
rb_ary_push(opts, rb_str_new2("token threaded code"));
#elif OPT_CALL_THREADED_CODE
rb_ary_push(opts, rb_str_new2("call threaded code"));
#endif
#if OPT_STACK_CACHING
rb_ary_push(opts, rb_str_new2("stack caching"));
#endif
#if OPT_OPERANDS_UNIFICATION
rb_ary_push(opts, rb_str_new2("operands unification"));
#endif
#if OPT_INSTRUCTIONS_UNIFICATION
rb_ary_push(opts, rb_str_new2("instructions unification"));
#endif
#if OPT_INLINE_METHOD_CACHE
rb_ary_push(opts, rb_str_new2("inline method cache"));
#endif
#if OPT_BLOCKINLINING
rb_ary_push(opts, rb_str_new2("block inlining"));
#endif
/* ::RubyVM::INSTRUCTION_NAMES */
rb_define_const(rb_cRubyVM, "INSTRUCTION_NAMES", rb_insns_name_array());
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
/* ::RubyVM::DEFAULT_PARAMS
* This constant variable shows VM's default parameters.
* Note that changing these values does not affect VM execution.
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
* Specification is not stable and you should not depend on this value.
* Of course, this constant is MRI specific.
*/
rb_define_const(rb_cRubyVM, "DEFAULT_PARAMS", vm_default_params());
/* debug functions ::RubyVM::SDR(), ::RubyVM::NSDR() */
#if VMDEBUG
rb_define_singleton_method(rb_cRubyVM, "SDR", sdr, 0);
rb_define_singleton_method(rb_cRubyVM, "NSDR", nsdr, 0);
#else
(void)sdr;
(void)nsdr;
#endif
/* VM bootstrap: phase 2 */
{
rb_vm_t *vm = ruby_current_vm_ptr;
rb_thread_t *th = GET_THREAD();
VALUE filename = rb_fstring_cstr("<main>");
2015-07-22 01:52:59 +03:00
const rb_iseq_t *iseq = rb_iseq_new(0, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
volatile VALUE th_self;
/* create vm object */
vm->self = TypedData_Wrap_Struct(rb_cRubyVM, &vm_data_type, vm);
/* create main thread */
th_self = th->self = TypedData_Wrap_Struct(rb_cThread, &thread_data_type, th);
rb_iv_set(th_self, "locals", rb_hash_new());
vm->main_thread = th;
vm->running_thread = th;
th->vm = vm;
th->top_wrapper = 0;
th->top_self = rb_vm_top_self();
rb_thread_set_current(th);
vm*: doubly-linked list from ccan to manage vm->living_threads A doubly-linked list for tracking living threads guarantees constant-time insert/delete performance with no corner cases of a hash table. I chose this ccan implementation of doubly-linked lists over the BSD sys/queue.h implementation since: 1) insertion and removal are both branchless 2) locality is improved if a struct may be a member of multiple lists (0002 patch in Feature 9632 will introduce a secondary list for waiting FDs) This also increases cache locality during iteration: improving performance in a new IO#close benchmark with many sleeping threads while still scanning the same number of threads. vm_thread_close 1.762 * vm_core.h (rb_vm_t): list_head and counter for living_threads (rb_thread_t): vmlt_node for living_threads linkage (rb_vm_living_threads_init): new function wrapper (rb_vm_living_threads_insert): ditto (rb_vm_living_threads_remove): ditto * vm.c (rb_vm_living_threads_foreach): new function wrapper * thread.c (terminate_i, thread_start_func_2, thread_create_core, thread_fd_close_i, thread_fd_close): update to use new APIs * vm.c (vm_mark_each_thread_func, rb_vm_mark, ruby_vm_destruct, vm_memsize, vm_init2, Init_VM): ditto * vm_trace.c (clear_trace_func_i, rb_clear_trace_func): ditto * benchmark/bm_vm_thread_close.rb: added to show improvement * ccan/build_assert/build_assert.h: added as a dependency of list.h * ccan/check_type/check_type.h: ditto * ccan/container_of/container_of.h: ditto * ccan/licenses/BSD-MIT: ditto * ccan/licenses/CC0: ditto * ccan/str/str.h: ditto (stripped of unused macros) * ccan/list/list.h: ditto * common.mk: add CCAN_LIST_INCLUDES [ruby-core:61871][Feature 9632 (part 1)] Apologies for the size of this commit, but I think a good doubly-linked list will be useful for future features, too. This may be used to add ordering to a container_of-based hash table to preserve compatibility if required (e.g. feature 9614). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@45913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-05-11 03:48:51 +04:00
rb_vm_living_threads_insert(vm, th);
2015-07-22 01:52:59 +03:00
rb_gc_register_mark_object((VALUE)iseq);
th->ec->cfp->iseq = iseq;
th->ec->cfp->pc = iseq->body->iseq_encoded;
th->ec->cfp->self = th->top_self;
VM_ENV_FLAGS_UNSET(th->ec->cfp->ep, VM_FRAME_FLAG_CFRAME);
VM_STACK_ENV_WRITE(th->ec->cfp->ep, VM_ENV_DATA_INDEX_ME_CREF, (VALUE)vm_cref_new(rb_cObject, METHOD_VISI_PRIVATE, FALSE, NULL, FALSE));
/*
* The Binding of the top level scope
*/
rb_define_global_const("TOPLEVEL_BINDING", rb_binding_new());
}
vm_init_redefined_flag();
/* vm_backtrace.c */
Init_vm_backtrace();
VM_PROFILE_ATEXIT();
}
void
rb_vm_set_progname(VALUE filename)
{
rb_thread_t *th = GET_VM()->main_thread;
rb_control_frame_t *cfp = (void *)(th->ec->vm_stack + th->ec->vm_stack_size);
--cfp;
rb_iseq_pathobj_set(cfp->iseq, rb_str_dup(filename), rb_iseq_realpath(cfp->iseq));
}
extern const struct st_hash_type rb_fstring_hash_type;
void
Init_BareVM(void)
{
/* VM bootstrap: phase 1 */
rb_vm_t * vm = ruby_mimmalloc(sizeof(*vm));
rb_thread_t * th = ruby_mimmalloc(sizeof(*th));
if (!vm || !th) {
fprintf(stderr, "[FATAL] failed to allocate memory\n");
exit(EXIT_FAILURE);
}
MEMZERO(th, rb_thread_t, 1);
vm_init2(vm);
vm->objspace = rb_objspace_alloc();
ruby_current_vm_ptr = vm;
Init_native_thread(th);
th->vm = vm;
* vm.c: support variable VM/Machine stack sizes. Specified by the following environment variaables: - RUBY_THREAD_VM_STACK_SIZE: vm stack size used at thread creation. default: 128KB (32bit CPU) or 256KB (64bit CPU). - RUBY_THREAD_MACHINE_STACK_SIZE: machine stack size used at thread creation. default: 512KB or 1024KB. - RUBY_FIBER_VM_STACK_SIZE: vm stack size used at fiber creation. default: 64KB or 128KB. - RUBY_FIBER_MACHINE_STACK_SIZE: machine stack size used at fiber creation. default: 256KB or 256KB. This values are specified at launched timing. You can not change these values at running time. Environ variables are only *hints* because: - They are aligned to 4KB. - They have minimum values (depend on OSs). - Machine stack settings are ignored by some OSs. Default values especially fiber stack sizes are increased. This change affect Fiber's behavior: (1) You can run more complex program on a Fiber. (2) You can not make many (thousands) Fibers because of lack of address space (on 32bit CPU). If (2) bothers you, (a) Use 64bit CPU with big memory, or (b) Specify RUBY_FIBER_(VM|MACHINE)_STACK_SIZE correctly. You need to choose correct stack size carefully. These values are completely rely on systems (OS/compiler and so on). * vm_core.h (rb_vm_t::default_params): add to record above settings. * vm.c (RubyVM::DEFAULT_PARAMS): add new constant to see above setting. * thread_pthread.c: support RUBY_THREAD_MACHINE_STACK_SIZE. * cont.c: support RUBY_FIBER_(VM|MACHINE)_STACK_SIZE. * test/ruby/test_fiber.rb: add tests for above. * test/ruby/test_thread.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38478 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-20 02:29:18 +04:00
th_init(th, 0);
rb_thread_set_current_raw(th);
ruby_thread_init_stack(th);
}
void
Init_vm_objects(void)
{
rb_vm_t *vm = GET_VM();
vm->defined_module_hash = rb_hash_new();
/* initialize mark object array, hash */
vm->mark_object_ary = rb_ary_tmp_new(128);
vm->loading_table = st_init_strtable();
vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 1000);
}
/* top self */
static VALUE
main_to_s(VALUE obj)
{
return rb_str_new2("main");
}
VALUE
rb_vm_top_self(void)
{
return GET_VM()->top_self;
}
void
Init_top_self(void)
{
rb_vm_t *vm = GET_VM();
vm->top_self = rb_obj_alloc(rb_cObject);
rb_define_singleton_method(rb_vm_top_self(), "to_s", main_to_s, 0);
rb_define_alias(rb_singleton_class(rb_vm_top_self()), "inspect", "to_s");
}
static VALUE *
ruby_vm_verbose_ptr(rb_vm_t *vm)
{
return &vm->verbose;
}
static VALUE *
ruby_vm_debug_ptr(rb_vm_t *vm)
{
return &vm->debug;
}
VALUE *
rb_ruby_verbose_ptr(void)
{
return ruby_vm_verbose_ptr(GET_VM());
}
VALUE *
rb_ruby_debug_ptr(void)
{
return ruby_vm_debug_ptr(GET_VM());
}
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-13 01:52:12 +04:00
/* iseq.c */
2015-07-22 01:52:59 +03:00
VALUE rb_insn_operand_intern(const rb_iseq_t *iseq,
VALUE insn, int op_no, VALUE op,
int len, size_t pos, VALUE *pnop, VALUE child);
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-13 01:52:12 +04:00
st_table *
rb_vm_fstring_table(void)
{
return GET_VM()->frozen_strings;
}
#if VM_COLLECT_USAGE_DETAILS
#define HASH_ASET(h, k, v) rb_hash_aset((h), (st_data_t)(k), (st_data_t)(v))
/* uh = {
* insn(Fixnum) => ihash(Hash)
* }
* ihash = {
* -1(Fixnum) => count, # insn usage
* 0(Fixnum) => ophash, # operand usage
* }
* ophash = {
* val(interned string) => count(Fixnum)
* }
*/
static void
vm_analysis_insn(int insn)
{
ID usage_hash;
ID bigram_hash;
static int prev_insn = -1;
VALUE uh;
VALUE ihash;
VALUE cv;
CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
CONST_ID(bigram_hash, "USAGE_ANALYSIS_INSN_BIGRAM");
uh = rb_const_get(rb_cRubyVM, usage_hash);
if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) {
ihash = rb_hash_new();
HASH_ASET(uh, INT2FIX(insn), ihash);
}
if ((cv = rb_hash_aref(ihash, INT2FIX(-1))) == Qnil) {
cv = INT2FIX(0);
}
HASH_ASET(ihash, INT2FIX(-1), INT2FIX(FIX2INT(cv) + 1));
/* calc bigram */
if (prev_insn != -1) {
VALUE bi;
VALUE ary[2];
VALUE cv;
ary[0] = INT2FIX(prev_insn);
ary[1] = INT2FIX(insn);
bi = rb_ary_new4(2, &ary[0]);
uh = rb_const_get(rb_cRubyVM, bigram_hash);
if ((cv = rb_hash_aref(uh, bi)) == Qnil) {
cv = INT2FIX(0);
}
HASH_ASET(uh, bi, INT2FIX(FIX2INT(cv) + 1));
}
prev_insn = insn;
}
static void
vm_analysis_operand(int insn, int n, VALUE op)
{
ID usage_hash;
VALUE uh;
VALUE ihash;
VALUE ophash;
VALUE valstr;
VALUE cv;
CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
uh = rb_const_get(rb_cRubyVM, usage_hash);
if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) {
ihash = rb_hash_new();
HASH_ASET(uh, INT2FIX(insn), ihash);
}
if ((ophash = rb_hash_aref(ihash, INT2FIX(n))) == Qnil) {
ophash = rb_hash_new();
HASH_ASET(ihash, INT2FIX(n), ophash);
}
/* intern */
valstr = rb_insn_operand_intern(GET_EC()->cfp->iseq, insn, n, op, 0, 0, 0, 0);
/* set count */
if ((cv = rb_hash_aref(ophash, valstr)) == Qnil) {
cv = INT2FIX(0);
}
HASH_ASET(ophash, valstr, INT2FIX(FIX2INT(cv) + 1));
}
static void
vm_analysis_register(int reg, int isset)
{
ID usage_hash;
VALUE uh;
VALUE valstr;
static const char regstrs[][5] = {
"pc", /* 0 */
"sp", /* 1 */
"ep", /* 2 */
"cfp", /* 3 */
"self", /* 4 */
"iseq", /* 5 */
};
static const char getsetstr[][4] = {
"get",
"set",
};
static VALUE syms[sizeof(regstrs) / sizeof(regstrs[0])][2];
VALUE cv;
CONST_ID(usage_hash, "USAGE_ANALYSIS_REGS");
if (syms[0] == 0) {
char buff[0x10];
int i;
for (i = 0; i < (int)(sizeof(regstrs) / sizeof(regstrs[0])); i++) {
int j;
for (j = 0; j < 2; j++) {
snprintf(buff, 0x10, "%d %s %-4s", i, getsetstr[j], regstrs[i]);
syms[i][j] = ID2SYM(rb_intern(buff));
}
}
}
valstr = syms[reg][isset];
uh = rb_const_get(rb_cRubyVM, usage_hash);
if ((cv = rb_hash_aref(uh, valstr)) == Qnil) {
cv = INT2FIX(0);
}
HASH_ASET(uh, valstr, INT2FIX(FIX2INT(cv) + 1));
}
#undef HASH_ASET
void (*ruby_vm_collect_usage_func_insn)(int insn) = vm_analysis_insn;
void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = vm_analysis_operand;
void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = vm_analysis_register;
/* :nodoc: */
static VALUE
usage_analysis_insn_stop(VALUE self)
{
ruby_vm_collect_usage_func_insn = 0;
return Qnil;
}
/* :nodoc: */
static VALUE
usage_analysis_operand_stop(VALUE self)
{
ruby_vm_collect_usage_func_operand = 0;
return Qnil;
}
/* :nodoc: */
static VALUE
usage_analysis_register_stop(VALUE self)
{
ruby_vm_collect_usage_func_register = 0;
return Qnil;
}
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-13 01:52:12 +04:00
#else
void (*ruby_vm_collect_usage_func_insn)(int insn) = NULL;
void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = NULL;
void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = NULL;
#endif
#if VM_COLLECT_USAGE_DETAILS
/* @param insn instruction number */
static void
vm_collect_usage_insn(int insn)
{
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-13 01:52:12 +04:00
if (RUBY_DTRACE_INSN_ENABLED()) {
RUBY_DTRACE_INSN(rb_insns_name(insn));
}
if (ruby_vm_collect_usage_func_insn)
(*ruby_vm_collect_usage_func_insn)(insn);
}
/* @param insn instruction number
* @param n n-th operand
* @param op operand value
*/
static void
vm_collect_usage_operand(int insn, int n, VALUE op)
{
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-13 01:52:12 +04:00
if (RUBY_DTRACE_INSN_OPERAND_ENABLED()) {
VALUE valstr;
valstr = rb_insn_operand_intern(GET_EC()->cfp->iseq, insn, n, op, 0, 0, 0, 0);
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-13 01:52:12 +04:00
RUBY_DTRACE_INSN_OPERAND(RSTRING_PTR(valstr), rb_insns_name(insn));
RB_GC_GUARD(valstr);
* probes.d: add DTrace probe declarations. [ruby-core:27448] * array.c (empty_ary_alloc, ary_new): added array create DTrace probe. * compile.c (rb_insns_name): allowing DTrace probes to access instruction sequence name. * Makefile.in: translate probes.d file to appropriate header file. * common.mk: declare dependencies on the DTrace header. * configure.in: add a test for existence of DTrace. * eval.c (setup_exception): add a probe for when an exception is raised. * gc.c: Add DTrace probes for mark begin and end, and sweep begin and end. * hash.c (empty_hash_alloc): Add a probe for hash allocation. * insns.def: Add probes for function entry and return. * internal.h: function declaration for compile.c change. * load.c (rb_f_load): add probes for `load` entry and exit, require entry and exit, and wrapping search_required for load path search. * object.c (rb_obj_alloc): added a probe for general object creation. * parse.y (yycompile0): added a probe around parse and compile phase. * string.c (empty_str_alloc, str_new): DTrace probes for string allocation. * test/dtrace/*: tests for DTrace probes. * vm.c (vm_invoke_proc): add probes for function return on exception raise, hash create, and instruction sequence execution. * vm_core.h: add probe declarations for function entry and exit. * vm_dump.c: add probes header file. * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on function entry and return. * vm_exec.c: expose instruction number to instruction name function. * vm_insnshelper.c: add function entry and exit probes for cfunc methods. * vm_insnhelper.h: vm usage information is always collected, so uncomment the functions. 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org> * configure.in (isinf, isnan): isinf() and isnan() are macros on DragonFly which cannot be found by AC_REPLACE_FUNCS(). This workaround enforces the fact that they exist on DragonFly. 12 15:59:38 2012 Shugo Maeda <shugo@ruby-lang.org> * vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo), vm_insnhelper.c (vm_search_method): revert r37616 because it's too slow. [ruby-dev:46477] * test/ruby/test_refinement.rb (test_inline_method_cache): skip the test until the bug is fixed efficiently. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-11-13 01:52:12 +04:00
}
if (ruby_vm_collect_usage_func_operand)
(*ruby_vm_collect_usage_func_operand)(insn, n, op);
}
/* @param reg register id. see code of vm_analysis_register() */
/* @param isset 0: read, 1: write */
static void
vm_collect_usage_register(int reg, int isset)
{
if (ruby_vm_collect_usage_func_register)
(*ruby_vm_collect_usage_func_register)(reg, isset);
}
#endif
#include "vm_call_iseq_optimized.inc" /* required from vm_insnhelper.c */