Skip to content

Commit 9bbe4b6

Browse files
authored
ZJIT: Carve out IseqPayload into a separate module (ruby#15098)
1 parent ae60b0b commit 9bbe4b6

File tree

7 files changed

+96
-90
lines changed

7 files changed

+96
-90
lines changed

zjit/src/codegen.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use crate::invariants::{
1313
track_bop_assumption, track_cme_assumption, track_no_ep_escape_assumption, track_no_trace_point_assumption,
1414
track_single_ractor_assumption, track_stable_constant_names_assumption, track_no_singleton_class_assumption
1515
};
16-
use crate::gc::{append_gc_offsets, get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqCodePtrs, IseqPayload, IseqStatus};
16+
use crate::gc::append_gc_offsets;
17+
use crate::payload::{get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqCodePtrs, IseqPayload, IseqStatus};
1718
use crate::state::ZJITState;
1819
use crate::stats::{send_fallback_counter, exit_counter_for_compile_error, incr_counter, incr_counter_by, send_fallback_counter_for_method_type, send_without_block_fallback_counter_for_method_type, send_without_block_fallback_counter_for_optimized_method_type, send_fallback_counter_ptr_for_opcode, CompileError};
1920
use crate::stats::{counter_ptr, with_time_stat, Counter, Counter::{compile_time_ns, exit_compile_error}};

zjit/src/gc.rs

Lines changed: 2 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,11 @@
11
//! This module is responsible for marking/moving objects on GC.
22
33
use std::{ffi::c_void, ops::Range};
4-
use crate::codegen::IseqCallRef;
5-
use crate::stats::CompileError;
6-
use crate::{cruby::*, profile::IseqProfile, state::ZJITState, stats::with_time_stat, virtualmem::CodePtr};
4+
use crate::{cruby::*, state::ZJITState, stats::with_time_stat, virtualmem::CodePtr};
5+
use crate::payload::{IseqPayload, get_or_create_iseq_payload, payload_ptr_as_mut};
76
use crate::stats::Counter::gc_time_ns;
87
use crate::state::gc_mark_raw_samples;
98

10-
/// This is all the data ZJIT stores on an ISEQ. We mark objects in this struct on GC.
11-
#[derive(Debug)]
12-
pub struct IseqPayload {
13-
/// Compilation status of the ISEQ. It has the JIT code address of the first block if Compiled.
14-
pub status: IseqStatus,
15-
16-
/// Type information of YARV instruction operands
17-
pub profile: IseqProfile,
18-
19-
/// GC offsets of the JIT code. These are the addresses of objects that need to be marked.
20-
pub gc_offsets: Vec<CodePtr>,
21-
22-
/// JIT-to-JIT calls in the ISEQ. The IseqPayload's ISEQ is the caller of it.
23-
pub iseq_calls: Vec<IseqCallRef>,
24-
}
25-
26-
impl IseqPayload {
27-
fn new(iseq_size: u32) -> Self {
28-
Self {
29-
status: IseqStatus::NotCompiled,
30-
profile: IseqProfile::new(iseq_size),
31-
gc_offsets: vec![],
32-
iseq_calls: vec![],
33-
}
34-
}
35-
}
36-
37-
/// Set of CodePtrs for an ISEQ
38-
#[derive(Clone, Debug, PartialEq)]
39-
pub struct IseqCodePtrs {
40-
/// Entry for the interpreter
41-
pub start_ptr: CodePtr,
42-
/// Entries for JIT-to-JIT calls
43-
pub jit_entry_ptrs: Vec<CodePtr>,
44-
}
45-
46-
#[derive(Debug, PartialEq)]
47-
pub enum IseqStatus {
48-
Compiled(IseqCodePtrs),
49-
CantCompile(CompileError),
50-
NotCompiled,
51-
}
52-
53-
/// Get a pointer to the payload object associated with an ISEQ. Create one if none exists.
54-
pub fn get_or_create_iseq_payload_ptr(iseq: IseqPtr) -> *mut IseqPayload {
55-
type VoidPtr = *mut c_void;
56-
57-
unsafe {
58-
let payload = rb_iseq_get_zjit_payload(iseq);
59-
if payload.is_null() {
60-
// Allocate a new payload with Box and transfer ownership to the GC.
61-
// We drop the payload with Box::from_raw when the GC frees the ISEQ and calls us.
62-
// NOTE(alan): Sometimes we read from an ISEQ without ever writing to it.
63-
// We allocate in those cases anyways.
64-
let iseq_size = get_iseq_encoded_size(iseq);
65-
let new_payload = IseqPayload::new(iseq_size);
66-
let new_payload = Box::into_raw(Box::new(new_payload));
67-
rb_iseq_set_zjit_payload(iseq, new_payload as VoidPtr);
68-
69-
new_payload
70-
} else {
71-
payload as *mut IseqPayload
72-
}
73-
}
74-
}
75-
76-
/// Get the payload object associated with an ISEQ. Create one if none exists.
77-
pub fn get_or_create_iseq_payload(iseq: IseqPtr) -> &'static mut IseqPayload {
78-
let payload_non_null = get_or_create_iseq_payload_ptr(iseq);
79-
payload_ptr_as_mut(payload_non_null)
80-
}
81-
82-
/// Convert an IseqPayload pointer to a mutable reference. Only one reference
83-
/// should be kept at a time.
84-
fn payload_ptr_as_mut(payload_ptr: *mut IseqPayload) -> &'static mut IseqPayload {
85-
// SAFETY: we should have the VM lock and all other Ruby threads should be asleep. So we have
86-
// exclusive mutable access.
87-
// Hmm, nothing seems to stop calling this on the same
88-
// iseq twice, though, which violates aliasing rules.
89-
unsafe { payload_ptr.as_mut() }.unwrap()
90-
}
91-
929
/// GC callback for marking GC objects in the per-ISEQ payload.
9310
#[unsafe(no_mangle)]
9411
pub extern "C" fn rb_zjit_iseq_mark(payload: *mut c_void) {

zjit/src/hir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#![allow(clippy::if_same_then_else)]
77
#![allow(clippy::match_like_matches_macro)]
88
use crate::{
9-
cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, gc::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState
9+
cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState
1010
};
1111
use std::{
1212
cell::RefCell, collections::{HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter

zjit/src/invariants.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
use std::{collections::{HashMap, HashSet}, mem};
44

5-
use crate::{backend::lir::{asm_comment, Assembler}, cruby::{iseq_name, rb_callable_method_entry_t, rb_gc_location, ruby_basic_operators, src_loc, with_vm_lock, IseqPtr, RedefinitionFlag, ID, VALUE}, gc::IseqPayload, hir::Invariant, options::debug, state::{zjit_enabled_p, ZJITState}, virtualmem::CodePtr};
5+
use crate::{backend::lir::{asm_comment, Assembler}, cruby::{iseq_name, rb_callable_method_entry_t, rb_gc_location, ruby_basic_operators, src_loc, with_vm_lock, IseqPtr, RedefinitionFlag, ID, VALUE}, hir::Invariant, options::debug, state::{zjit_enabled_p, ZJITState}, virtualmem::CodePtr};
6+
use crate::payload::IseqPayload;
67
use crate::stats::with_time_stat;
78
use crate::stats::Counter::invalidation_time_ns;
89
use crate::gc::remove_gc_offsets;
@@ -367,7 +368,7 @@ pub fn track_no_trace_point_assumption(patch_point_ptr: CodePtr, side_exit_ptr:
367368

368369
#[unsafe(no_mangle)]
369370
pub extern "C" fn rb_zjit_tracing_invalidate_all() {
370-
use crate::gc::{get_or_create_iseq_payload, IseqStatus};
371+
use crate::payload::{get_or_create_iseq_payload, IseqStatus};
371372
use crate::cruby::{for_each_iseq, rb_iseq_reset_jit_func};
372373

373374
if !zjit_enabled_p() {

zjit/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ mod profile;
2828
mod invariants;
2929
mod bitset;
3030
mod gc;
31+
mod payload;

zjit/src/payload.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use std::ffi::c_void;
2+
use crate::codegen::IseqCallRef;
3+
use crate::stats::CompileError;
4+
use crate::{cruby::*, profile::IseqProfile, virtualmem::CodePtr};
5+
6+
/// This is all the data ZJIT stores on an ISEQ. We mark objects in this struct on GC.
7+
#[derive(Debug)]
8+
pub struct IseqPayload {
9+
/// Compilation status of the ISEQ. It has the JIT code address of the first block if Compiled.
10+
pub status: IseqStatus,
11+
12+
/// Type information of YARV instruction operands
13+
pub profile: IseqProfile,
14+
15+
/// GC offsets of the JIT code. These are the addresses of objects that need to be marked.
16+
pub gc_offsets: Vec<CodePtr>,
17+
18+
/// JIT-to-JIT calls in the ISEQ. The IseqPayload's ISEQ is the caller of it.
19+
pub iseq_calls: Vec<IseqCallRef>,
20+
}
21+
22+
impl IseqPayload {
23+
fn new(iseq_size: u32) -> Self {
24+
Self {
25+
status: IseqStatus::NotCompiled,
26+
profile: IseqProfile::new(iseq_size),
27+
gc_offsets: vec![],
28+
iseq_calls: vec![],
29+
}
30+
}
31+
}
32+
33+
/// Set of CodePtrs for an ISEQ
34+
#[derive(Clone, Debug, PartialEq)]
35+
pub struct IseqCodePtrs {
36+
/// Entry for the interpreter
37+
pub start_ptr: CodePtr,
38+
/// Entries for JIT-to-JIT calls
39+
pub jit_entry_ptrs: Vec<CodePtr>,
40+
}
41+
42+
#[derive(Debug, PartialEq)]
43+
pub enum IseqStatus {
44+
Compiled(IseqCodePtrs),
45+
CantCompile(CompileError),
46+
NotCompiled,
47+
}
48+
49+
/// Get a pointer to the payload object associated with an ISEQ. Create one if none exists.
50+
pub fn get_or_create_iseq_payload_ptr(iseq: IseqPtr) -> *mut IseqPayload {
51+
type VoidPtr = *mut c_void;
52+
53+
unsafe {
54+
let payload = rb_iseq_get_zjit_payload(iseq);
55+
if payload.is_null() {
56+
// Allocate a new payload with Box and transfer ownership to the GC.
57+
// We drop the payload with Box::from_raw when the GC frees the ISEQ and calls us.
58+
// NOTE(alan): Sometimes we read from an ISEQ without ever writing to it.
59+
// We allocate in those cases anyways.
60+
let iseq_size = get_iseq_encoded_size(iseq);
61+
let new_payload = IseqPayload::new(iseq_size);
62+
let new_payload = Box::into_raw(Box::new(new_payload));
63+
rb_iseq_set_zjit_payload(iseq, new_payload as VoidPtr);
64+
65+
new_payload
66+
} else {
67+
payload as *mut IseqPayload
68+
}
69+
}
70+
}
71+
72+
/// Get the payload object associated with an ISEQ. Create one if none exists.
73+
pub fn get_or_create_iseq_payload(iseq: IseqPtr) -> &'static mut IseqPayload {
74+
let payload_non_null = get_or_create_iseq_payload_ptr(iseq);
75+
payload_ptr_as_mut(payload_non_null)
76+
}
77+
78+
/// Convert an IseqPayload pointer to a mutable reference. Only one reference
79+
/// should be kept at a time.
80+
pub fn payload_ptr_as_mut(payload_ptr: *mut IseqPayload) -> &'static mut IseqPayload {
81+
// SAFETY: we should have the VM lock and all other Ruby threads should be asleep. So we have
82+
// exclusive mutable access.
83+
// Hmm, nothing seems to stop calling this on the same
84+
// iseq twice, though, which violates aliasing rules.
85+
unsafe { payload_ptr.as_mut() }.unwrap()
86+
}

zjit/src/profile.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// We use the YARV bytecode constants which have a CRuby-style name
44
#![allow(non_upper_case_globals)]
55

6-
use crate::{cruby::*, gc::get_or_create_iseq_payload, options::{get_option, NumProfiles}};
6+
use crate::{cruby::*, payload::get_or_create_iseq_payload, options::{get_option, NumProfiles}};
77
use crate::distribution::{Distribution, DistributionSummary};
88
use crate::stats::Counter::profile_time_ns;
99
use crate::stats::with_time_stat;

0 commit comments

Comments
 (0)