Ruby 4.1.0dev (2026-09-27 revision f6ff9e7d02e46360f8930b280a3dd921cccbda29)
zjit.c (f6ff9e7d02e46360f8930b280a3dd921cccbda29)
1#include "internal.h"
2#include "internal/sanitizers.h"
3#include "internal/string.h"
4#include "internal/hash.h"
5#include "internal/variable.h"
6#include "internal/compile.h"
7#include "internal/class.h"
8#include "internal/fixnum.h"
9#include "internal/numeric.h"
10#include "internal/gc.h"
11#include "internal/vm.h"
12#include "yjit.h"
13#include "vm_core.h"
14#include "vm_callinfo.h"
15#include "builtin.h"
16#include "insns.inc"
17#include "insns_info.inc"
18#include "zjit.h"
19#include "vm_insnhelper.h"
20#include "probes.h"
21#include "probes_helper.h"
22#include "constant.h"
23#include "iseq.h"
24#include "ruby/debug.h"
25#include "internal/cont.h"
26#include "internal/jit.h"
27#include "ractor_core.h"
28#include "shape.h"
29
30#ifndef _WIN32
31#include <sys/mman.h>
32#endif
33
34// This build config impacts the pointer tagging scheme and we only want to
35// support one scheme for simplicity.
36STATIC_ASSERT(pointer_tagging_scheme, USE_FLONUM);
37
38enum zjit_struct_offsets {
39 ISEQ_BODY_OFFSET_PARAM = offsetof(struct rb_iseq_constant_body, param),
40 ISEQ_BODY_OFFSET_OUTER_VARIABLES = offsetof(struct rb_iseq_constant_body, outer_variables),
41 RUBY_OFFSET_THREAD_RACTOR = offsetof(rb_thread_t, ractor),
42};
43
44// Struct offsets that cannot be constants in the checked-in bindgen output
45// (zjit/src/cruby_bindings.inc.rs) because they vary with the build target
46// and configuration. For example, offsetof(rb_ractor_t, newobj_cache) depends
47// on the sizes of pthread types embedded in rb_ractor_t, which differ across
48// architectures and OSes, as well as on VM_CHECK_MODE and RACTOR_CHECK_MODE.
49// This table is filled out at C compile time and read by Rust at JIT compile
50// time. Offsets that are identical on all supported builds should be added to
51// enum zjit_struct_offsets above instead.
53 int32_t ractor_newobj_cache;
54 int32_t ractor_objspace;
55};
57 .ractor_newobj_cache = offsetof(rb_ractor_t, newobj_cache),
58 .ractor_objspace = offsetof(rb_ractor_t, objspace),
59};
60
61// Special JITFrame used by all C method calls. We don't control the native
62// stack layout for C frames, so cfp->jit_return points at this static frame
63// via the ZJIT_JIT_RETURN_C_FRAME sentinel instead of a per-call allocation.
64const zjit_jit_frame_t rb_zjit_c_frame = (zjit_jit_frame_t) {
65 .pc = 0,
66 .iseq = 0,
67 .materialize_block_code = false,
68};
69
70#if !defined(_WIN32) && defined(MAP_ANONYMOUS)
71uint8_t *rb_jit_align_ptr(uint8_t *ptr, uint32_t multiple); // defined in jit.c
72
73// Reserve address space that lives entirely below INT32_MAX for JITFrame.
74//
75// When a JITFrame pointer fits in 32 bits, x86_64 can encode the store
76// as `mov qword ptr [mem], imm32` (8 bytes) instead of `movabs` + a store,
77// and arm64 materializes it in two instructions instead of four.
78//
79// Like rb_jit_reserve_addr_space in jit.c, this only reserves address space (PROT_NONE).
80// VirtualMem is in charge of mapping physical memory into the reserved space page by page.
81void *
82rb_zjit_reserve_low_addr_space(size_t size)
83{
84 void *mem_block = MAP_FAILED;
85
86 // Linux (x86_64): Use MAP_32BIT to map within the first 2GiB of address space.
87 // This works only for x86_64, and the kernel restricts it to [1GiB, 2GiB).
88 #ifdef MAP_32BIT
89 mem_block = mmap(NULL, size, PROT_NONE,
90 MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
91 #endif
92
93 // Linux (all arch): Probe a free hole below 2GiB if MAP_32BIT is not possible.
94 // MAP_FIXED_NOREPLACE fails rather than clobbering an existing mapping.
95 #if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
96 if (mem_block == MAP_FAILED) {
97 // Distance between probes. 64MiB sweeps the usable 2GiB in at most 32 mmap calls.
98 const uintptr_t probe_stride = 64 * 1024 * 1024;
99 const uint32_t page_size = (uint32_t)sysconf(_SC_PAGESIZE);
100 const uintptr_t limit = (uintptr_t)INT32_MAX - size;
101 for (uintptr_t addr = probe_stride; addr < limit; addr += probe_stride) {
102 // mmap only honors a hint that is page-aligned.
103 void *req = rb_jit_align_ptr((uint8_t *)addr, page_size);
104 mem_block = mmap(req, size, PROT_NONE,
105 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0);
106 if (mem_block != MAP_FAILED) break;
107 }
108 }
109 #endif
110
111 if (mem_block == MAP_FAILED) return NULL;
112
113 // Both MAP_32BIT and MAP_FIXED_NOREPLACE are advisory in some platforms, e.g.
114 // sandboxes or older kernels. Fallback to normal allocation if it doesn't work.
115 if ((uintptr_t)mem_block + size > (uintptr_t)INT32_MAX) {
116 munmap(mem_block, size);
117 return NULL;
118 }
119 ruby_annotate_mmap(mem_block, size, "Ruby:rb_zjit_reserve_low_addr_space");
120 return mem_block;
121}
122
123#else
124
125// Windows not supported for now
126void *rb_zjit_reserve_low_addr_space(size_t size) { return NULL; }
127
128#endif
129
130void rb_zjit_profile_disable(const rb_iseq_t *iseq);
131int rb_zjit_insn_to_bare_insn(int insn);
132
133void
134rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception)
135{
136 RB_VM_LOCKING() {
137 rb_vm_barrier();
138
139 // Compile a block version starting at the current instruction
140 uint8_t *rb_zjit_iseq_gen_entry_point(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception); // defined in Rust
141 uintptr_t code_ptr = (uintptr_t)rb_zjit_iseq_gen_entry_point(iseq, ec, jit_exception);
142
143 if (jit_exception) {
144 ISEQ_BODY(iseq)->jit_exception = (rb_jit_func_t)code_ptr;
145 }
146 else {
147 ISEQ_BODY(iseq)->jit_entry = (rb_jit_func_t)code_ptr;
148 }
149 }
150}
151
152// This is used by a function stub to install compiled code as the ISEQ's entry point.
153void
154rb_zjit_iseq_set_jit_entry(const rb_iseq_t *iseq, void *code_ptr)
155{
156 ISEQ_BODY(iseq)->jit_entry = (rb_jit_func_t)code_ptr;
157}
158
159extern VALUE *rb_vm_base_ptr(struct rb_control_frame_struct *cfp);
160
161// Convert a given ISEQ's instructions to zjit_* instructions
162void
163rb_zjit_profile_enable(const rb_iseq_t *iseq)
164{
165 // This table encodes an opcode into the instruction's address
166 const void *const *insn_table = rb_vm_get_insns_address_table();
167
168 unsigned int insn_idx = 0;
169 while (insn_idx < ISEQ_BODY(iseq)->iseq_size) {
170 int insn = rb_vm_insn_addr2opcode((void *)ISEQ_BODY(iseq)->iseq_encoded[insn_idx]);
171 int zjit_insn = vm_bare_insn_to_zjit_insn(insn);
172 if (insn != zjit_insn) {
173 ISEQ_BODY(iseq)->iseq_encoded[insn_idx] = (VALUE)insn_table[zjit_insn];
174 }
175 insn_idx += insn_len(insn);
176 }
177}
178
179// Return false if a function stub has not collected enough profiles yet, enabling
180// profiling instructions as needed. Return true once enough profiles are collected.
181bool
182rb_zjit_iseq_has_profiled_enough(const rb_iseq_t *iseq)
183{
184 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
185
186 if (body->jit_entry_calls < rb_zjit_profile_threshold) {
187 // Skip the unprofiled warmup. The compiled caller already establishes
188 // that the callee is hot, so go straight to the profiling window.
189 body->jit_entry_calls = rb_zjit_profile_threshold;
190 rb_zjit_profile_enable(iseq);
191 }
192 else {
193 body->jit_entry_calls++;
194 }
195
196 return body->jit_entry_calls >= rb_zjit_call_threshold;
197}
198
199// Convert a given ISEQ's ZJIT instructions to bare instructions
200void
201rb_zjit_profile_disable(const rb_iseq_t *iseq)
202{
203 // This table encodes an opcode into the instruction's address
204 const void *const *insn_table = rb_vm_get_insns_address_table();
205
206 unsigned int insn_idx = 0;
207 while (insn_idx < ISEQ_BODY(iseq)->iseq_size) {
208 int insn = rb_vm_insn_addr2opcode((void *)ISEQ_BODY(iseq)->iseq_encoded[insn_idx]);
209 int bare_insn = vm_zjit_insn_to_bare_insn(insn);
210 if (insn != bare_insn) {
211 ISEQ_BODY(iseq)->iseq_encoded[insn_idx] = (VALUE)insn_table[bare_insn];
212 }
213 insn_idx += insn_len(insn);
214 }
215}
216
217// Map `zjit_* instructions back to their bare form. This is an identity function for all others.
218int
219rb_zjit_insn_to_bare_insn(int insn)
220{
221 return vm_zjit_insn_to_bare_insn(insn);
222}
223
224// Update a YARV instruction to a given opcode (to disable ZJIT profiling).
225void
226rb_zjit_iseq_insn_set(const rb_iseq_t *iseq, unsigned int insn_idx, enum ruby_vminsn_type bare_insn)
227{
228#if RUBY_DEBUG
229 int insn = rb_vm_insn_addr2opcode((void *)ISEQ_BODY(iseq)->iseq_encoded[insn_idx]);
230 RUBY_ASSERT(vm_zjit_insn_to_bare_insn(insn) == (int)bare_insn);
231#endif
232 const void *const *insn_table = rb_vm_get_insns_address_table();
233 ISEQ_BODY(iseq)->iseq_encoded[insn_idx] = (VALUE)insn_table[bare_insn];
234}
235
236void
237rb_zjit_print_exception(void)
238{
239 VALUE exception = rb_errinfo();
240 rb_set_errinfo(Qnil);
241 assert(RTEST(exception));
242 rb_warn("Ruby error: %"PRIsVALUE"", rb_funcall(exception, rb_intern("full_message"), 0));
243}
244
245bool
246rb_zjit_singleton_class_p(VALUE klass)
247{
248 return RCLASS_SINGLETON_P(klass);
249}
250
251/* Sets all of the required shape flags for the object including the layout type,
252 * the frozen status, and the slot size. Mimics `rb_newobj`.
253 */
254VALUE
255rb_zjit_new_obj_shape(VALUE flags, size_t alloc_size)
256{
257 shape_id_t shape_id;
258 switch (flags & T_MASK) {
259 case T_OBJECT:
260 shape_id = ROOT_SHAPE_ID;
261 break;
262 case T_STRUCT:
263 shape_id = ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_EXTENDED;
264 break;
265 case T_DATA:
266 shape_id = ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_RDATA;
267 break;
268 default:
269 shape_id = ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER;
270 break;
271 }
272
273 if (flags & FL_FREEZE) {
274 shape_id = rb_shape_transition_frozen(shape_id);
275 }
276
277 shape_id = rb_shape_transition_slot_size(shape_id, rb_gc_size_slot_size(alloc_size));
278
279 return (flags & SHAPE_FLAG_MASK) | ((VALUE)shape_id << SHAPE_FLAG_SHIFT);
280}
281
282VALUE
283rb_zjit_defined_ivar(VALUE obj, ID id, VALUE pushval)
284{
285 VALUE result = rb_ivar_defined(obj, id);
286 return result ? pushval : Qnil;
287}
288
289bool
290rb_zjit_method_tracing_currently_enabled(void)
291{
292 rb_event_flag_t tracing_events;
293 if (rb_multi_ractor_p()) {
294 tracing_events = ruby_vm_event_enabled_global_flags;
295 }
296 else {
297 // At the time of writing, events are never removed from
298 // ruby_vm_event_enabled_global_flags so always checking using it would
299 // mean we don't compile even after tracing is disabled.
300 tracing_events = rb_ec_ractor_hooks(GET_EC())->events;
301 }
302
303 return tracing_events & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN);
304}
305
306// Check if any ISEQ trace events are currently enabled.
307// Used to prevent ZJIT from compiling while tracing is active, since ZJIT's
308// send fallback (rb_vm_opt_send_without_block) uses VM_EXEC which sets
309// VM_FRAME_FLAG_FINISH on the callee frame, changing exception handling
310// semantics for throw TAG_RETURN (e.g. return from rescue).
311bool
312rb_zjit_iseq_tracing_currently_enabled(void)
313{
314 rb_event_flag_t tracing_events;
315 if (rb_multi_ractor_p()) {
316 tracing_events = ruby_vm_event_enabled_global_flags;
317 }
318 else {
319 tracing_events = rb_ec_ractor_hooks(GET_EC())->events;
320 }
321
322 return tracing_events & ISEQ_TRACE_EVENTS;
323}
324
325bool
326rb_zjit_insn_leaf(int insn, const VALUE *opes)
327{
328 return insn_leaf(insn, opes);
329}
330
331ID
332rb_zjit_local_id(const rb_iseq_t *iseq, unsigned idx)
333{
334 return ISEQ_BODY(iseq)->local_table[idx];
335}
336
337bool rb_zjit_cme_is_cfunc(const rb_callable_method_entry_t *me, const void *func);
338
340rb_zjit_vm_search_method(VALUE cd_owner, struct rb_call_data *cd, VALUE recv);
341
342bool
343rb_zjit_class_initialized_p(VALUE klass)
344{
345 return RCLASS_INITIALIZED_P(klass);
346}
347
348// Whether rb_class_superclass can be called on the class without raising: it raises
349// TypeError when the superclasses array is unbuilt (an uninitialized class, e.g.
350// Class.allocate), except for BasicObject, which it special-cases to return nil.
351bool
352rb_zjit_can_load_superclass_p(VALUE klass)
353{
354 return klass == rb_cBasicObject || RCLASS_SUPERCLASSES(klass) != NULL;
355}
356
357rb_alloc_func_t rb_zjit_class_get_alloc_func(VALUE klass);
358
359// Defined in struct.c, where struct_alloc is visible.
360bool rb_zjit_class_has_struct_allocator(VALUE klass);
361
362VALUE rb_class_allocate_instance(VALUE klass);
363
364bool
365rb_zjit_class_has_default_allocator(VALUE klass)
366{
367 assert(RCLASS_INITIALIZED_P(klass));
368 assert(!RCLASS_SINGLETON_P(klass));
369 rb_alloc_func_t alloc = rb_zjit_class_get_alloc_func(klass);
370 return alloc == rb_class_allocate_instance;
371}
372
373
374VALUE rb_vm_get_untagged_block_handler(rb_control_frame_t *reg_cfp);
375bool rb_vm_once_done_value(ISE is, VALUE *result);
376
377// Primitives used by zjit.rb. Don't put other functions below, which wouldn't use them.
378VALUE rb_zjit_enable(rb_execution_context_t *ec, VALUE self);
379VALUE rb_zjit_assert_compiles(rb_execution_context_t *ec, VALUE self);
380VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self, VALUE target_key);
381VALUE rb_zjit_reset_stats_bang(rb_execution_context_t *ec, VALUE self);
382VALUE rb_zjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self);
383VALUE rb_zjit_print_stats_p(rb_execution_context_t *ec, VALUE self);
384VALUE rb_zjit_get_stats_file_path_p(rb_execution_context_t *ec, VALUE self);
385VALUE rb_zjit_trace_exit_locations_enabled_p(rb_execution_context_t *ec, VALUE self);
386VALUE rb_zjit_get_exit_locations(rb_execution_context_t *ec, VALUE self);
387
388// Preprocessed zjit.rb generated during build
389#include "zjit.rbinc"
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define RUBY_EVENT_C_CALL
A method, written in C, is called.
Definition event.h:43
#define RUBY_EVENT_C_RETURN
Return from a method, written in C.
Definition event.h:44
uint32_t rb_event_flag_t
Represents event(s).
Definition event.h:108
#define T_MASK
Old name of RUBY_T_MASK.
Definition value_type.h:68
#define T_STRUCT
Old name of RUBY_T_STRUCT.
Definition value_type.h:79
#define T_DATA
Old name of RUBY_T_DATA.
Definition value_type.h:60
#define Qnil
Old name of RUBY_Qnil.
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
#define FL_FREEZE
Old name of RUBY_FL_FREEZE.
Definition fl_type.h:65
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:468
VALUE rb_cBasicObject
BasicObject class.
Definition object.c:58
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1123
Defines RBIMPL_HAS_BUILTIN.
VALUE rb_ivar_defined(VALUE obj, ID name)
Queries if the instance variable is defined at the object.
Definition variable.c:2201
VALUE(* rb_alloc_func_t)(VALUE klass)
This is the type of functions that ruby calls when trying to allocate an object.
Definition vm.h:219
#define RTEST
This is an old name of RB_TEST.
#define USE_FLONUM
Definition method.h:63
Definition vm_core.h:299
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40