Ruby 4.1.0dev (2026-10-28 revision 1b06b950bb460d20a9dcecb8b97d363adbf4a358)
zjit.c (1b06b950bb460d20a9dcecb8b97d363adbf4a358)
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
130static VALUE
131rb_zjit_total_compile_time_ns_get(rb_execution_context_t *ec, VALUE self)
132{
133 return ULL2NUM(rb_zjit_total_compile_time_ns);
134}
135
136static VALUE
137rb_zjit_max_compile_time_ns_get(rb_execution_context_t *ec, VALUE self)
138{
139 return ULL2NUM(rb_zjit_max_compile_time_ns);
140}
141
142void rb_zjit_update_max_compile_time_ns(uint64_t max_compile_time_ns);
143
144static VALUE
145rb_zjit_max_compile_time_ns_set(rb_execution_context_t *ec, VALUE self, VALUE max_compile_time_ns)
146{
147 rb_zjit_update_max_compile_time_ns(NUM2ULL(max_compile_time_ns));
148 return max_compile_time_ns;
149}
150
151void rb_zjit_profile_disable(const rb_iseq_t *iseq);
152int rb_zjit_insn_to_bare_insn(int insn);
153
154void
155rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception)
156{
157 RB_VM_LOCKING() {
158 rb_vm_barrier();
159
160 // Compile a block version starting at the current instruction
161 uint8_t *rb_zjit_iseq_gen_entry_point(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception); // defined in Rust
162 uintptr_t code_ptr = (uintptr_t)rb_zjit_iseq_gen_entry_point(iseq, ec, jit_exception);
163
164 if (jit_exception) {
165 ISEQ_BODY(iseq)->jit_exception = (rb_jit_func_t)code_ptr;
166 }
167 else {
168 ISEQ_BODY(iseq)->jit_entry = (rb_jit_func_t)code_ptr;
169 }
170 }
171}
172
173// This is used by a function stub to install compiled code as the ISEQ's entry point.
174void
175rb_zjit_iseq_set_jit_entry(const rb_iseq_t *iseq, void *code_ptr)
176{
177 ISEQ_BODY(iseq)->jit_entry = (rb_jit_func_t)code_ptr;
178}
179
180extern VALUE *rb_vm_base_ptr(struct rb_control_frame_struct *cfp);
181
182// Convert a given ISEQ's instructions to zjit_* instructions
183void
184rb_zjit_profile_enable(const rb_iseq_t *iseq)
185{
186 // This table encodes an opcode into the instruction's address
187 const void *const *insn_table = rb_vm_get_insns_address_table();
188
189 unsigned int insn_idx = 0;
190 while (insn_idx < ISEQ_BODY(iseq)->iseq_size) {
191 int insn = rb_vm_insn_addr2opcode((void *)ISEQ_BODY(iseq)->iseq_encoded[insn_idx]);
192 int zjit_insn = vm_bare_insn_to_zjit_insn(insn);
193 if (insn != zjit_insn) {
194 ISEQ_BODY(iseq)->iseq_encoded[insn_idx] = (VALUE)insn_table[zjit_insn];
195 }
196 insn_idx += insn_len(insn);
197 }
198}
199
200// Return false if a function stub has not collected enough profiles yet, enabling
201// profiling instructions as needed. Return true once enough profiles are collected.
202bool
203rb_zjit_iseq_has_profiled_enough(const rb_iseq_t *iseq)
204{
205 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
206
207 if (body->jit_entry_calls < rb_zjit_profile_threshold) {
208 // Skip the unprofiled warmup. The compiled caller already establishes
209 // that the callee is hot, so go straight to the profiling window.
210 body->jit_entry_calls = rb_zjit_profile_threshold;
211 rb_zjit_profile_enable(iseq);
212 }
213 else {
214 body->jit_entry_calls++;
215 }
216
217 return body->jit_entry_calls >= rb_zjit_call_threshold;
218}
219
220// Convert a given ISEQ's ZJIT instructions to bare instructions
221void
222rb_zjit_profile_disable(const rb_iseq_t *iseq)
223{
224 // This table encodes an opcode into the instruction's address
225 const void *const *insn_table = rb_vm_get_insns_address_table();
226
227 unsigned int insn_idx = 0;
228 while (insn_idx < ISEQ_BODY(iseq)->iseq_size) {
229 int insn = rb_vm_insn_addr2opcode((void *)ISEQ_BODY(iseq)->iseq_encoded[insn_idx]);
230 int bare_insn = vm_zjit_insn_to_bare_insn(insn);
231 if (insn != bare_insn) {
232 ISEQ_BODY(iseq)->iseq_encoded[insn_idx] = (VALUE)insn_table[bare_insn];
233 }
234 insn_idx += insn_len(insn);
235 }
236}
237
238// Map `zjit_* instructions back to their bare form. This is an identity function for all others.
239int
240rb_zjit_insn_to_bare_insn(int insn)
241{
242 return vm_zjit_insn_to_bare_insn(insn);
243}
244
245// Update a YARV instruction to a given opcode (to disable ZJIT profiling).
246void
247rb_zjit_iseq_insn_set(const rb_iseq_t *iseq, unsigned int insn_idx, enum ruby_vminsn_type bare_insn)
248{
249#if RUBY_DEBUG
250 int insn = rb_vm_insn_addr2opcode((void *)ISEQ_BODY(iseq)->iseq_encoded[insn_idx]);
251 RUBY_ASSERT(vm_zjit_insn_to_bare_insn(insn) == (int)bare_insn);
252#endif
253 const void *const *insn_table = rb_vm_get_insns_address_table();
254 ISEQ_BODY(iseq)->iseq_encoded[insn_idx] = (VALUE)insn_table[bare_insn];
255}
256
257void
258rb_zjit_print_exception(void)
259{
260 VALUE exception = rb_errinfo();
261 rb_set_errinfo(Qnil);
262 assert(RTEST(exception));
263 rb_warn("Ruby error: %"PRIsVALUE"", rb_funcall(exception, rb_intern("full_message"), 0));
264}
265
266bool
267rb_zjit_singleton_class_p(VALUE klass)
268{
269 return RCLASS_SINGLETON_P(klass);
270}
271
272/* Sets all of the required shape flags for the object including the layout type,
273 * the frozen status, and the slot size. Mimics `rb_newobj`.
274 */
275VALUE
276rb_zjit_new_obj_shape(VALUE flags, size_t alloc_size)
277{
278 shape_id_t shape_id;
279 switch (flags & T_MASK) {
280 case T_OBJECT:
281 shape_id = ROOT_SHAPE_ID;
282 break;
283 case T_STRUCT:
284 shape_id = ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_EXTENDED;
285 break;
286 case T_DATA:
287 shape_id = ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_RDATA;
288 break;
289 default:
290 shape_id = ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER;
291 break;
292 }
293
294 if (flags & FL_FREEZE) {
295 shape_id = rb_shape_transition_frozen(shape_id);
296 }
297
298 shape_id = rb_shape_transition_slot_size(shape_id, rb_gc_size_slot_size(alloc_size));
299
300 return (flags & SHAPE_FLAG_MASK) | ((VALUE)shape_id << SHAPE_FLAG_SHIFT);
301}
302
303VALUE
304rb_zjit_defined_ivar(VALUE obj, ID id, VALUE pushval)
305{
306 VALUE result = rb_ivar_defined(obj, id);
307 return result ? pushval : Qnil;
308}
309
310bool
311rb_zjit_method_tracing_currently_enabled(void)
312{
313 rb_event_flag_t tracing_events;
314 if (rb_multi_ractor_p()) {
315 tracing_events = ruby_vm_event_enabled_global_flags;
316 }
317 else {
318 // At the time of writing, events are never removed from
319 // ruby_vm_event_enabled_global_flags so always checking using it would
320 // mean we don't compile even after tracing is disabled.
321 tracing_events = rb_ec_ractor_hooks(GET_EC())->events;
322 }
323
324 return tracing_events & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN);
325}
326
327// Check if any ISEQ trace events are currently enabled.
328// Used to prevent ZJIT from compiling while tracing is active, since ZJIT's
329// send fallback (rb_vm_opt_send_without_block) uses VM_EXEC which sets
330// VM_FRAME_FLAG_FINISH on the callee frame, changing exception handling
331// semantics for throw TAG_RETURN (e.g. return from rescue).
332bool
333rb_zjit_iseq_tracing_currently_enabled(void)
334{
335 rb_event_flag_t tracing_events;
336 if (rb_multi_ractor_p()) {
337 tracing_events = ruby_vm_event_enabled_global_flags;
338 }
339 else {
340 tracing_events = rb_ec_ractor_hooks(GET_EC())->events;
341 }
342
343 return tracing_events & ISEQ_TRACE_EVENTS;
344}
345
346bool
347rb_zjit_insn_leaf(int insn, const VALUE *opes)
348{
349 return insn_leaf(insn, opes);
350}
351
352ID
353rb_zjit_local_id(const rb_iseq_t *iseq, unsigned idx)
354{
355 return ISEQ_BODY(iseq)->local_table[idx];
356}
357
358bool rb_zjit_cme_is_cfunc(const rb_callable_method_entry_t *me, const void *func);
359
361rb_zjit_vm_search_method(VALUE cd_owner, struct rb_call_data *cd, VALUE recv);
362
363rb_alloc_func_t rb_zjit_class_get_alloc_func(VALUE klass);
364
365// Defined in struct.c, where struct_alloc and the members ivar are visible.
366bool rb_zjit_class_has_struct_allocator(VALUE klass);
367rb_len_t rb_zjit_struct_num_members(VALUE klass);
368ID rb_zjit_struct_member_id(VALUE klass, rb_len_t index);
369bool rb_zjit_struct_embedded_p(rb_len_t num_members);
370VALUE rb_struct_s_keyword_init(VALUE klass);
371
372VALUE rb_class_allocate_instance(VALUE klass);
373
374bool
375rb_zjit_class_has_default_allocator(VALUE klass)
376{
377 assert(RCLASS_INITIALIZED_P(klass));
378 assert(!RCLASS_SINGLETON_P(klass));
379 rb_alloc_func_t alloc = rb_zjit_class_get_alloc_func(klass);
380 return alloc == rb_class_allocate_instance;
381}
382
383
384VALUE rb_vm_get_untagged_block_handler(rb_control_frame_t *reg_cfp);
385bool rb_vm_once_done_value(ISE is, VALUE *result);
386
387bool
388rb_zjit_array_aref_with_adjusted_index(VALUE ary, rb_len_t index, VALUE *out)
389{
391 if (index >= 0 && index < RARRAY_LEN(ary)) {
392 *out = RARRAY_AREF(ary, index);
393 return true;
394 }
395 else {
396 return false;
397 }
398}
399
400// Primitives used by zjit.rb. Don't put other functions below, which wouldn't use them.
401VALUE rb_zjit_enable(rb_execution_context_t *ec, VALUE self);
402VALUE rb_zjit_assert_compiles(rb_execution_context_t *ec, VALUE self);
403VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self, VALUE target_key);
404VALUE rb_zjit_reset_stats_bang(rb_execution_context_t *ec, VALUE self);
405VALUE rb_zjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self);
406VALUE rb_zjit_print_stats_p(rb_execution_context_t *ec, VALUE self);
407VALUE rb_zjit_get_stats_file_path_p(rb_execution_context_t *ec, VALUE self);
408VALUE rb_zjit_trace_exit_locations_enabled_p(rb_execution_context_t *ec, VALUE self);
409VALUE rb_zjit_get_exit_locations(rb_execution_context_t *ec, VALUE self);
410
411// Preprocessed zjit.rb generated during build
412#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 ULL2NUM
Old name of RB_ULL2NUM.
Definition long_long.h:31
#define Qnil
Old name of RUBY_Qnil.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
#define NUM2ULL
Old name of RB_NUM2ULL.
Definition long_long.h:35
#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:464
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1148
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:2210
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 RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
#define RARRAY_AREF(a, i)
References an object in an array.
Definition rarray.h:421
#define RTEST
This is an old name of RB_TEST.
#define USE_FLONUM
Definition method.h:63
Definition vm_core.h:301
long rb_len_t
A signed integer type for lengths and indices of objects such as String and Array.
Definition value.h:142
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
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376