Skip to content

Commit 7a18738

Browse files
committed
ZJIT: Remove RefCell from IseqCall
No point taking the panic risks with RefCell when most fields in it are already in a Cell. Put `iseq` in a Cell and we no longer need the wrapping. Saves memory, too.
1 parent baec95c commit 7a18738

File tree

2 files changed

+28
-30
lines changed

2 files changed

+28
-30
lines changed

zjit/src/codegen.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ struct JITState {
3737
jit_entries: Vec<Rc<RefCell<JITEntry>>>,
3838

3939
/// ISEQ calls that need to be compiled later
40-
iseq_calls: Vec<Rc<RefCell<IseqCall>>>,
40+
iseq_calls: Vec<IseqCallRef>,
4141

4242
/// The number of bytes allocated for basic block arguments spilled onto the C stack
4343
c_stack_slots: usize,
@@ -132,17 +132,17 @@ fn gen_iseq_entry_point(cb: &mut CodeBlock, iseq: IseqPtr, jit_exception: bool)
132132
}
133133

134134
/// Stub a branch for a JIT-to-JIT call
135-
fn gen_iseq_call(cb: &mut CodeBlock, caller_iseq: IseqPtr, iseq_call: &Rc<RefCell<IseqCall>>) -> Result<(), CompileError> {
135+
fn gen_iseq_call(cb: &mut CodeBlock, caller_iseq: IseqPtr, iseq_call: &IseqCallRef) -> Result<(), CompileError> {
136136
// Compile a function stub
137137
let stub_ptr = gen_function_stub(cb, iseq_call.clone()).inspect_err(|err| {
138138
debug!("{err:?}: gen_function_stub failed: {} -> {}",
139-
iseq_get_location(caller_iseq, 0), iseq_get_location(iseq_call.borrow().iseq, 0));
139+
iseq_get_location(caller_iseq, 0), iseq_get_location(iseq_call.iseq.get(), 0));
140140
})?;
141141

142142
// Update the JIT-to-JIT call to call the stub
143143
let stub_addr = stub_ptr.raw_ptr(cb);
144-
let iseq = iseq_call.borrow().iseq;
145-
iseq_call.borrow_mut().regenerate(cb, |asm| {
144+
let iseq = iseq_call.iseq.get();
145+
iseq_call.regenerate(cb, |asm| {
146146
asm_comment!(asm, "call function stub: {}", iseq_get_location(iseq, 0));
147147
asm.ccall(stub_addr, vec![]);
148148
});
@@ -235,7 +235,7 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>,
235235
}
236236

237237
// Prepare for GC
238-
payload.iseq_calls.extend(iseq_calls.clone());
238+
payload.iseq_calls.extend(iseq_calls);
239239
append_gc_offsets(iseq, &gc_offsets);
240240
Ok(iseq_code_ptrs)
241241
}
@@ -1797,8 +1797,8 @@ c_callable! {
17971797
with_vm_lock(src_loc!(), || {
17981798
// gen_push_frame() doesn't set PC, so we need to set them before exit.
17991799
// function_stub_hit_body() may allocate and call gc_validate_pc(), so we always set PC.
1800-
let iseq_call = unsafe { Rc::from_raw(iseq_call_ptr as *const RefCell<IseqCall>) };
1801-
let iseq = iseq_call.borrow().iseq;
1800+
let iseq_call = unsafe { Rc::from_raw(iseq_call_ptr as *const IseqCall) };
1801+
let iseq = iseq_call.iseq.get();
18021802
let pc = unsafe { rb_iseq_pc_at_idx(iseq, 0) }; // TODO: handle opt_pc once supported
18031803
unsafe { rb_set_cfp_pc(cfp, pc) };
18041804

@@ -1834,7 +1834,7 @@ c_callable! {
18341834
};
18351835
if let Some(compile_error) = compile_error {
18361836
// We'll use this Rc again, so increment the ref count decremented by from_raw.
1837-
unsafe { Rc::increment_strong_count(iseq_call_ptr as *const RefCell<IseqCall>); }
1837+
unsafe { Rc::increment_strong_count(iseq_call_ptr as *const IseqCall); }
18381838

18391839
prepare_for_exit(iseq, cfp, sp, compile_error);
18401840
return ZJITState::get_exit_trampoline_with_counter().raw_ptr(cb);
@@ -1853,10 +1853,10 @@ c_callable! {
18531853
}
18541854

18551855
/// Compile an ISEQ for a function stub
1856-
fn function_stub_hit_body(cb: &mut CodeBlock, iseq_call: &Rc<RefCell<IseqCall>>) -> Result<CodePtr, CompileError> {
1856+
fn function_stub_hit_body(cb: &mut CodeBlock, iseq_call: &IseqCallRef) -> Result<CodePtr, CompileError> {
18571857
// Compile the stubbed ISEQ
1858-
let IseqCodePtrs { jit_entry_ptrs, .. } = gen_iseq(cb, iseq_call.borrow().iseq, None).inspect_err(|err| {
1859-
debug!("{err:?}: gen_iseq failed: {}", iseq_get_location(iseq_call.borrow().iseq, 0));
1858+
let IseqCodePtrs { jit_entry_ptrs, .. } = gen_iseq(cb, iseq_call.iseq.get(), None).inspect_err(|err| {
1859+
debug!("{err:?}: gen_iseq failed: {}", iseq_get_location(iseq_call.iseq.get(), 0));
18601860
})?;
18611861

18621862
// We currently don't support JIT-to-JIT calls for ISEQs with optional arguments.
@@ -1866,8 +1866,8 @@ fn function_stub_hit_body(cb: &mut CodeBlock, iseq_call: &Rc<RefCell<IseqCall>>)
18661866

18671867
// Update the stub to call the code pointer
18681868
let code_addr = jit_entry_ptr.raw_ptr(cb);
1869-
let iseq = iseq_call.borrow().iseq;
1870-
iseq_call.borrow_mut().regenerate(cb, |asm| {
1869+
let iseq = iseq_call.iseq.get();
1870+
iseq_call.regenerate(cb, |asm| {
18711871
asm_comment!(asm, "call compiled function: {}", iseq_get_location(iseq, 0));
18721872
asm.ccall(code_addr, vec![]);
18731873
});
@@ -1876,9 +1876,9 @@ fn function_stub_hit_body(cb: &mut CodeBlock, iseq_call: &Rc<RefCell<IseqCall>>)
18761876
}
18771877

18781878
/// Compile a stub for an ISEQ called by SendWithoutBlockDirect
1879-
fn gen_function_stub(cb: &mut CodeBlock, iseq_call: Rc<RefCell<IseqCall>>) -> Result<CodePtr, CompileError> {
1879+
fn gen_function_stub(cb: &mut CodeBlock, iseq_call: IseqCallRef) -> Result<CodePtr, CompileError> {
18801880
let mut asm = Assembler::new();
1881-
asm_comment!(asm, "Stub: {}", iseq_get_location(iseq_call.borrow().iseq, 0));
1881+
asm_comment!(asm, "Stub: {}", iseq_get_location(iseq_call.iseq.get(), 0));
18821882

18831883
// Call function_stub_hit using the shared trampoline. See `gen_function_stub_hit_trampoline`.
18841884
// Use load_into instead of mov, which is split on arm64, to avoid clobbering ALLOC_REGS.
@@ -2043,7 +2043,7 @@ fn aligned_stack_bytes(num_slots: usize) -> usize {
20432043

20442044
impl Assembler {
20452045
/// Make a C call while marking the start and end positions for IseqCall
2046-
fn ccall_with_iseq_call(&mut self, fptr: *const u8, opnds: Vec<Opnd>, iseq_call: &Rc<RefCell<IseqCall>>) -> Opnd {
2046+
fn ccall_with_iseq_call(&mut self, fptr: *const u8, opnds: Vec<Opnd>, iseq_call: &IseqCallRef) -> Opnd {
20472047
// We need to create our own branch rc objects so that we can move the closure below
20482048
let start_iseq_call = iseq_call.clone();
20492049
let end_iseq_call = iseq_call.clone();
@@ -2052,10 +2052,10 @@ impl Assembler {
20522052
fptr,
20532053
opnds,
20542054
move |code_ptr, _| {
2055-
start_iseq_call.borrow_mut().start_addr.set(Some(code_ptr));
2055+
start_iseq_call.start_addr.set(Some(code_ptr));
20562056
},
20572057
move |code_ptr, _| {
2058-
end_iseq_call.borrow_mut().end_addr.set(Some(code_ptr));
2058+
end_iseq_call.end_addr.set(Some(code_ptr));
20592059
},
20602060
)
20612061
}
@@ -2084,7 +2084,7 @@ impl JITEntry {
20842084
#[derive(Debug)]
20852085
pub struct IseqCall {
20862086
/// Callee ISEQ that start_addr jumps to
2087-
pub iseq: IseqPtr,
2087+
pub iseq: Cell<IseqPtr>,
20882088

20892089
/// Position where the call instruction starts
20902090
start_addr: Cell<Option<CodePtr>>,
@@ -2093,17 +2093,17 @@ pub struct IseqCall {
20932093
end_addr: Cell<Option<CodePtr>>,
20942094
}
20952095

2096-
type IseqCallRef = Rc<RefCell<IseqCall>>;
2096+
pub type IseqCallRef = Rc<IseqCall>;
20972097

20982098
impl IseqCall {
20992099
/// Allocate a new IseqCall
2100-
fn new(iseq: IseqPtr) -> Rc<RefCell<Self>> {
2100+
fn new(iseq: IseqPtr) -> IseqCallRef {
21012101
let iseq_call = IseqCall {
2102-
iseq,
2102+
iseq: Cell::new(iseq),
21032103
start_addr: Cell::new(None),
21042104
end_addr: Cell::new(None),
21052105
};
2106-
Rc::new(RefCell::new(iseq_call))
2106+
Rc::new(iseq_call)
21072107
}
21082108

21092109
/// Regenerate a IseqCall with a given callback

zjit/src/gc.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
//! This module is responsible for marking/moving objects on GC.
22
3-
use std::cell::RefCell;
4-
use std::rc::Rc;
53
use std::{ffi::c_void, ops::Range};
6-
use crate::codegen::IseqCall;
4+
use crate::codegen::IseqCallRef;
75
use crate::stats::CompileError;
86
use crate::{cruby::*, profile::IseqProfile, state::ZJITState, stats::with_time_stat, virtualmem::CodePtr};
97
use crate::stats::Counter::gc_time_ns;
@@ -21,7 +19,7 @@ pub struct IseqPayload {
2119
pub gc_offsets: Vec<CodePtr>,
2220

2321
/// JIT-to-JIT calls in the ISEQ. The IseqPayload's ISEQ is the caller of it.
24-
pub iseq_calls: Vec<Rc<RefCell<IseqCall>>>,
22+
pub iseq_calls: Vec<IseqCallRef>,
2523
}
2624

2725
impl IseqPayload {
@@ -191,10 +189,10 @@ fn iseq_update_references(payload: &mut IseqPayload) {
191189

192190
// Move ISEQ references in IseqCall
193191
for iseq_call in payload.iseq_calls.iter_mut() {
194-
let old_iseq = iseq_call.borrow().iseq;
192+
let old_iseq = iseq_call.iseq.get();
195193
let new_iseq = unsafe { rb_gc_location(VALUE(old_iseq as usize)) }.0 as IseqPtr;
196194
if old_iseq != new_iseq {
197-
iseq_call.borrow_mut().iseq = new_iseq;
195+
iseq_call.iseq.set(new_iseq);
198196
}
199197
}
200198

0 commit comments

Comments
 (0)