Project

General

Profile

Bug #15313 ยป 0001-Add-a-tail-call-predicate-for-TracePoint.patch

alanwu (Alan Wu), 11/22/2018 05:45 PM

View differences:

include/ruby/debug.h
82 82
VALUE rb_tracearg_return_value(rb_trace_arg_t *trace_arg);
83 83
VALUE rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg);
84 84
VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg);
85
VALUE rb_tracearg_is_tailcall(rb_trace_arg_t *trace_arg);
85 86

  
86 87
/*
87 88
 * Postponed Job API
spec/ruby/core/tracepoint/tailcall_spec.rb
1
require_relative '../../spec_helper'
2

  
3
describe 'TracePoint#tailcall?' do
4
  it 'returns true when a tailcall happens' do
5
    iseq = RubyVM::InstructionSequence.compile(<<-RUBY, "#{__FILE__}:#{__LINE__}", tailcall_optimization: true)
6
      Module.new do
7
        def self.leaf(foo, bar)
8
          foo * bar
9
        end
10

  
11
        def self.middle
12
          leaf(2, 3)
13
        end
14

  
15
        def self.tco
16
          leaf(1, 9)
17
          middle
18
        end
19
      end
20
    RUBY
21

  
22
    mod = iseq.eval
23
    events = []
24
    TracePoint.new(:call) { |tp| events << [tp.method_id, tp.tailcall?] }.enable do
25
      mod.tco
26
      mod.leaf(2, 3)
27
    end
28

  
29
    events.should == [
30
      [:tco,    false],
31
      [:leaf,   false],
32
      [:middle, true],
33
      [:leaf,   true],
34
      [:leaf,   false]
35
    ]
36
  end
37
end
test/ruby/test_settracefunc.rb
440 440
    begin
441 441
    eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
442 442
    1: trace = TracePoint.trace(*trace_events){|tp| next if !target_thread?
443
    2:   events << [tp.event, tp.lineno, tp.path, _defined_class.(tp), tp.method_id, tp.self, tp.binding.eval("_local_var"), _get_data.(tp)] if tp.path == 'xyzzy'
443
    2:   events << [tp.event, tp.lineno, tp.path, _defined_class.(tp), tp.method_id, tp.self, tp.binding.eval("_local_var"), _get_data.(tp), tp.tailcall?] if tp.path == 'xyzzy'
444 444
    3: }
445 445
    4: 1.times{|;_local_var| _local_var = :inner
446 446
    5:   tap{}
......
468 468

  
469 469
    answer_events = [
470 470
     #
471
     [:c_return, 1, "xyzzy", TracePoint,  :trace,           TracePoint,  :outer,  trace],
472
     [:line,     4, 'xyzzy', self.class,  method,           self,        :outer, :nothing],
473
     [:c_call,   4, 'xyzzy', Integer,     :times,           1,           :outer, :nothing],
474
     [:line,     4, 'xyzzy', self.class,  method,           self,        nil,    :nothing],
475
     [:line,     5, 'xyzzy', self.class,  method,           self,        :inner, :nothing],
476
     [:c_call,   5, 'xyzzy', Kernel,      :tap,             self,        :inner, :nothing],
477
     [:c_return, 5, "xyzzy", Kernel,      :tap,             self,        :inner, self],
478
     [:c_return, 4, "xyzzy", Integer,     :times,           1,           :outer, 1],
479
     [:line,     7, 'xyzzy', self.class,  method,           self,        :outer, :nothing],
480
     [:c_call,   7, "xyzzy", Class,       :inherited,       Object,      :outer, :nothing],
481
     [:c_return, 7, "xyzzy", Class,       :inherited,       Object,      :outer, nil],
482
     [:class,    7, "xyzzy", nil,         nil,              xyzzy.class, nil,    :nothing],
483
     [:line,     8, "xyzzy", nil,         nil,              xyzzy.class, nil,    :nothing],
484
     [:line,     9, "xyzzy", nil,         nil,              xyzzy.class, :XYZZY_outer, :nothing],
485
     [:c_call,   9, "xyzzy", Module,      :method_added,    xyzzy.class, :XYZZY_outer, :nothing],
486
     [:c_return, 9, "xyzzy", Module,      :method_added,    xyzzy.class, :XYZZY_outer, nil],
487
     [:line,    13, "xyzzy", nil,         nil,              xyzzy.class, :XYZZY_outer, :nothing],
488
     [:c_call,  13, "xyzzy", Module,      :method_added,    xyzzy.class, :XYZZY_outer, :nothing],
489
     [:c_return,13, "xyzzy", Module,      :method_added,    xyzzy.class, :XYZZY_outer, nil],
490
     [:end,     17, "xyzzy", nil,         nil,              xyzzy.class, :XYZZY_outer, :nothing],
491
     [:line,    18, "xyzzy", TestSetTraceFunc, method,      self,        :outer, :nothing],
492
     [:c_call,  18, "xyzzy", Class,       :new,             xyzzy.class, :outer, :nothing],
493
     [:c_call,  18, "xyzzy", BasicObject, :initialize,      xyzzy,       :outer, :nothing],
494
     [:c_return,18, "xyzzy", BasicObject, :initialize,      xyzzy,       :outer, nil],
495
     [:c_return,18, "xyzzy", Class,       :new,             xyzzy.class, :outer, xyzzy],
496
     [:line,    19, "xyzzy", TestSetTraceFunc, method,      self, :outer, :nothing],
497
     [:call,     9, "xyzzy", xyzzy.class, :foo,             xyzzy,       nil,  :nothing],
498
     [:line,    10, "xyzzy", xyzzy.class, :foo,             xyzzy,       nil,  :nothing],
499
     [:line,    11, "xyzzy", xyzzy.class, :foo,             xyzzy,       :XYZZY_foo, :nothing],
500
     [:call,    13, "xyzzy", xyzzy.class, :bar,             xyzzy,       nil, :nothing],
501
     [:line,    14, "xyzzy", xyzzy.class, :bar,             xyzzy,       nil, :nothing],
502
     [:line,    15, "xyzzy", xyzzy.class, :bar,             xyzzy,       :XYZZY_bar, :nothing],
503
     [:c_call,  15, "xyzzy", Kernel,      :tap,             xyzzy,       :XYZZY_bar, :nothing],
504
     [:c_return,15, "xyzzy", Kernel,      :tap,             xyzzy,       :XYZZY_bar, xyzzy],
505
     [:return,  16, "xyzzy", xyzzy.class, :bar,             xyzzy,       :XYZZY_bar, xyzzy],
506
     [:return,  12, "xyzzy", xyzzy.class, :foo,             xyzzy,       :XYZZY_foo, xyzzy],
507
     [:line,    20, "xyzzy", TestSetTraceFunc, method,      self,        :outer, :nothing],
508
     [:c_call,  20, "xyzzy", Kernel,      :raise,           self,        :outer, :nothing],
509
     [:c_call,  20, "xyzzy", Exception,   :exception,       RuntimeError, :outer, :nothing],
510
     [:c_call,  20, "xyzzy", Exception,   :initialize,      raised_exc,  :outer, :nothing],
511
     [:c_return,20, "xyzzy", Exception,   :initialize,      raised_exc,  :outer, raised_exc],
512
     [:c_return,20, "xyzzy", Exception,   :exception,       RuntimeError, :outer, raised_exc],
513
     [:c_return,20, "xyzzy", Kernel,      :raise,           self,        :outer, nil],
514
     [:c_call,  20, "xyzzy", Exception,   :backtrace,       raised_exc,  :outer, :nothing],
515
     [:c_return,20, "xyzzy", Exception,   :backtrace,       raised_exc,  :outer, nil],
516
     [:raise,   20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc],
517
     [:c_call,  20, "xyzzy", Module,      :===,             RuntimeError,:outer, :nothing],
518
     [:c_return,20, "xyzzy", Module,      :===,             RuntimeError,:outer, true],
519
     [:line,    21, "xyzzy", TestSetTraceFunc, method,      self,        :outer, :nothing],
520
     [:c_call,  21, "xyzzy", TracePoint,  :disable,         trace,       :outer, :nothing],
471
     [:c_return, 1, "xyzzy", TracePoint,  :trace,           TracePoint,  :outer,  trace, false],
472
     [:line,     4, 'xyzzy', self.class,  method,           self,        :outer, :nothing, false],
473
     [:c_call,   4, 'xyzzy', Integer,     :times,           1,           :outer, :nothing, false],
474
     [:line,     4, 'xyzzy', self.class,  method,           self,        nil,    :nothing, false],
475
     [:line,     5, 'xyzzy', self.class,  method,           self,        :inner, :nothing, false],
476
     [:c_call,   5, 'xyzzy', Kernel,      :tap,             self,        :inner, :nothing, false],
477
     [:c_return, 5, "xyzzy", Kernel,      :tap,             self,        :inner, self, false],
478
     [:c_return, 4, "xyzzy", Integer,     :times,           1,           :outer, 1, false],
479
     [:line,     7, 'xyzzy', self.class,  method,           self,        :outer, :nothing, false],
480
     [:c_call,   7, "xyzzy", Class,       :inherited,       Object,      :outer, :nothing, false],
481
     [:c_return, 7, "xyzzy", Class,       :inherited,       Object,      :outer, nil, false],
482
     [:class,    7, "xyzzy", nil,         nil,              xyzzy.class, nil,    :nothing, false],
483
     [:line,     8, "xyzzy", nil,         nil,              xyzzy.class, nil,    :nothing, false],
484
     [:line,     9, "xyzzy", nil,         nil,              xyzzy.class, :XYZZY_outer, :nothing, false],
485
     [:c_call,   9, "xyzzy", Module,      :method_added,    xyzzy.class, :XYZZY_outer, :nothing, false],
486
     [:c_return, 9, "xyzzy", Module,      :method_added,    xyzzy.class, :XYZZY_outer, nil, false],
487
     [:line,    13, "xyzzy", nil,         nil,              xyzzy.class, :XYZZY_outer, :nothing, false],
488
     [:c_call,  13, "xyzzy", Module,      :method_added,    xyzzy.class, :XYZZY_outer, :nothing, false],
489
     [:c_return,13, "xyzzy", Module,      :method_added,    xyzzy.class, :XYZZY_outer, nil, false],
490
     [:end,     17, "xyzzy", nil,         nil,              xyzzy.class, :XYZZY_outer, :nothing, false],
491
     [:line,    18, "xyzzy", TestSetTraceFunc, method,      self,        :outer, :nothing, false],
492
     [:c_call,  18, "xyzzy", Class,       :new,             xyzzy.class, :outer, :nothing, false],
493
     [:c_call,  18, "xyzzy", BasicObject, :initialize,      xyzzy,       :outer, :nothing, false],
494
     [:c_return,18, "xyzzy", BasicObject, :initialize,      xyzzy,       :outer, nil, false],
495
     [:c_return,18, "xyzzy", Class,       :new,             xyzzy.class, :outer, xyzzy, false],
496
     [:line,    19, "xyzzy", TestSetTraceFunc, method,      self, :outer, :nothing, false],
497
     [:call,     9, "xyzzy", xyzzy.class, :foo,             xyzzy,       nil,  :nothing, false],
498
     [:line,    10, "xyzzy", xyzzy.class, :foo,             xyzzy,       nil,  :nothing, false],
499
     [:line,    11, "xyzzy", xyzzy.class, :foo,             xyzzy,       :XYZZY_foo, :nothing, false],
500
     [:call,    13, "xyzzy", xyzzy.class, :bar,             xyzzy,       nil, :nothing, false],
501
     [:line,    14, "xyzzy", xyzzy.class, :bar,             xyzzy,       nil, :nothing, false],
502
     [:line,    15, "xyzzy", xyzzy.class, :bar,             xyzzy,       :XYZZY_bar, :nothing, false],
503
     [:c_call,  15, "xyzzy", Kernel,      :tap,             xyzzy,       :XYZZY_bar, :nothing, false],
504
     [:c_return,15, "xyzzy", Kernel,      :tap,             xyzzy,       :XYZZY_bar, xyzzy, false],
505
     [:return,  16, "xyzzy", xyzzy.class, :bar,             xyzzy,       :XYZZY_bar, xyzzy, false],
506
     [:return,  12, "xyzzy", xyzzy.class, :foo,             xyzzy,       :XYZZY_foo, xyzzy, false],
507
     [:line,    20, "xyzzy", TestSetTraceFunc, method,      self,        :outer, :nothing, false],
508
     [:c_call,  20, "xyzzy", Kernel,      :raise,           self,        :outer, :nothing, false],
509
     [:c_call,  20, "xyzzy", Exception,   :exception,       RuntimeError, :outer, :nothing, false],
510
     [:c_call,  20, "xyzzy", Exception,   :initialize,      raised_exc,  :outer, :nothing, false],
511
     [:c_return,20, "xyzzy", Exception,   :initialize,      raised_exc,  :outer, raised_exc, false],
512
     [:c_return,20, "xyzzy", Exception,   :exception,       RuntimeError, :outer, raised_exc, false],
513
     [:c_return,20, "xyzzy", Kernel,      :raise,           self,        :outer, nil, false],
514
     [:c_call,  20, "xyzzy", Exception,   :backtrace,       raised_exc,  :outer, :nothing, false],
515
     [:c_return,20, "xyzzy", Exception,   :backtrace,       raised_exc,  :outer, nil, false],
516
     [:raise,   20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc, false],
517
     [:c_call,  20, "xyzzy", Module,      :===,             RuntimeError,:outer, :nothing, false],
518
     [:c_return,20, "xyzzy", Module,      :===,             RuntimeError,:outer, true, false],
519
     [:line,    21, "xyzzy", TestSetTraceFunc, method,      self,        :outer, :nothing, false],
520
     [:c_call,  21, "xyzzy", TracePoint,  :disable,         trace,       :outer, :nothing, false],
521 521
     ]
522 522

  
523 523
    return events, answer_events
vm.c
1025 1025
		  iseq->body->stack_max);
1026 1026

  
1027 1027
    RUBY_DTRACE_METHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
1028
    EXEC_EVENT_HOOK(ec, RUBY_EVENT_CALL, self, me->def->original_id, me->called_id, me->owner, Qnil);
1028
    EXEC_EVENT_HOOK(ec, RUBY_EVENT_CALL, self, me->def->original_id, me->called_id, me->owner, Qfalse);
1029 1029
    VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);
1030 1030
    ret = vm_exec(ec, TRUE);
1031 1031
    EXEC_EVENT_HOOK(ec, RUBY_EVENT_RETURN, self, me->def->original_id, me->called_id, me->owner, ret);
vm_core.h
1115 1115

  
1116 1116
enum {
1117 1117
    /* Frame/Environment flag bits:
1118
     *   MMMM MMMM MMMM MMMM ____ __FF FFFF EEEX (LSB)
1118
     *   MMMM MMMM MMMM MMMM ____ _FFF FFFF EEEX (LSB)
1119 1119
     *
1120 1120
     * X   : tag for GC marking (It seems as Fixnum)
1121 1121
     * EEE : 3 bits Env flags
1122
     * FF..: 6 bits Frame flags
1122
     * FF..: 7 bits Frame flags
1123 1123
     * MM..: 15 bits frame magic (to check frame corruption)
1124 1124
     */
