Skip to content

Commit adfa784

Browse files
committed
ZJIT: Forget about dead ISEQs in Invariants
Without this, we crash during reference update.
1 parent 00e6c10 commit adfa784

File tree

4 files changed

+26
-0
lines changed

4 files changed

+26
-0
lines changed

iseq.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ rb_iseq_free(const rb_iseq_t *iseq)
174174
RUBY_ASSERT(rb_yjit_live_iseq_count > 0);
175175
rb_yjit_live_iseq_count--;
176176
}
177+
#endif
178+
#if USE_ZJIT
179+
rb_zjit_iseq_free(iseq);
177180
#endif
178181
ruby_xfree((void *)body->iseq_encoded);
179182
ruby_xfree((void *)body->insns_info.body);

zjit.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ 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);
25+
void rb_zjit_iseq_free(const rb_iseq_t *iseq);
2526
void rb_zjit_before_ractor_spawn(void);
2627
void rb_zjit_tracing_invalidate_all(void);
2728
#else

zjit/src/gc.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ pub extern "C" fn rb_zjit_iseq_update_references(payload: *mut c_void) {
128128
with_time_stat(gc_time_ns, || iseq_update_references(payload));
129129
}
130130

131+
/// GC callback for finalizing an ISEQ
132+
#[unsafe(no_mangle)]
133+
pub extern "C" fn rb_zjit_iseq_free(iseq: IseqPtr) {
134+
if !ZJITState::has_instance() {
135+
return;
136+
}
137+
// TODO(Shopify/ruby#682): Free `IseqPayload`
138+
let invariants = ZJITState::get_invariants();
139+
invariants.forget_iseq(iseq);
140+
}
141+
131142
/// GC callback for updating object references after all object moves
132143
#[unsafe(no_mangle)]
133144
pub extern "C" fn rb_zjit_root_update_references() {

zjit/src/invariants.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ impl Invariants {
6868
self.update_no_ep_escape_iseq_patch_points();
6969
}
7070

71+
/// Forget an ISEQ when freeing it. We need to because a) if the address is reused, we'd be
72+
/// tracking the wrong object b) dead VALUEs in the table can means we risk passing invalid
73+
/// VALUEs to `rb_gc_location()`.
74+
pub fn forget_iseq(&mut self, iseq: IseqPtr) {
75+
// Why not patch the patch points? If the ISEQ is dead then the GC also proved that all
76+
// generated code referencing the ISEQ are unreachable. We mark the ISEQs baked into
77+
// generated code.
78+
self.ep_escape_iseqs.remove(&iseq);
79+
self.no_ep_escape_iseq_patch_points.remove(&iseq);
80+
}
81+
7182
/// Update ISEQ references in Invariants::ep_escape_iseqs
7283
fn update_ep_escape_iseqs(&mut self) {
7384
let updated = std::mem::take(&mut self.ep_escape_iseqs)

0 commit comments

Comments
 (0)