Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -971,14 +971,16 @@ rb_gc_obj_slot_size(VALUE obj)
}

static inline void
gc_validate_pc(void)
gc_validate_pc(VALUE obj)
{
#if RUBY_DEBUG
rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = ec->cfp;
if (cfp && VM_FRAME_RUBYFRAME_P(cfp) && cfp->pc) {
RUBY_ASSERT(cfp->pc >= ISEQ_BODY(cfp->iseq)->iseq_encoded);
RUBY_ASSERT(cfp->pc <= ISEQ_BODY(cfp->iseq)->iseq_encoded + ISEQ_BODY(cfp->iseq)->iseq_size);
if (!RB_TYPE_P(obj, T_IMEMO) && cfp && VM_FRAME_RUBYFRAME_P(cfp) && cfp->pc) {
const VALUE *iseq_encoded = ISEQ_BODY(cfp->iseq)->iseq_encoded;
const VALUE *iseq_encoded_end = iseq_encoded + ISEQ_BODY(cfp->iseq)->iseq_size;
RUBY_ASSERT(cfp->pc >= iseq_encoded, "PC not set when allocating, breaking tracing");
RUBY_ASSERT(cfp->pc <= iseq_encoded_end, "PC not set when allocating, breaking tracing");
}
#endif
}
Expand All @@ -988,7 +990,7 @@ newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, bool wb_protected, size_t s
{
VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, wb_protected, size);

gc_validate_pc();
gc_validate_pc(obj);

if (UNLIKELY(rb_gc_event_hook_required_p(RUBY_INTERNAL_EVENT_NEWOBJ))) {
int lev = RB_GC_VM_LOCK_NO_BARRIER();
Expand Down
9 changes: 8 additions & 1 deletion method.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,17 @@ typedef struct rb_callable_method_entry_struct { /* same fields with rb_method_e
#define METHOD_ENTRY_COMPLEMENTED(me) ((me)->flags & IMEMO_FL_USER3)
#define METHOD_ENTRY_COMPLEMENTED_SET(me) ((me)->flags |= IMEMO_FL_USER3)
#define METHOD_ENTRY_CACHED(me) ((me)->flags & IMEMO_FL_USER4)
#define METHOD_ENTRY_CACHED_SET(me) ((me)->flags |= IMEMO_FL_USER4)
#define METHOD_ENTRY_INVALIDATED(me) ((me)->flags & IMEMO_FL_USER5)
#define METHOD_ENTRY_INVALIDATED_SET(me) ((me)->flags |= IMEMO_FL_USER5)

static inline void
METHOD_ENTRY_CACHED_SET(rb_callable_method_entry_t *me)
{
if (!METHOD_ENTRY_CACHED(me)) {
me->flags |= IMEMO_FL_USER4;
}
}

static inline void
METHOD_ENTRY_VISI_SET(rb_method_entry_t *me, rb_method_visibility_t visi)
{
Expand Down
32 changes: 30 additions & 2 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2813,6 +2813,28 @@ def test
}, insns: [:opt_send_without_block]
end

def test_allocating_in_hir_c_method_is
assert_compiles ":k", %q{
# Put opt_new in a frame JIT code sets up that doesn't set cfp->pc
def a(f) = test(f)
def test(f) = (f.new if f)
# A parallel couple methods that will set PC at the same stack height
def second = third
def third = nil

a(nil)
a(nil)

class Foo
def self.new = :k
end

second

a(Foo)
}, call_threshold: 2, insns: [:opt_new]
end

private

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

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

unless insns.empty?
iseq = result.fetch(:insns)
assert_equal("YARVInstructionSequence/SimpleDataFormat", iseq.first, "failed to get iseq disassembly")
assert_equal(
"YARVInstructionSequence/SimpleDataFormat",
iseq.first,
"Failed to get ISEQ disassembly. " \
"Make sure to put code directly under the '#{disasm_method}' method."
)
iseq_insns = iseq.last

expected_insns = Set.new(insns)
Expand Down