1125 1125

  
......
1143 1143
    VM_FRAME_FLAG_CFRAME    = 0x0080,
1144 1144
    VM_FRAME_FLAG_LAMBDA    = 0x0100,
1145 1145
    VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM = 0x0200,
1146
    VM_FRAME_FLAG_TAILCALLED = 0x0400,
1146 1147

  
1147 1148
    /* env flag */
1148 1149
    VM_ENV_FLAG_LOCAL       = 0x0002,
vm_insnhelper.c
1719 1719
	*sp++ = src_argv[i];
1720 1720
    }
1721 1721

  
1722
    vm_push_frame(ec, iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL | finish_flag,
1722
    vm_push_frame(ec, iseq, VM_FRAME_MAGIC_METHOD | VM_FRAME_FLAG_TAILCALLED | VM_ENV_FLAG_LOCAL | finish_flag,
1723 1723
		  calling->recv, calling->block_handler, (VALUE)me,
1724 1724
		  iseq->body->iseq_encoded + opt_pc, sp,
1725 1725
		  iseq->body->local_table_size - iseq->body->param.size,
......
3889 3889
	VM_ASSERT(vm_event_flags & events);
3890 3890

  
3891 3891
	/* increment PC because source line is calculated with PC-1 */
3892
        if ((event = (events & (RUBY_EVENT_CLASS | RUBY_EVENT_CALL | RUBY_EVENT_B_CALL))) != 0) {
3893
	    VM_ASSERT(event == RUBY_EVENT_CLASS ||
3894
		      event == RUBY_EVENT_CALL  ||
3895
		      event == RUBY_EVENT_B_CALL);
3892
        if ((event = (events & (RUBY_EVENT_CLASS | RUBY_EVENT_B_CALL))) != 0) {
3893
	    VM_ASSERT(event == RUBY_EVENT_CLASS || event == RUBY_EVENT_B_CALL);
3896 3894
	    reg_cfp->pc++;
3897 3895
	    vm_dtrace(event, ec);
3898 3896
	    EXEC_EVENT_HOOK(ec, event, GET_SELF(), 0, 0, 0, Qundef);
3899 3897
	    reg_cfp->pc--;
3900 3898
	}
3899
        if (events & RUBY_EVENT_CALL) {
3900
            VALUE is_tailcall = (VM_ENV_FLAGS(reg_cfp->ep, VM_FRAME_FLAG_TAILCALLED)) ? Qtrue : Qfalse;
3901
            reg_cfp->pc++;
3902
            vm_dtrace(RUBY_EVENT_CALL, ec);
3903
            EXEC_EVENT_HOOK(ec, RUBY_EVENT_CALL, GET_SELF(), 0, 0, 0, is_tailcall);
3904
            reg_cfp->pc--;
3905
        }
3901 3906
	if (events & RUBY_EVENT_LINE) {
3902 3907
	    reg_cfp->pc++;
3903 3908
	    vm_dtrace(RUBY_EVENT_LINE, ec);
vm_trace.c
935 935
    return trace_arg->data;
936 936
}
937 937

  
938
VALUE
939
rb_tracearg_is_tailcall(rb_trace_arg_t *trace_arg)
940
{
941
    if (trace_arg->event == RUBY_EVENT_CALL) {
942
        return (trace_arg->data == Qtrue) ? Qtrue : Qfalse;
943
    }
944
    return Qfalse;
945
}
946

  
938 947
/*
939 948
 * Type of event
940 949
 *
......
1070 1079
    return rb_tracearg_raised_exception(get_trace_arg());
1071 1080
}
1072 1081

  
1082
/*
1083
 * Whether the interpreter performed a tail call. Can only
1084
 * be true for +:call+ events.
1085
 */
1086
static VALUE
1087
tracepoint_attr_is_tailcall(VALUE tpval)
1088
{
1089
    return rb_tracearg_is_tailcall(get_trace_arg());
1090
}
1091

  
1073 1092
static void
1074 1093
tp_call_trace(VALUE tpval, rb_trace_arg_t *trace_arg)
1075 1094
{
......
1562 1581
    rb_define_method(rb_cTracePoint, "self", tracepoint_attr_self, 0);
1563 1582
    rb_define_method(rb_cTracePoint, "return_value", tracepoint_attr_return_value, 0);
1564 1583
    rb_define_method(rb_cTracePoint, "raised_exception", tracepoint_attr_raised_exception, 0);
1584
    rb_define_method(rb_cTracePoint, "tailcall?", tracepoint_attr_is_tailcall, 0);
1565 1585

  
1566 1586
    rb_define_singleton_method(rb_cTracePoint, "stat", tracepoint_stat_s, 0);
1567 1587
}
1568
-