Project

General

Profile

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

rhenium (Kazuki Yamaguchi), 03/09/2016 09:17 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;
3617

  
3618
    /* rescue block can't tail call because of errinfo */
3619
    if (iseq->body->type == ISEQ_TYPE_RESCUE || iseq->body->type == ISEQ_TYPE_ENSURE)
3620
        return COMPILE_OK;
3621

  
3622
    cmap = xcalloc(1, iseq->body->iseq_size);
3623

  
3624
    if (table) {
3625
	for (i = table->size; i-- > 0; ) {
3626
	    const struct iseq_catch_table_entry *entry = &table->entries[i];
3627
	    memset(cmap + entry->start, 1, entry->end - entry->start);
3628
	}
3629
    }
3630

  
3631
    /*
3632
     *  send ...
3633
     *  leave
3634
     * =>
3635
     *  send ..., ... | VM_CALL_TAILCALL, ...
3636
     *  leave # unreachable
3637
     */
3638
    for (i = 0; i < iseq->body->iseq_size; ) {
3639
	int insn = (int)encoded[i];
3640
	int len = insn_len(insn);
3641
	if (i + len >= iseq->body->iseq_size) break;
3642

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

  
3677
    xfree(cmap);
3678
    return COMPILE_OK;
3679
}
3680

  
3651 3681
/**
3652 3682
  compile each node
3653 3683

  
......
4318 4348
	    ADD_INSNL(ret, line, jump, label_miss);
4319 4349
	    ADD_LABEL(ret, label_hit);
4320 4350
	    COMPILE(ret, "resbody body", resq->nd_body);
4321
	    if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
4322
		ADD_INSN(ret, line, nop);
4323
	    }
4324 4351
	    ADD_INSN(ret, line, leave);
4325 4352
	    ADD_LABEL(ret, label_miss);
4326 4353
	    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
      def errinfo
300
        $!
301
      end
302
      options = {
303
        tailcall_optimization: true,
304
        trace_instruction: false,
305
      }
306
      RubyVM::InstructionSequence.compile(<<~EOF, __FILE__, __FILE__, __LINE__, options).eval
307
        def test_rescue
308
          return do_raise if true # suppress unreachable warning
309
          1 + 2
310
        rescue
311
          errinfo
312
        end
313
      EOF
314
      err = test_rescue
315
      assert(err)
316
      assert_equal(RuntimeError, err.class)
317
    end;
318
  end
319

  
294 320
  class Bug10557
295 321
    def [](_)
296 322
      block_given?
297
-