Ruby 4.1.0dev (2026-10-28 revision 637bd7cba1bd5a89b4128eac4cb389a8e236b5b3)
zjit.h (637bd7cba1bd5a89b4128eac4cb389a8e236b5b3)
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_ctx.
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;
114extern uint64_t rb_zjit_max_compile_time_ns;
115extern uint64_t rb_zjit_total_compile_time_ns;
116void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception);
117void rb_zjit_profile_insn(uint32_t insn, rb_execution_context_t *ec);
118void rb_zjit_profile_enable(const rb_iseq_t *iseq);
119void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
120void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme);
121void rb_zjit_cme_free(const rb_callable_method_entry_t *cme);
122void rb_zjit_klass_free(VALUE klass);
123void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq);
124void rb_zjit_constant_state_changed(ID id);
125void rb_zjit_iseq_mark(void *payload);
126void rb_zjit_iseq_update_references(void *payload);
127void rb_zjit_mark_all_writable(void);
128void rb_zjit_mark_all_executable(void);
129void rb_zjit_iseq_free(const rb_iseq_t *iseq);
130void rb_zjit_invalidate_single_ractor(void);
131void rb_zjit_tracing_invalidate_all(void);
132void rb_zjit_invalidate_newobj_hook(void);
133void rb_zjit_invalidate_no_singleton_class(VALUE klass);
134void rb_zjit_invalidate_root_box(void);
135void rb_zjit_jit_frame_update_references(zjit_jit_frame_t *jit_frame);
136void rb_zjit_materialize_frames(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
137void rb_zjit_materialize_frames_for_longjmp(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
138size_t rb_zjit_hash_new_size(VALUE *flags_out, size_t size);
139VALUE rb_zjit_new_obj_shape(VALUE flags, size_t alloc_size);
140bool rb_zjit_class_allocate_instance_fastpath(VALUE klass, size_t *size_out, VALUE *flags_out);
141bool rb_zjit_str_resurrect_fastpath(VALUE str, bool chilled, size_t *size_out, VALUE *flags_out, rb_len_t *len_out, size_t *byte_size_out);
142bool rb_zjit_array_dup_can_fastpath(VALUE ary, size_t *alloc_size_out, VALUE *flags_out, rb_len_t *len_out);
143bool rb_zjit_array_new_can_fastpath(rb_len_t len, size_t *alloc_size_out, VALUE *flags_out);
144bool rb_zjit_hash_dup_can_fastpath(VALUE hash, size_t *alloc_size_out, VALUE *flags_out, VALUE *ifnone_out, rb_len_t *bound_out);
145void rb_zjit_range_new_fastpath(bool exclude_end, size_t *alloc_size_out, VALUE *flags_out);
146void rb_zjit_array_new_fastpath(size_t *alloc_size_out, VALUE *flags_out);
147bool rb_zjit_newobj_hook_enabled_p(void);
148
149// Special value for cfp->jit_return that means "this is a C method frame, use
150// rb_zjit_c_frame as the JITFrame". We don't control the native stack layout
151// for C frames, so there's no per-call JITFrame storage; we set this sentinel
152// instead of a heap-allocated JITFrame pointer.
153#define ZJIT_JIT_RETURN_C_FRAME 0x1
154
155static inline const zjit_jit_frame_t *
156CFP_ZJIT_FRAME(const rb_control_frame_t *cfp)
157{
158 if ((VALUE)cfp->jit_return == ZJIT_JIT_RETURN_C_FRAME) {
159 return &rb_zjit_c_frame;
160 }
161 else {
162 // Read JITFrame from this frame's stack slot. cfp->jit_return points at
163 // the slot reserved for this frame's inlining depth, so distinct frames in
164 // the same JIT function read distinct slots. An initial frame describing
165 // the entry PC + iseq is written by gen_entry_point() for the top-level
166 // frame and by gen_push_inline_frame() for inlined frames. That entry
167 // PC is correct only at the frame's start; because the PC this frame reports
168 // must track where execution currently is, later gen_save_pc_for_gc() calls
169 // rewrite the slot with the live PC as execution advances through the frame,
170 // before any non-leaf C call.
171 return (const zjit_jit_frame_t *)((VALUE *)cfp->jit_return)[-1];
172 }
173}
174#else
175#define rb_zjit_entry 0
176#define rb_zjit_compiling_p false
177static inline void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception) {}
178static inline void rb_zjit_profile_insn(uint32_t insn, rb_execution_context_t *ec) {}
179static inline void rb_zjit_profile_enable(const rb_iseq_t *iseq) {}
180static inline void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {}
181static inline void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme) {}
182static inline void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq) {}
183static inline void rb_zjit_constant_state_changed(ID id) {}
184static inline void rb_zjit_invalidate_single_ractor(void) {}
185static inline void rb_zjit_tracing_invalidate_all(void) {}
186static inline void rb_zjit_invalidate_newobj_hook(void) {}
187static inline void rb_zjit_invalidate_no_singleton_class(VALUE klass) {}
188static inline void rb_zjit_invalidate_root_box(void) {}
189static inline void rb_zjit_jit_frame_update_references(zjit_jit_frame_t *jit_frame) {}
190static inline void rb_zjit_materialize_frames(const rb_execution_context_t *ec, rb_control_frame_t *cfp) {}
191static inline void rb_zjit_materialize_frames_for_longjmp(const rb_execution_context_t *ec, rb_control_frame_t *cfp) {}
192static inline const zjit_jit_frame_t *CFP_ZJIT_FRAME(const rb_control_frame_t *cfp) { return NULL; }
193#endif // #if USE_ZJIT
194
195#define rb_zjit_enabled_p (rb_zjit_entry != 0)
196
197// Return true if a given CFP has ZJIT's JITFrame.
198static inline bool
199CFP_ZJIT_FRAME_P(const rb_control_frame_t *cfp)
200{
201 if (!rb_zjit_enabled_p) return false;
202 return cfp->jit_return != NULL;
203}
204
205static inline const VALUE*
206CFP_PC(const rb_control_frame_t *cfp)
207{
208 if (CFP_ZJIT_FRAME_P(cfp)) {
209 return CFP_ZJIT_FRAME(cfp)->pc;
210 }
211 return cfp->pc;
212}
213
214static inline const rb_iseq_t*
215CFP_ISEQ(const rb_control_frame_t *cfp)
216{
217 if (CFP_ZJIT_FRAME_P(cfp)) {
218 return CFP_ZJIT_FRAME(cfp)->iseq;
219 }
220 return cfp->_iseq;
221}
222
223#endif // #ifndef ZJIT_H
int len
Length of the buffer.
Definition io.h:8
Definition method.h:63
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