Skip to content

Commit 4f03095

Browse files
authored
ZJIT: Invalidate local variables on EP escape (ruby#14448)
1 parent ce20d68 commit 4f03095

File tree

15 files changed

+236
-107
lines changed

15 files changed

+236
-107
lines changed

common.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1133,7 +1133,7 @@ $(srcs_vpath)insns_info.inc: $(tooldir)/ruby_vm/views/insns_info.inc.erb $(inc_c
11331133
$(tooldir)/ruby_vm/views/_insn_type_chars.erb $(tooldir)/ruby_vm/views/_insn_name_info.erb \
11341134
$(tooldir)/ruby_vm/views/_insn_len_info.erb $(tooldir)/ruby_vm/views/_insn_operand_info.erb \
11351135
$(tooldir)/ruby_vm/views/_attributes.erb $(tooldir)/ruby_vm/views/_comptime_insn_stack_increase.erb \
1136-
$(tooldir)/ruby_vm/views/_zjit_helpers.erb
1136+
$(tooldir)/ruby_vm/views/_zjit_helpers.erb $(tooldir)/ruby_vm/views/_insn_leaf_info.erb
11371137
$(srcs_vpath)vmtc.inc: $(tooldir)/ruby_vm/views/vmtc.inc.erb $(inc_common_headers)
11381138
$(srcs_vpath)vm.inc: $(tooldir)/ruby_vm/views/vm.inc.erb $(inc_common_headers) \
11391139
$(tooldir)/ruby_vm/views/_insn_entry.erb $(tooldir)/ruby_vm/views/_trace_instruction.erb \

template/Makefile.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,7 @@ $(INSNS): $(srcdir)/insns.def vm_opts.h \
679679
$(tooldir)/ruby_vm/views/_comptime_insn_stack_increase.erb \
680680
$(tooldir)/ruby_vm/views/_copyright.erb \
681681
$(tooldir)/ruby_vm/views/_insn_entry.erb \
682+
$(tooldir)/ruby_vm/views/_insn_leaf_info.erb \
682683
$(tooldir)/ruby_vm/views/_insn_len_info.erb \
683684
$(tooldir)/ruby_vm/views/_insn_name_info.erb \
684685
$(tooldir)/ruby_vm/views/_insn_operand_info.erb \

test/.excludes-zjit/TestRubyOptimization.rb

Lines changed: 0 additions & 1 deletion
This file was deleted.

test/ruby/test_zjit.rb

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,50 @@ def test
196196
}, insns: [:setglobal]
197197
end
198198

199+
def test_getlocal_after_eval
200+
assert_compiles '2', %q{
201+
def test
202+
a = 1
203+
eval('a = 2')
204+
a
205+
end
206+
test
207+
}
208+
end
209+
210+
def test_getlocal_after_instance_eval
211+
assert_compiles '2', %q{
212+
def test
213+
a = 1
214+
instance_eval('a = 2')
215+
a
216+
end
217+
test
218+
}
219+
end
220+
221+
def test_getlocal_after_module_eval
222+
assert_compiles '2', %q{
223+
def test
224+
a = 1
225+
Kernel.module_eval('a = 2')
226+
a
227+
end
228+
test
229+
}
230+
end
231+
232+
def test_getlocal_after_class_eval
233+
assert_compiles '2', %q{
234+
def test
235+
a = 1
236+
Kernel.class_eval('a = 2')
237+
a
238+
end
239+
test
240+
}
241+
end
242+
199243
def test_setlocal
200244
assert_compiles '3', %q{
201245
def test(n)
@@ -1453,8 +1497,7 @@ def entry = jit_frame1 # 3
14531497
end
14541498

14551499
def test_bop_invalidation
1456-
omit 'Invalidation on BOP redefinition is not implemented yet'
1457-
assert_compiles '', %q{
1500+
assert_compiles '100', %q{
14581501
def test
14591502
eval(<<~RUBY)
14601503
class Integer
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
MAYBE_UNUSED(static bool insn_leaf(int insn, const VALUE *opes));
2+
static bool
3+
insn_leaf(int insn, const VALUE *opes)
4+
{
5+
switch (insn) {
6+
% RubyVM::Instructions.each do |insn|
7+
% next if insn.is_a?(RubyVM::TraceInstructions) || insn.is_a?(RubyVM::ZJITInstructions)
8+
case <%= insn.bin %>:
9+
return attr_leaf_<%= insn.name %>(<%=
10+
insn.operands.map.with_index do |ope, i|
11+
"(#{ope[:type]})opes[#{i}]"
12+
end.join(', ')
13+
%>);
14+
% end
15+
default:
16+
return false;
17+
}
18+
}

tool/ruby_vm/views/insns_info.inc.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@
2121
<%= render 'sp_inc_helpers' %>
2222
<%= render 'zjit_helpers' %>
2323
<%= render 'attributes' %>
24+
<%= render 'insn_leaf_info' %>
2425
<%= render 'comptime_insn_stack_increase' %>
2526
#endif

vm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,7 @@ vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *co
10611061
// Invalidate JIT code that assumes cfp->ep == vm_base_ptr(cfp).
10621062
if (env->iseq) {
10631063
rb_yjit_invalidate_ep_is_bp(env->iseq);
1064-
rb_zjit_invalidate_ep_is_bp(env->iseq);
1064+
rb_zjit_invalidate_no_ep_escape(env->iseq);
10651065
}
10661066

10671067
return (VALUE)env;

vm_eval.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1985,6 +1985,10 @@ eval_string_with_cref(VALUE self, VALUE src, rb_cref_t *cref, VALUE file, int li
19851985
block.as.captured.code.iseq = cfp->iseq;
19861986
block.type = block_type_iseq;
19871987

1988+
// EP is not escaped to the heap here, but captured and reused by another frame.
1989+
// ZJIT's locals are incompatible with it unlike YJIT's, so invalidate the ISEQ for ZJIT.
1990+
rb_zjit_invalidate_no_ep_escape(cfp->iseq);
1991+
19881992
iseq = eval_make_iseq(src, file, line, &block);
19891993
if (!iseq) {
19901994
rb_exc_raise(ec->errinfo);

zjit.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,12 @@ rb_zjit_defined_ivar(VALUE obj, ID id, VALUE pushval)
333333
return result ? pushval : Qnil;
334334
}
335335

336+
bool
337+
rb_zjit_insn_leaf(int insn, const VALUE *opes)
338+
{
339+
return insn_leaf(insn, opes);
340+
}
341+
336342
// Primitives used by zjit.rb. Don't put other functions below, which wouldn't use them.
337343
VALUE rb_zjit_assert_compiles(rb_execution_context_t *ec, VALUE self);
338344
VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self, VALUE target_key);

zjit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ void rb_zjit_profile_insn(uint32_t insn, rb_execution_context_t *ec);
1818
void rb_zjit_profile_enable(const rb_iseq_t *iseq);
1919
void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
2020
void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme);
21-
void rb_zjit_invalidate_ep_is_bp(const rb_iseq_t *iseq);
21+
void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq);
2222
void rb_zjit_constant_state_changed(ID id);
2323
void rb_zjit_iseq_mark(void *payload);
2424
void rb_zjit_iseq_update_references(void *payload);
@@ -31,7 +31,7 @@ static inline void rb_zjit_profile_insn(uint32_t insn, rb_execution_context_t *e
3131
static inline void rb_zjit_profile_enable(const rb_iseq_t *iseq) {}
3232
static inline void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {}
3333
static inline void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme) {}
34-
static inline void rb_zjit_invalidate_ep_is_bp(const rb_iseq_t *iseq) {}
34+
static inline void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq) {}
3535
static inline void rb_zjit_constant_state_changed(ID id) {}
3636
static inline void rb_zjit_before_ractor_spawn(void) {}
3737
static inline void rb_zjit_tracing_invalidate_all(void) {}

0 commit comments

Comments
 (0)