Project

General

Profile

Bug #12082 ยป 0001-compile.c-don-t-do-tail-call-optimization-if-covered.patch

rhenium (Kazuki Yamaguchi), 02/18/2016 04:34 AM

View differences:

compile.c
418 418
static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
419 419
static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
420 420
static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
421
static int iseq_optimize_tailcall(rb_iseq_t *iseq);
421 422

  
422 423
static int iseq_set_local_table(rb_iseq_t *iseq, const ID *tbl);
423 424
static int iseq_set_exception_local_table(rb_iseq_t *iseq);
......
1129 1130
    debugs("[compile step 4.3 (set_optargs_table)] \n");
1130 1131
    if (!iseq_set_optargs_table(iseq)) return COMPILE_NG;
1131 1132

  
1133
    /* dirty hack: it have to look through catch table */
1134
    if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization &&
1135
	    ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization)
1136
	iseq_optimize_tailcall(iseq);
1137

  
1132 1138
    debugs("[compile step 5 (iseq_translate_threaded_code)] \n");
1133 1139
    if (!rb_iseq_translate_threaded_code(iseq)) return COMPILE_NG;
1134 1140

  
......
1996 2002
}
1997 2003

  
1998 2004
static int
1999
iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
2005
iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list)
2000 2006
{
2001 2007
    INSN *iobj = (INSN *)list;
2002 2008
  again:
......
2212 2218
	}
2213 2219
    }
2214 2220

  
2215
    if (do_tailcallopt &&
2216
	(iobj->insn_id == BIN(send) ||
2217
	 iobj->insn_id == BIN(opt_aref_with) ||
2218
	 iobj->insn_id == BIN(opt_aset_with) ||
2219
	 iobj->insn_id == BIN(invokesuper))) {
2220
	/*
2221
	 *  send ...
2222
	 *  leave
2223
	 * =>
2224
	 *  send ..., ... | VM_CALL_TAILCALL, ...
2225
	 *  leave # unreachable
2226
	 */
2227
	INSN *piobj = NULL;
2228
	if (iobj->link.next) {
2229
	    LINK_ELEMENT *next = iobj->link.next;
2230
	    do {
2231
		if (next->type != ISEQ_ELEMENT_INSN) {
2232
		    next = next->next;
2233
		    continue;
2234
		}
2235
		switch (INSN_OF(next)) {
2236
		  case BIN(nop):
2237
		  /*case BIN(trace):*/
2238
		    next = next->next;
2239
		    break;
2240
		  case BIN(leave):
2241
		    piobj = iobj;
2242
		  default:
2243
		    next = NULL;
2244
		    break;
2245
		}
2246
	    } while (next);
2247
	}
2248

  
2249
	if (piobj) {
2250
	    struct rb_call_info *ci = (struct rb_call_info *)piobj->operands[0];
2251
	    if (piobj->insn_id == BIN(send) || piobj->insn_id == BIN(invokesuper)) {
2252
		if (piobj->operands[2] == 0) { /* no blockiseq */
2253
		    ci->flag |= VM_CALL_TAILCALL;
2254
		}
2255
	    }
2256
	    else {
2257
		ci->flag |= VM_CALL_TAILCALL;
2258
	    }
2259
	}
2260
    }
2261 2221
    return COMPILE_OK;
