Ruby 4.1.0dev (2026-09-07 revision 11ce3778c6eacc10897729314e5ab3bed7830971)
zjit.h (11ce3778c6eacc10897729314e5ab3bed7830971)
1#ifndef ZJIT_H
2#define ZJIT_H 1
3//
4// This file contains definitions ZJIT exposes to the CRuby codebase
5//
6
7#include "shape.h" // for shape_id_t
8
9// ZJIT_STATS controls whether to support runtime counters in the interpreter
10#ifndef ZJIT_STATS
11# define ZJIT_STATS (USE_ZJIT && RUBY_DEBUG)
12#endif
13
14// JITFrame is defined here as the single source of truth and imported into
15// Rust via bindgen. C code reads fields directly; Rust uses an impl block.
16typedef struct zjit_jit_frame {
17 // Program counter for this frame, used for backtraces and GC.
18 // NULL for C frames (they don't have a Ruby PC).
19 const VALUE *pc;
20 // The ISEQ this frame belongs to. Marked via rb_execution_context_mark.
21 // NULL for C frames.
22 const rb_iseq_t *iseq;
23 // Whether to materialize block_code when this frame is materialized.
24 // True when the ISEQ doesn't contain send/invokesuper/invokeblock
25 // (which write block_code themselves), so we must restore it.
26 // Always false for C frames.
27 bool materialize_block_code;
28
29 // Number of stack map entries in stack[].
30 uint32_t stack_size;
31 // Flexible array of stack map entries, executed in order by
32 // zjit_materialize_frames(). See the ZJIT_STACK_MAP_* opcodes above.
33 VALUE stack[];
35
36#if USE_ZJIT
37// Stack map entries are opcodes for zjit_materialize_frames(), which walks them
38// in order while moving a cursor down the VM stack. An untagged entry is an
39// immediate Ruby VALUE to store; the tagged forms below copy from the native
40// stack, skip slots, or move the cursor. Stack maps never contain heap VALUEs,
41// so these tags are available: they are not Qfalse (0), and their low 3 bits
42// are zero, so RB_SPECIAL_CONST_P is false. Tags must stay non-zero multiples
43// of 8 for that to hold.
44#define ZJIT_STACK_MAP_VREG_TAG 0x08
45#define ZJIT_STACK_MAP_SKIP_TAG 0x10
46#define ZJIT_STACK_MAP_BASE_PTR_TAG 0x18
47#define ZJIT_STACK_MAP_TAG_MASK 0xff
48#define ZJIT_STACK_MAP_SHIFT 8
49
50// The BASE_PTR payload packs two fields above the tag byte: the slot index in
51// bits 8..=31 and the operand stack size in bits 32..=63.
52#define ZJIT_STACK_MAP_BASE_PTR_SIZE_SHIFT 32
53#define ZJIT_STACK_MAP_BASE_PTR_INDEX_MASK 0xffffff
54
55static inline bool
56ZJIT_STACK_MAP_VREG_P(VALUE entry)
57{
58 return (entry & ZJIT_STACK_MAP_TAG_MASK) == ZJIT_STACK_MAP_VREG_TAG;
59}
60
61static inline size_t
62ZJIT_STACK_MAP_VREG_INDEX(VALUE entry)
63{
64 return entry >> ZJIT_STACK_MAP_SHIFT;
65}
66
67static inline bool
68ZJIT_STACK_MAP_SKIP_P(VALUE entry)
69{
70 return (entry & ZJIT_STACK_MAP_TAG_MASK) == ZJIT_STACK_MAP_SKIP_TAG;
71}
72
73static inline size_t
74ZJIT_STACK_MAP_SKIP_SIZE(VALUE entry)
75{
76 return entry >> ZJIT_STACK_MAP_SHIFT;
77}
78
79// Anchor the write cursor using the SP register the JIT saved on its native
80// stack, instead of cfp->sp. cfp->sp is not a reliable starting point for a
81// frame that is in the middle of a non-leaf C call, as e.g. raising
82// ArgumentError can push through and move cfp->sp, then use the stack map.
83// gen_prepare_non_leaf_call() emits this opcode, always as stack[0], so the
84// entries after decode to the right place.
85static inline bool
86ZJIT_STACK_MAP_BASE_PTR_P(VALUE entry)
87{
88 return (entry & ZJIT_STACK_MAP_TAG_MASK) == ZJIT_STACK_MAP_BASE_PTR_TAG;
89}
90
91// VALUE index from cfp->jit_return down to the native stack slot holding the
92// saved SP register, i.e. base_ptr is `((VALUE **)cfp->jit_return)[-index]`.
93// There is one such slot per compiled function, so the index depends on the
94// frame's inlining depth.
95static inline size_t
96ZJIT_STACK_MAP_BASE_PTR_SLOT_INDEX(VALUE entry)
97{
98 return (entry >> ZJIT_STACK_MAP_SHIFT) & ZJIT_STACK_MAP_BASE_PTR_INDEX_MASK;
99}
100
101// Number of VM stack slots above base_ptr, i.e. the frame's operand stack
102// depth. The cursor is set to `base_ptr + this`.
103static inline size_t
104ZJIT_STACK_MAP_BASE_PTR_STACK_SIZE(VALUE entry)
105{
106 return entry >> ZJIT_STACK_MAP_BASE_PTR_SIZE_SHIFT;
107}
108
109extern void *rb_zjit_entry;
110extern bool rb_zjit_compiling_p;
111extern const zjit_jit_frame_t rb_zjit_c_frame;
112extern unsigned int rb_zjit_call_threshold;
113extern unsigned int rb_zjit_profile_threshold;
114void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception);
115void rb_zjit_profile_insn(uint32_t insn, rb_execution_context_t *ec);
116void rb_zjit_profile_enable(const rb_iseq_t *iseq);
117void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
118void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme);
119void rb_zjit_cme_free(const rb_callable_method_entry_t *cme);
120void rb_zjit_klass_free(VALUE klass);
121void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq);
122void rb_zjit_constant_state_changed(ID id);
123void rb_zjit_iseq_mark(void *payload);
124void rb_zjit_iseq_update_references(void *payload);
125void rb_zjit_mark_all_writable(void);
126void rb_zjit_mark_all_executable(void);
127void rb_zjit_iseq_free(const rb_iseq_t *iseq);
128void rb_zjit_invalidate_single_ractor(void);
129void rb_zjit_tracing_invalidate_all(void);
130void rb_zjit_invalidate_newobj_hook(void);
131void rb_zjit_invalidate_no_singleton_class(VALUE klass);
132void rb_zjit_invalidate_root_box(void);
133void rb_zjit_jit_frame_update_references(zjit_jit_frame_t *jit_frame);
134void rb_zjit_materialize_frames(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
135void rb_zjit_materialize_frames_for_longjmp(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
136size_t rb_zjit_hash_new_size(VALUE *flags_out, size_t size);
137VALUE rb_zjit_new_obj_shape(VALUE flags, size_t alloc_size);
138bool rb_zjit_class_allocate_instance_fastpath(VALUE klass, size_t *size_out, VALUE *flags_out);
139bool rb_zjit_str_resurrect_fastpath(VALUE str, bool chilled, size_t *size_out, VALUE *flags_out, long *len_out, size_t *byte_size_out);
140bool rb_zjit_array_dup_can_fastpath(VALUE ary, size_t *alloc_size_out, VALUE *flags_out, long *len_out);
141bool rb_zjit_array_new_can_fastpath(long len, size_t *alloc_size_out, VALUE *flags_out);
142bool rb_zjit_hash_dup_can_fastpath(VALUE hash, size_t *alloc_size_out, VALUE *flags_out, VALUE *ifnone_out, long *bound_out);
143void rb_zjit_range_new_fastpath(bool exclude_end, size_t *alloc_size_out, VALUE *flags_out);
144void rb_zjit_array_new_fastpath(size_t *alloc_size_out, VALUE *flags_out);
145bool rb_zjit_newobj_hook_enabled_p(void);
146
147// Special value for cfp->jit_return that means "this is a C method frame, use
148// rb_zjit_c_frame as the JITFrame". We don't control the native stack layout
149// for C frames, so there's no per-call JITFrame storage; we set this sentinel
150// instead of a heap-allocated JITFrame pointer.
151#define ZJIT_JIT_RETURN_C_FRAME 0x1
152
153static inline const zjit_jit_frame_t *
154CFP_ZJIT_FRAME(const rb_control_frame_t *cfp)
155{
156 if ((VALUE)cfp->jit_return == ZJIT_JIT_RETURN_C_FRAME) {
157 return &rb_zjit_c_frame;
158 }
159 else {
160 // Read JITFrame from this frame's stack slot. cfp->jit_return points at
161 // the slot reserved for this frame's inlining depth, so distinct frames in
162 // the same JIT function read distinct slots. An initial frame describing
163 // the entry PC + iseq is written by gen_entry_point() for the top-level
164 // frame and by gen_push_inline_frame() for inlined frames. That entry
165 // PC is correct only at the frame's start; because the PC this frame reports
166 // must track where execution currently is, later gen_save_pc_for_gc() calls
167 // rewrite the slot with the live PC as execution advances through the frame,
168 // before any non-leaf C call.
169 return (const zjit_jit_frame_t *)((VALUE *)cfp->jit_return)[-1];
170 }
171}
172#else
173#define rb_zjit_entry 0
174#define rb_zjit_compiling_p false
175static inline void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception) {}
176static inline void rb_zjit_profile_insn(uint32_t insn, rb_execution_context_t *ec) {}
177static inline void rb_zjit_profile_enable(const rb_iseq_t *iseq) {}
178static inline void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {}
179static inline void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme) {}
180static inline void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq) {}
181static inline void rb_zjit_constant_state_changed(ID id) {}
182static inline void rb_zjit_invalidate_single_ractor(void) {}
183static inline void rb_zjit_tracing_invalidate_all(void) {}
184static inline void rb_zjit_invalidate_newobj_hook(void) {}
185static inline void rb_zjit_invalidate_no_singleton_class(VALUE klass) {}
186static inline void rb_zjit_invalidate_root_box(void) {}
187static inline void rb_zjit_jit_frame_update_references(zjit_jit_frame_t *jit_frame) {}
188static inline void rb_zjit_materialize_frames(const rb_execution_context_t *ec, rb_control_frame_t *cfp) {}
189static inline void rb_zjit_materialize_frames_for_longjmp(const rb_execution_context_t *ec, rb_control_frame_t *cfp) {}
190static inline const zjit_jit_frame_t *CFP_ZJIT_FRAME(const rb_control_frame_t *cfp) { return NULL; }
191#endif // #if USE_ZJIT
192
193#define rb_zjit_enabled_p (rb_zjit_entry != 0)
194
195// Return true if a given CFP has ZJIT's JITFrame.
196static inline bool
197CFP_ZJIT_FRAME_P(const rb_control_frame_t *cfp)
198{
199 if (!rb_zjit_enabled_p) return false;
200 return cfp->jit_return != NULL;
201}
202
203static inline const VALUE*
204CFP_PC(const rb_control_frame_t *cfp)
205{
206 if (CFP_ZJIT_FRAME_P(cfp)) {
207 return CFP_ZJIT_FRAME(cfp)->pc;
208 }
209 return cfp->pc;
210}
211
212static inline const rb_iseq_t*
213CFP_ISEQ(const rb_control_frame_t *cfp)
214{
215 if (CFP_ZJIT_FRAME_P(cfp)) {
216 return CFP_ZJIT_FRAME(cfp)->iseq;
217 }
218 return cfp->_iseq;
219}
220
221#endif // #ifndef ZJIT_H
int len
Length of the buffer.
Definition io.h:8
Definition method.h:63
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