Skip to content

Commit bb7fa59

Browse files
committed
merge revision(s) 54542,54548: [Backport ruby#12082]
* compile.c (iseq_optimize): disable tail call optimization in rescued, rescue, and ensure blocks. [ruby-core:73871] [Bug ruby#12082] * compile.c (new_label_body): initialize bit fields, since compile_data_alloc does not clear the memory. [Bug ruby#12082] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@54698 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
1 parent 3a2effb commit bb7fa59

File tree

4 files changed

+98
-3
lines changed

4 files changed

+98
-3
lines changed

ChangeLog

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
Fri Apr 22 18:36:15 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
2+
3+
* compile.c (new_label_body): initialize bit fields, since
4+
compile_data_alloc does not clear the memory. [Bug #12082]
5+
6+
Fri Apr 22 18:36:15 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
7+
8+
* compile.c (iseq_optimize): disable tail call optimization in
9+
rescued, rescue, and ensure blocks.
10+
[ruby-core:73871] [Bug #12082]
11+
112
Fri Apr 22 18:34:31 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
213

314
* doc/regexp.rdoc (comments): [DOC] terminators cannot appear in

compile.c

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,21 @@ typedef struct iseq_link_anchor {
4141
LINK_ELEMENT *last;
4242
} LINK_ANCHOR;
4343

44+
typedef enum {
45+
LABEL_RESCUE_NONE,
46+
LABEL_RESCUE_BEG,
47+
LABEL_RESCUE_END,
48+
LABEL_RESCUE_TYPE_MAX
49+
} LABEL_RESCUE_TYPE;
50+
4451
typedef struct iseq_label_data {
4552
LINK_ELEMENT link;
4653
int label_no;
4754
int position;
4855
int sc_state;
4956
int set;
5057
int sp;
58+
unsigned int rescued: 2;
5159
} LABEL;
5260

5361
typedef struct iseq_insn_data {
@@ -497,6 +505,9 @@ rb_iseq_compile_node(VALUE self, NODE *node)
497505
LABEL *start = iseq->compile_data->start_label = NEW_LABEL(0);
498506
LABEL *end = iseq->compile_data->end_label = NEW_LABEL(0);
499507

508+
start->rescued = LABEL_RESCUE_BEG;
509+
end->rescued = LABEL_RESCUE_END;
510+
500511
ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_B_CALL);
501512
ADD_LABEL(ret, start);
502513
COMPILE(ret, "block body", node->nd_body);
@@ -2038,20 +2049,37 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
20382049
return COMPILE_OK;
20392050
}
20402051

2052+
static inline int
2053+
tailcallable_p(rb_iseq_t *iseq)
2054+
{
2055+
switch (iseq->type) {
2056+
case ISEQ_TYPE_RESCUE:
2057+
case ISEQ_TYPE_ENSURE:
2058+
/* rescue block can't tail call because of errinfo */
2059+
return FALSE;
2060+
default:
2061+
return TRUE;
2062+
}
2063+
}
2064+
20412065
static int
20422066
iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
20432067
{
20442068
LINK_ELEMENT *list;
20452069
const int do_peepholeopt = iseq->compile_data->option->peephole_optimization;
2046-
const int do_tailcallopt = iseq->compile_data->option->tailcall_optimization;
2070+
const int do_tailcallopt = tailcallable_p(iseq) &&
2071+
iseq->compile_data->option->tailcall_optimization;
20472072
const int do_si = iseq->compile_data->option->specialized_instruction;
20482073
const int do_ou = iseq->compile_data->option->operands_unification;
2074+
int rescue_level = 0;
2075+
int tailcallopt = do_tailcallopt;
2076+
20492077
list = FIRST_ELEMENT(anchor);
20502078

20512079
while (list) {
20522080
if (list->type == ISEQ_ELEMENT_INSN) {
20532081
if (do_peepholeopt) {
2054-
iseq_peephole_optimize(iseq, list, do_tailcallopt);
2082+
iseq_peephole_optimize(iseq, list, tailcallopt);
20552083
}
20562084
if (do_si) {
20572085
iseq_specialized_instruction(iseq, (INSN *)list);
@@ -2060,6 +2088,17 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
20602088
insn_operands_unification((INSN *)list);
20612089
}
20622090
}
2091+
if (list->type == ISEQ_ELEMENT_LABEL) {
2092+
switch (((LABEL *)list)->rescued) {
2093+
case LABEL_RESCUE_BEG:
2094+
rescue_level++;
2095+
tailcallopt = FALSE;
2096+
break;
2097+
case LABEL_RESCUE_END:
2098+
if (!--rescue_level) tailcallopt = do_tailcallopt;
2099+
break;
2100+
}
2101+
}
20632102
list = list->next;
20642103
}
20652104
return COMPILE_OK;
@@ -3108,6 +3147,8 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
31083147
("defined guard in "),
31093148
iseq->location.label),
31103149
ISEQ_TYPE_DEFINED_GUARD, 0);
3150+
lstart->rescued = LABEL_RESCUE_BEG;
3151+
lend->rescued = LABEL_RESCUE_END;
31113152
APPEND_LABEL(ret, lcur, lstart);
31123153
ADD_LABEL(ret, lend);
31133154
ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
@@ -3878,6 +3919,8 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
38783919
rb_str_concat(rb_str_new2("rescue in "), iseq->location.label),
38793920
ISEQ_TYPE_RESCUE, line);
38803921

3922+
lstart->rescued = LABEL_RESCUE_BEG;
3923+
lend->rescued = LABEL_RESCUE_END;
38813924
ADD_LABEL(ret, lstart);
38823925
COMPILE(ret, "rescue head", node->nd_head);
38833926
ADD_LABEL(ret, lend);

test/ruby/test_optimization.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,23 @@ def test_eq
210210
assert_equal true, MyObj.new == nil
211211
end
212212

213+
def self.tailcall(klass, src, file = nil, path = nil, line = nil)
214+
unless file
215+
loc, = caller_locations(1, 1)
216+
file = loc.path
217+
line ||= loc.lineno
218+
end
219+
RubyVM::InstructionSequence.new("proc {|_|_.class_eval {#{src}}}",
220+
file, (path || file), line,
221+
tailcall_optimization: true,
222+
trace_instruction: false)
223+
.eval[klass]
224+
end
225+
226+
def tailcall(*args)
227+
self.class.tailcall(singleton_class, *args)
228+
end
229+
213230
def test_tailcall
214231
bug4082 = '[ruby-core:33289]'
215232

@@ -255,6 +272,30 @@ def delay
255272
assert_equal(123, delay { 123 }.call, bug6901)
256273
end
257274

275+
def do_raise
276+
raise "should be rescued"
277+
end
278+
279+
def errinfo
280+
$!
281+
end
282+
283+
def test_tailcall_inhibited_by_rescue
284+
bug12082 = '[ruby-core:73871] [Bug #12082]'
285+
286+
tailcall(<<-'end;')
287+
def to_be_rescued
288+
return do_raise
289+
1 + 2
290+
rescue
291+
errinfo
292+
end
293+
end;
294+
result = to_be_rescued
295+
assert_instance_of(RuntimeError, result, bug12082)
296+
assert_equal("should be rescued", result.message, bug12082)
297+
end
298+
258299
class Bug10557
259300
def [](_)
260301
block_given?

version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#define RUBY_VERSION "2.2.5"
22
#define RUBY_RELEASE_DATE "2016-04-22"
3-
#define RUBY_PATCHLEVEL 312
3+
#define RUBY_PATCHLEVEL 313
44

55
#define RUBY_RELEASE_YEAR 2016
66
#define RUBY_RELEASE_MONTH 4

0 commit comments

Comments
 (0)