@@ -158,7 +158,9 @@ fn iseq_version_update_references(mut version: IseqVersionRef) {
158158 }
159159 }
160160
161- // Move objects baked in JIT code
161+ // Move objects baked in JIT code.
162+ // The code region is already writable because rb_zjit_mark_all_writable() was called
163+ // before the GC update_references phase. We write directly to avoid per-page mprotect calls.
162164 let cb = ZJITState :: get_code_block ( ) ;
163165 for & offset in unsafe { version. as_ref ( ) } . gc_offsets . iter ( ) {
164166 let value_ptr: * const u8 = offset. raw_ptr ( cb) ;
@@ -170,13 +172,10 @@ fn iseq_version_update_references(mut version: IseqVersionRef) {
170172
171173 // Only write when the VALUE moves, to be copy-on-write friendly.
172174 if new_addr != object {
173- for ( byte_idx, & byte) in new_addr. as_u64 ( ) . to_le_bytes ( ) . iter ( ) . enumerate ( ) {
174- let byte_code_ptr = offset. add_bytes ( byte_idx) ;
175- cb. write_mem ( byte_code_ptr, byte) . expect ( "patching existing code should be within bounds" ) ;
176- }
175+ let value_ptr = value_ptr as * mut VALUE ;
176+ unsafe { value_ptr. write_unaligned ( new_addr) } ;
177177 }
178178 }
179- cb. mark_all_executable ( ) ;
180179}
181180
182181/// Append a set of gc_offsets to the iseq's payload
@@ -211,6 +210,25 @@ fn ranges_overlap<T>(left: &Range<T>, right: &Range<T>) -> bool where T: Partial
211210 left. start < right. end && right. start < left. end
212211}
213212
213+ /// GC callback for making all JIT code writable before updating references in bulk.
214+ /// This avoids toggling W^X permissions per-page during GC compaction.
215+ #[ unsafe( no_mangle) ]
216+ pub extern "C" fn rb_zjit_mark_all_writable ( ) {
217+ if !ZJITState :: has_instance ( ) {
218+ return ;
219+ }
220+ ZJITState :: get_code_block ( ) . mark_all_writable ( ) ;
221+ }
222+
223+ /// GC callback for making all JIT code executable after updating references in bulk.
224+ #[ unsafe( no_mangle) ]
225+ pub extern "C" fn rb_zjit_mark_all_executable ( ) {
226+ if !ZJITState :: has_instance ( ) {
227+ return ;
228+ }
229+ ZJITState :: get_code_block ( ) . mark_all_executable ( ) ;
230+ }
231+
214232/// Callback for marking GC objects inside [crate::invariants::Invariants].
215233#[ unsafe( no_mangle) ]
216234pub extern "C" fn rb_zjit_root_mark ( ) {
0 commit comments