Skip to content

Commit 9a5e48f

Browse files
committed
gc_validate_pc(): Exclude imemos, add a test and explain the asserts
The validation is relevant only for traceable userland ruby objects ruby code could interact with. ZJIT's use of rb_vm_method_cfunc_is() allocates a CC imemo and was failing this validation when it was actually fine. Relax the check.
1 parent cdb9c25 commit 9a5e48f

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

gc.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -971,14 +971,16 @@ rb_gc_obj_slot_size(VALUE obj)
971971
}
972972

973973
static inline void
974-
gc_validate_pc(void)
974+
gc_validate_pc(VALUE obj)
975975
{
976976
#if RUBY_DEBUG
977977
rb_execution_context_t *ec = GET_EC();
978978
const rb_control_frame_t *cfp = ec->cfp;
979-
if (cfp && VM_FRAME_RUBYFRAME_P(cfp) && cfp->pc) {
980-
RUBY_ASSERT(cfp->pc >= ISEQ_BODY(cfp->iseq)->iseq_encoded);
981-
RUBY_ASSERT(cfp->pc <= ISEQ_BODY(cfp->iseq)->iseq_encoded + ISEQ_BODY(cfp->iseq)->iseq_size);
979+
if (!RB_TYPE_P(obj, T_IMEMO) && cfp && VM_FRAME_RUBYFRAME_P(cfp) && cfp->pc) {
980+
const VALUE *iseq_encoded = ISEQ_BODY(cfp->iseq)->iseq_encoded;
981+
const VALUE *iseq_encoded_end = iseq_encoded + ISEQ_BODY(cfp->iseq)->iseq_size;
982+
RUBY_ASSERT(cfp->pc >= iseq_encoded, "PC not set when allocating, breaking tracing");
983+
RUBY_ASSERT(cfp->pc <= iseq_encoded_end, "PC not set when allocating, breaking tracing");
982984
}
983985
#endif
984986
}
@@ -988,7 +990,7 @@ newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, bool wb_protected, size_t s
988990
{
989991
VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, wb_protected, size);
990992

991-
gc_validate_pc();
993+
gc_validate_pc(obj);
992994

993995
if (UNLIKELY(rb_gc_event_hook_required_p(RUBY_INTERNAL_EVENT_NEWOBJ))) {
994996
int lev = RB_GC_VM_LOCK_NO_BARRIER();

test/ruby/test_zjit.rb

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2813,6 +2813,28 @@ def test
28132813
}, insns: [:opt_send_without_block]
28142814
end
28152815

2816+
def test_allocating_in_hir_c_method_is
2817+
assert_compiles ":k", %q{
2818+
# Put opt_new in a frame JIT code sets up that doesn't set cfp->pc
2819+
def a(f) = test(f)
2820+
def test(f) = (f.new if f)
2821+
# A parallel couple methods that will set PC at the same stack height
2822+
def second = third
2823+
def third = nil
2824+
2825+
a(nil)
2826+
a(nil)
2827+
2828+
class Foo
2829+
def self.new = :k
2830+
end
2831+
2832+
second
2833+
2834+
a(Foo)
2835+
}, call_threshold: 2, insns: [:opt_new]
2836+
end
2837+
28162838
private
28172839

28182840
# Assert that every method call in `test_script` can be compiled by ZJIT
@@ -2826,13 +2848,14 @@ def assert_compiles(expected, test_script, insns: [], **opts)
28262848
# allows ZJIT to skip compiling methods.
28272849
def assert_runs(expected, test_script, insns: [], assert_compiles: false, **opts)
28282850
pipe_fd = 3
2851+
disasm_method = :test
28292852

28302853
script = <<~RUBY
28312854
ret_val = (_test_proc = -> { #{('RubyVM::ZJIT.assert_compiles; ' if assert_compiles)}#{test_script.lstrip} }).call
28322855
result = {
28332856
ret_val:,
28342857
#{ unless insns.empty?
2835-
'insns: RubyVM::InstructionSequence.of(method(:test)).to_a'
2858+
"insns: RubyVM::InstructionSequence.of(method(#{disasm_method.inspect})).to_a"
28362859
end}
28372860
}
28382861
IO.open(#{pipe_fd}).write(Marshal.dump(result))
@@ -2846,7 +2869,12 @@ def assert_runs(expected, test_script, insns: [], assert_compiles: false, **opts
28462869

28472870
unless insns.empty?
28482871
iseq = result.fetch(:insns)
2849-
assert_equal("YARVInstructionSequence/SimpleDataFormat", iseq.first, "failed to get iseq disassembly")
2872+
assert_equal(
2873+
"YARVInstructionSequence/SimpleDataFormat",
2874+
iseq.first,
2875+
"Failed to get ISEQ disassembly. " \
2876+
"Make sure to put code directly under the '#{disasm_method}' method."
2877+
)
28502878
iseq_insns = iseq.last
28512879

28522880
expected_insns = Set.new(insns)

0 commit comments

Comments
 (0)