2262 2222
}
2263 2223

  
......
2339 2299
{
2340 2300
    LINK_ELEMENT *list;
2341 2301
    const int do_peepholeopt = ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization;
2342
    const int do_tailcallopt = ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization;
2343 2302
    const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction;
2344 2303
    const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification;
2345 2304
    list = FIRST_ELEMENT(anchor);
......
2347 2306
    while (list) {
2348 2307
	if (list->type == ISEQ_ELEMENT_INSN) {
2349 2308
	    if (do_peepholeopt) {
2350
		iseq_peephole_optimize(iseq, list, do_tailcallopt);
2309
		iseq_peephole_optimize(iseq, list);
2351 2310
	    }
2352 2311
	    if (do_si) {
2353 2312
		iseq_specialized_instruction(iseq, (INSN *)list);
......
3648 3607
    return Qnil;
3649 3608
}
3650 3609

  
3610
static int
3611
iseq_optimize_tailcall(rb_iseq_t *iseq)
3612
{
3613
    unsigned int i;
3614
    VALUE *encoded = (VALUE *)iseq->body->iseq_encoded;
3615
    const struct iseq_catch_table *table = iseq->body->catch_table;
3616
    char *cmap = xcalloc(1, iseq->body->iseq_size);
3617

  
3618
    if (table) {
3619
	for (i = table->size; i-- > 0; ) {
3620
	    const struct iseq_catch_table_entry *entry = &table->entries[i];
3621
	    memset(cmap + entry->start, 1, entry->end - entry->start);
3622
	}
3623
    }
3624

  
3625
    /*
3626
     *  send ...
3627
     *  leave
3628
     * =>
3629
     *  send ..., ... | VM_CALL_TAILCALL, ...
3630
     *  leave # unreachable
3631
     */
3632
    for (i = 0; i < iseq->body->iseq_size; ) {
3633
	int insn = (int)encoded[i];
3634
	int len = insn_len(insn);
3635
	if (i + len >= iseq->body->iseq_size) break;
3636

  
3637
	if (!cmap[i] && BIN(leave) == (int)encoded[i + len]) {
3638
	    struct rb_call_info *ci = (struct rb_call_info *)encoded[i + 1];
3639
	    switch (insn) {
3640
	      case BIN(invokesuper):
3641
	      case BIN(send):
3642
		if (encoded[i + 3]) break; /* with block */
3643
	      case BIN(opt_aref_with):
3644
	      case BIN(opt_aset_with):
3645
	      case BIN(opt_send_without_block):
3646
	      case BIN(opt_length):
3647
	      case BIN(opt_size):
3648
	      case BIN(opt_empty_p):
3649
	      case BIN(opt_succ):
3650
	      case BIN(opt_not):
3651
	      case BIN(opt_plus):
3652
	      case BIN(opt_minus):
3653
	      case BIN(opt_mult):
3654
	      case BIN(opt_div):
3655
	      case BIN(opt_mod):
3656
	      case BIN(opt_eq):
3657
	      case BIN(opt_neq):
3658
	      case BIN(opt_lt):
3659
	      case BIN(opt_le):
3660
	      case BIN(opt_gt):
3661
	      case BIN(opt_ge):
3662
	      case BIN(opt_ltlt):
3663
	      case BIN(opt_aref):
3664
	      case BIN(opt_aset):
3665
		ci->flag |= VM_CALL_TAILCALL;
3666
	    }
3667
	}
3668
	i += len;
3669
    }
3670

  
3671
    xfree(cmap);
3672
    return COMPILE_OK;
3673
}
3674

  
3651 3675
/**
3652 3676
  compile each node
3653 3677

  
......
4318 4342
	    ADD_INSNL(ret, line, jump, label_miss);
4319 4343
	    ADD_LABEL(ret, label_hit);
4320 4344
	    COMPILE(ret, "resbody body", resq->nd_body);
4321
	    if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
4322
		ADD_INSN(ret, line, nop);
4323
	    }
4324 4345
	    ADD_INSN(ret, line, leave);
4325 4346
	    ADD_LABEL(ret, label_miss);
4326 4347
	    resq = resq->nd_head;
test/ruby/test_optimization.rb
291 291
    end;
292 292
  end
293 293

  
294
  def test_tailcall_rescue
295
    assert_separately([], <<~'end;')
296
      def do_raise
297
        raise "must be rescued"
298
      end
299
      options = {
300
        tailcall_optimization: true,
301
        trace_instruction: false,
302
      }
303
      RubyVM::InstructionSequence.compile(<<~EOF, __FILE__, __FILE__, __LINE__, options).eval
304
        def test_rescue
305
          return do_raise if true # suppress unreachable warning
306
          1 + 2
307
        rescue
308
          :ok
309
        end
310
      EOF
311
      assert_equal(:ok, test_rescue)
312
    end;
313
  end
314

  
294 315
  class Bug10557
295 316
    def [](_)
296 317
      block_given?
297
-