Ruby 4.1.0dev (2026-10-07 revision 9ce0df671980d669cbab8afc48124c7453897533)
jit.c (9ce0df671980d669cbab8afc48124c7453897533)
1// Glue code shared between YJIT and ZJIT for use from Rust.
2// For FFI safety and bindgen compatibility reasons, certain types of C
3// functions require wrapping before they can be called from Rust. Those show
4// up here.
5//
6// Code specific to YJIT and ZJIT should go to yjit.c and zjit.c respectively.
7
8#include "internal.h"
9#include "vm_core.h"
10#include "vm_callinfo.h"
11#include "builtin.h"
12#include "insns.inc"
13#include "insns_info.inc"
14#include "iseq.h"
15#include "internal/compile.h"
16#include "internal/gc.h"
17#include "internal/jit.h"
18#include "vm_sync.h"
19#include "internal/fixnum.h"
20#include "internal/hash.h"
21#include "internal/string.h"
22#include "internal/class.h"
23#include "internal/imemo.h"
24#include "internal/struct.h"
26#include "zjit.h"
27
28#ifndef _WIN32
29#include <sys/mman.h>
30#endif
31
32enum jit_bindgen_constants {
33 // Field offsets for the RObject struct
34 ROBJECT_OFFSET_AS_HEAP_FIELDS = offsetof(struct RObject, as.extended),
35 ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary),
36
37 // Field offset for prime classext's fields_obj from a class pointer
38 RCLASS_OFFSET_PRIME_FIELDS_OBJ = offsetof(struct RClass_and_rb_classext_t, classext.fields_obj),
39
40 // Field offset for fields_obj in T_DATA
41 TDATA_OFFSET_FIELDS_OBJ = offsetof(struct RTypedData, fields_obj),
42
43 // Field offset for the RHash struct
44 RUBY_OFFSET_RHASH_IFNONE = offsetof(struct RHash, ifnone),
45
46 // Field offsets for the embedded ar_table in a hash
47 RUBY_OFFSET_RHASH_AR_HINT = sizeof(struct RHash) + offsetof(ar_table, ar_hint),
48 RUBY_OFFSET_RHASH_AR_PAIRS = sizeof(struct RHash) + offsetof(ar_table, pairs),
49
50 // Max pairs an embedded ar_table hash holds before it converts to an st_table
51 RUBY_RHASH_AR_TABLE_MAX_SIZE = RHASH_AR_TABLE_MAX_SIZE,
52
53 // Field offsets for the RString struct
54 RUBY_OFFSET_RSTRING_LEN = offsetof(struct RString, len),
55
56 // Shape constant related to RBasic::flags. (See RBASIC_SET_SHAPE_ID())
57 RB_SHAPE_FLAG_SHIFT = SHAPE_FLAG_SHIFT,
58
59 // Field offsets for rb_execution_context_t
60 RUBY_OFFSET_EC_CFP = offsetof(rb_execution_context_t, cfp),
61 RUBY_OFFSET_EC_INTERRUPT_FLAG = offsetof(rb_execution_context_t, interrupt_flag),
62 RUBY_OFFSET_EC_INTERRUPT_MASK = offsetof(rb_execution_context_t, interrupt_mask),
63 RUBY_OFFSET_EC_THREAD_PTR = offsetof(rb_execution_context_t, thread_ptr),
64 RUBY_OFFSET_EC_RACTOR_ID = offsetof(rb_execution_context_t, ractor_id),
65};
66
67// Manually bound in rust since this is out-of-range of `int`,
68// so this can't be in a `enum`, and we avoid `static const`
69// to avoid allocating storage for the constant.
70const shape_id_t rb_invalid_shape_id = INVALID_SHAPE_ID;
71
72unsigned int
73rb_iseq_encoded_size(const rb_iseq_t *iseq)
74{
75 return ISEQ_BODY(iseq)->iseq_size;
76}
77
78// Get the PC for a given index in an iseq
79VALUE *
80rb_iseq_pc_at_idx(const rb_iseq_t *iseq, uint32_t insn_idx)
81{
82 RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq));
83 RUBY_ASSERT_ALWAYS(insn_idx < ISEQ_BODY(iseq)->iseq_size);
84 VALUE *encoded = ISEQ_BODY(iseq)->iseq_encoded;
85 VALUE *pc = &encoded[insn_idx];
86 return pc;
87}
88
89// Get the opcode given a program counter. Can return trace opcode variants.
90int
91rb_iseq_opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc)
92{
93 // YJIT should only use iseqs after AST to bytecode compilation.
94 RUBY_ASSERT_ALWAYS(FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED));
95
96 const VALUE at_pc = *pc;
97 return rb_vm_insn_addr2opcode((const void *)at_pc);
98}
99
100// Get the bare opcode given a program counter. Always returns the base
101// instruction, stripping trace/zjit variants.
102int
103rb_iseq_bare_opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc)
104{
105 RUBY_ASSERT_ALWAYS(FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED));
106
107 const VALUE at_pc = *pc;
108 return rb_vm_insn_addr2insn((const void *)at_pc);
109}
110
111unsigned long
112rb_RSTRING_LEN(VALUE str)
113{
114 return RSTRING_LEN(str);
115}
116
117char *
118rb_RSTRING_PTR(VALUE str)
119{
120 return RSTRING_PTR(str);
121}
122
123const char *
124rb_insn_name(VALUE insn)
125{
126 return insn_name(insn);
127}
128
129unsigned int
130rb_vm_ci_argc(const struct rb_callinfo *ci)
131{
132 return vm_ci_argc(ci);
133}
134
135ID
136rb_vm_ci_mid(const struct rb_callinfo *ci)
137{
138 return vm_ci_mid(ci);
139}
140
141unsigned int
142rb_vm_ci_flag(const struct rb_callinfo *ci)
143{
144 return vm_ci_flag(ci);
145}
146
147const struct rb_callinfo_kwarg *
148rb_vm_ci_kwarg(const struct rb_callinfo *ci)
149{
150 return vm_ci_kwarg(ci);
151}
152
153int
154rb_get_cikw_keyword_len(const struct rb_callinfo_kwarg *cikw)
155{
156 return cikw->keyword_len;
157}
158
159VALUE
160rb_get_cikw_keywords_idx(const struct rb_callinfo_kwarg *cikw, int idx)
161{
162 return cikw->keywords[idx];
163}
164
165rb_method_visibility_t
166rb_METHOD_ENTRY_VISI(const rb_callable_method_entry_t *me)
167{
168 return METHOD_ENTRY_VISI(me);
169}
170
171rb_method_type_t
172rb_get_cme_def_type(const rb_callable_method_entry_t *cme)
173{
174 if (UNDEFINED_METHOD_ENTRY_P(cme)) {
175 return VM_METHOD_TYPE_UNDEF;
176 }
177 else {
178 return cme->def->type;
179 }
180}
181
182ID
183rb_get_cme_def_body_attr_id(const rb_callable_method_entry_t *cme)
184{
185 return cme->def->body.attr.id;
186}
187
188enum method_optimized_type
189rb_get_cme_def_body_optimized_type(const rb_callable_method_entry_t *cme)
190{
191 return cme->def->body.optimized.type;
192}
193
194unsigned int
195rb_get_cme_def_body_optimized_index(const rb_callable_method_entry_t *cme)
196{
197 return cme->def->body.optimized.index;
198}
199
201rb_get_cme_def_body_cfunc(const rb_callable_method_entry_t *cme)
202{
203 return UNALIGNED_MEMBER_PTR(cme->def, body.cfunc);
204}
205
206uintptr_t
207rb_get_def_method_serial(const rb_method_definition_t *def)
208{
209 return def->method_serial;
210}
211
212ID
213rb_get_def_original_id(const rb_method_definition_t *def)
214{
215 return def->original_id;
216}
217
218VALUE
219rb_get_def_bmethod_proc(rb_method_definition_t *def)
220{
221 RUBY_ASSERT(def->type == VM_METHOD_TYPE_BMETHOD);
222 return def->body.bmethod.proc;
223}
224
225rb_proc_t *
226rb_jit_get_proc_ptr(VALUE procv)
227{
228 rb_proc_t *proc;
229 GetProcPtr(procv, proc);
230 return proc;
231}
232
233VALUE
234rb_optimized_call(VALUE recv, rb_execution_context_t *ec, int argc, VALUE *argv, int kw_splat, VALUE block_handler)
235{
236 rb_proc_t *proc;
237 GetProcPtr(recv, proc);
238 return rb_vm_invoke_proc(ec, proc, argc, argv, kw_splat, block_handler,
239 rb_proc_refinements_cref_for_call(recv));
240}
241
242unsigned int
243rb_jit_iseq_builtin_attrs(const rb_iseq_t *iseq)
244{
245 return ISEQ_BODY(iseq)->builtin_attrs;
246}
247
248// Relaxed memory ordering, but called by the JIT with VM lock and barrier.
249void
250rb_jit_iseq_mark_ep_escape_recorded(const rb_iseq_t *iseq)
251{
252 rbimpl_atomic_store(&ISEQ_BODY(iseq)->jit_ep_escape_recorded, 1, RBIMPL_ATOMIC_RELAXED);
253}
254
255// Whether an EP escape of this iseq has been reported to the enabled JIT.
256bool
257rb_jit_iseq_ep_escape_recorded_p(const rb_iseq_t *iseq)
258{
259 return rbimpl_atomic_load(&ISEQ_BODY(iseq)->jit_ep_escape_recorded, RBIMPL_ATOMIC_RELAXED) != 0;
260}
261
262int
263rb_get_mct_argc(const rb_method_cfunc_t *mct)
264{
265 return mct->argc;
266}
267
268void *
269rb_get_mct_func(const rb_method_cfunc_t *mct)
270{
271 return (void*)(uintptr_t)mct->func; // this field is defined as type VALUE (*func)(ANYARGS)
272}
273
274const rb_iseq_t *
275rb_get_def_iseq_ptr(rb_method_definition_t *def)
276{
277 return def_iseq_ptr(def);
278}
279
280const rb_iseq_t *
281rb_get_iseq_body_local_iseq(const rb_iseq_t *iseq)
282{
283 return ISEQ_BODY(iseq)->local_iseq;
284}
285
286const rb_iseq_t *
287rb_get_iseq_body_parent_iseq(const rb_iseq_t *iseq)
288{
289 return ISEQ_BODY(iseq)->parent_iseq;
290}
291
292unsigned int
293rb_get_iseq_body_local_table_size(const rb_iseq_t *iseq)
294{
295 return ISEQ_BODY(iseq)->local_table_size;
296}
297
298VALUE *
299rb_get_iseq_body_iseq_encoded(const rb_iseq_t *iseq)
300{
301 return ISEQ_BODY(iseq)->iseq_encoded;
302}
303
304unsigned
305rb_get_iseq_body_stack_max(const rb_iseq_t *iseq)
306{
307 return ISEQ_BODY(iseq)->stack_max;
308}
309
310enum rb_iseq_type
311rb_get_iseq_body_type(const rb_iseq_t *iseq)
312{
313 return ISEQ_BODY(iseq)->type;
314}
315
316bool
317rb_get_iseq_flags_has_lead(const rb_iseq_t *iseq)
318{
319 return ISEQ_BODY(iseq)->param.flags.has_lead;
320}
321
322bool
323rb_get_iseq_flags_has_opt(const rb_iseq_t *iseq)
324{
325 return ISEQ_BODY(iseq)->param.flags.has_opt;
326}
327
328bool
329rb_get_iseq_flags_has_kw(const rb_iseq_t *iseq)
330{
331 return ISEQ_BODY(iseq)->param.flags.has_kw;
332}
333
334bool
335rb_get_iseq_flags_has_post(const rb_iseq_t *iseq)
336{
337 return ISEQ_BODY(iseq)->param.flags.has_post;
338}
339
340bool
341rb_get_iseq_flags_has_kwrest(const rb_iseq_t *iseq)
342{
343 return ISEQ_BODY(iseq)->param.flags.has_kwrest;
344}
345
346bool
347rb_get_iseq_flags_anon_kwrest(const rb_iseq_t *iseq)
348{
349 return ISEQ_BODY(iseq)->param.flags.anon_kwrest;
350}
351
352bool
353rb_get_iseq_flags_has_rest(const rb_iseq_t *iseq)
354{
355 return ISEQ_BODY(iseq)->param.flags.has_rest;
356}
357
358bool
359rb_get_iseq_flags_ruby2_keywords(const rb_iseq_t *iseq)
360{
361 return ISEQ_BODY(iseq)->param.flags.ruby2_keywords;
362}
363
364bool
365rb_get_iseq_flags_has_block(const rb_iseq_t *iseq)
366{
367 return ISEQ_BODY(iseq)->param.flags.has_block;
368}
369
370bool
371rb_get_iseq_flags_ambiguous_param0(const rb_iseq_t *iseq)
372{
373 return ISEQ_BODY(iseq)->param.flags.ambiguous_param0;
374}
375
376bool
377rb_get_iseq_flags_accepts_no_kwarg(const rb_iseq_t *iseq)
378{
379 return ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg;
380}
381
382bool
383rb_get_iseq_flags_forwardable(const rb_iseq_t *iseq)
384{
385 return ISEQ_BODY(iseq)->param.flags.forwardable;
386}
387
388// This is defined only as a named struct inside rb_iseq_constant_body.
389// By giving it a separate typedef, we make it nameable by rust-bindgen.
390// Bindgen's temp/anon name isn't guaranteed stable.
391typedef struct rb_iseq_param_keyword rb_iseq_param_keyword_struct;
392
393const rb_iseq_param_keyword_struct *
394rb_get_iseq_body_param_keyword(const rb_iseq_t *iseq)
395{
396 return ISEQ_BODY(iseq)->param.keyword;
397}
398
399unsigned
400rb_get_iseq_body_param_size(const rb_iseq_t *iseq)
401{
402 return ISEQ_BODY(iseq)->param.size;
403}
404
405int
406rb_get_iseq_body_param_lead_num(const rb_iseq_t *iseq)
407{
408 return ISEQ_BODY(iseq)->param.lead_num;
409}
410
411int
412rb_get_iseq_body_param_opt_num(const rb_iseq_t *iseq)
413{
414 return ISEQ_BODY(iseq)->param.opt_num;
415}
416
417const VALUE *
418rb_get_iseq_body_param_opt_table(const rb_iseq_t *iseq)
419{
420 return ISEQ_BODY(iseq)->param.opt_table;
421}
422
424rb_get_ec_cfp(const rb_execution_context_t *ec)
425{
426 return ec->cfp;
427}
428
429const rb_iseq_t *
430rb_get_cfp_iseq(struct rb_control_frame_struct *cfp)
431{
432 return CFP_ISEQ(cfp);
433}
434
435VALUE *
436rb_get_cfp_pc(struct rb_control_frame_struct *cfp)
437{
438 return (VALUE*)cfp->pc;
439}
440
441VALUE *
442rb_get_cfp_sp(struct rb_control_frame_struct *cfp)
443{
444 return cfp->sp;
445}
446
447VALUE
448rb_get_cfp_self(struct rb_control_frame_struct *cfp)
449{
450 return cfp->self;
451}
452
453VALUE *
454rb_get_cfp_ep(struct rb_control_frame_struct *cfp)
455{
456 return (VALUE*)cfp->ep;
457}
458
459const VALUE *
460rb_get_cfp_ep_level(struct rb_control_frame_struct *cfp, uint32_t lv)
461{
462 uint32_t i;
463 const VALUE *ep = (VALUE*)cfp->ep;
464 for (i = 0; i < lv; i++) {
465 ep = VM_ENV_PREV_EP(ep);
466 }
467 return ep;
468}
469
470VALUE
471rb_yarv_class_of(VALUE obj)
472{
473 return rb_class_of(obj);
474}
475
476// The FL_TEST() macro
477VALUE
478rb_FL_TEST(VALUE obj, VALUE flags)
479{
480 return RB_FL_TEST(obj, flags);
481}
482
483// The FL_TEST_RAW() macro, normally an internal implementation detail
484VALUE
485rb_FL_TEST_RAW(VALUE obj, VALUE flags)
486{
487 return FL_TEST_RAW(obj, flags);
488}
489
490// The RB_TYPE_P macro
491bool
492rb_RB_TYPE_P(VALUE obj, enum ruby_value_type t)
493{
494 return RB_TYPE_P(obj, t);
495}
496
497long
498rb_RSTRUCT_LEN(VALUE st)
499{
500 return RSTRUCT_LEN(st);
501}
502
503const struct rb_callinfo *
504rb_get_call_data_ci(const struct rb_call_data *cd)
505{
506 return cd->ci;
507}
508
509bool
510rb_BASIC_OP_UNREDEFINED_P(enum ruby_basic_operators bop, uint32_t klass)
511{
512 return BASIC_OP_UNREDEFINED_P(bop, klass);
513}
514
515VALUE
516rb_RCLASS_ORIGIN(VALUE c)
517{
518 return RCLASS_ORIGIN(c);
519}
520
521// For debug builds
522void
523rb_assert_iseq_handle(VALUE handle)
524{
525 RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_iseq));
526}
527
528// Assert that we have the VM lock. Relevant mostly for multi ractor situations.
529// The GC takes the lock before calling us, and this asserts that it indeed happens.
530void
531rb_assert_holding_vm_lock(void)
532{
533 ASSERT_vm_locking();
534}
535
536int
537rb_IMEMO_TYPE_P(VALUE imemo, enum imemo_type imemo_type)
538{
539 return IMEMO_TYPE_P(imemo, imemo_type);
540}
541
542void
543rb_assert_cme_handle(VALUE handle)
544{
545 RUBY_ASSERT_ALWAYS(!rb_objspace_garbage_object_p(handle));
546 RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_ment));
547}
548
549// YJIT and ZJIT need this function to never allocate and never raise
550VALUE
551rb_yarv_ary_entry_internal(VALUE ary, long offset)
552{
553 return rb_ary_entry_internal(ary, offset);
554}
555
556long
557rb_jit_array_len(VALUE a)
558{
559 return rb_array_len(a);
560}
561
562// Return non-zero when `obj` is an array and its last item is a
563// `ruby2_keywords` hash. The JITs don't support this kind of splat.
564size_t
565rb_jit_ruby2_keywords_splat_p(VALUE obj)
566{
567 if (!RB_TYPE_P(obj, T_ARRAY)) return 0;
568 long len = RARRAY_LEN(obj);
569 if (len == 0) return 0;
570 VALUE last = RARRAY_AREF(obj, len - 1);
571 if (!RB_TYPE_P(last, T_HASH)) return 0;
572 return FL_TEST_RAW(last, RHASH_PASS_AS_KEYWORDS);
573}
574
575void
576rb_set_cfp_pc(struct rb_control_frame_struct *cfp, const VALUE *pc)
577{
578 cfp->pc = pc;
579}
580
581void
582rb_set_cfp_sp(struct rb_control_frame_struct *cfp, VALUE *sp)
583{
584 cfp->sp = sp;
585}
586
587bool
588rb_jit_shape_complex_p(shape_id_t shape_id)
589{
590 return rb_shape_complex_p(shape_id);
591}
592
593bool
594rb_jit_multi_ractor_p(void)
595{
596 return rb_multi_ractor_p();
597}
598
599bool
600rb_jit_constcache_shareable(const struct iseq_inline_constant_cache_entry *ice)
601{
602 return (ice->flags & IMEMO_CONST_CACHE_SHAREABLE) != 0;
603}
604
605// Acquire the VM lock and then signal all other Ruby threads (ractors) to
606// contend for the VM lock, putting them to sleep. ZJIT and YJIT use this to
607// evict threads running inside generated code so among other things, it can
608// safely change memory protection of regions housing generated code.
609void
610rb_jit_vm_lock_then_barrier(unsigned int *recursive_lock_level, const char *file, int line)
611{
612 rb_vm_lock_enter(recursive_lock_level, file, line);
613 rb_vm_barrier();
614}
615
616// Release the VM lock. The lock level must point to the same integer used to
617// acquire the lock.
618void
619rb_jit_vm_unlock(unsigned int *recursive_lock_level, const char *file, int line)
620{
621 rb_vm_lock_leave(recursive_lock_level, file, line);
622}
623
624void *
625rb_iseq_get_jit_payload(const rb_iseq_t *iseq)
626{
627 RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq));
628 if (ISEQ_BODY(iseq)) {
629 return ISEQ_BODY(iseq)->jit_payload;
630 }
631 else {
632 return NULL;
633 }
634}
635
636void
637rb_iseq_set_jit_payload(const rb_iseq_t *iseq, void *payload)
638{
639 RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq));
640 RUBY_ASSERT_ALWAYS(ISEQ_BODY(iseq));
641 RUBY_ASSERT_ALWAYS(NULL == ISEQ_BODY(iseq)->jit_payload);
642 ISEQ_BODY(iseq)->jit_payload = payload;
643}
644
645void
646rb_iseq_clear_jit_payload(const rb_iseq_t *iseq)
647{
648 RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq));
649 RUBY_ASSERT_ALWAYS(ISEQ_BODY(iseq));
650 ISEQ_BODY(iseq)->jit_payload = NULL;
651}
652
653void
654rb_iseq_reset_jit_func(const rb_iseq_t *iseq)
655{
656 RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq));
657 ISEQ_BODY(iseq)->jit_entry = NULL;
658 ISEQ_BODY(iseq)->jit_exception = NULL;
659 // Enable re-compiling this ISEQ. Event when it's invalidated for TracePoint,
660 // we'd like to re-compile ISEQs that haven't been converted to trace_* insns.
661 ISEQ_BODY(iseq)->jit_entry_calls = 0;
662 ISEQ_BODY(iseq)->jit_exception_calls = 0;
663}
664
665// Callback data for rb_jit_for_each_iseq
667 rb_iseq_callback callback;
668 void *data;
669};
670
671// Heap-walking callback for rb_jit_for_each_iseq
672static int
673for_each_iseq_i(void *vstart, void *vend, size_t stride, void *data)
674{
675 const struct iseq_callback_data *callback_data = (struct iseq_callback_data *)data;
676 VALUE v = (VALUE)vstart;
677 for (; v != (VALUE)vend; v += stride) {
678 void *ptr = rb_asan_poisoned_object_p(v);
679 rb_asan_unpoison_object(v, false);
680
681 if (rb_obj_is_iseq(v)) {
682 rb_iseq_t *iseq = (rb_iseq_t *)v;
683 callback_data->callback(iseq, callback_data->data);
684 }
685
686 if (ptr) {
687 rb_asan_poison_object(v);
688 }
689 }
690 return 0;
691}
692
693uint32_t
694rb_jit_get_page_size(void)
695{
696#if defined(_SC_PAGESIZE)
697 long page_size = sysconf(_SC_PAGESIZE);
698 if (page_size <= 0) rb_bug("jit: failed to get page size");
699
700 // 1 GiB limit. x86 CPUs with PDPE1GB can do this and anything larger is unexpected.
701 // Though our design sort of assume we have fine grained control over memory protection
702 // which require small page sizes.
703 if (page_size > 0x40000000l) rb_bug("jit page size too large");
704
705 return (uint32_t)page_size;
706#elif defined(_WIN32)
707 SYSTEM_INFO si;
708 GetSystemInfo(&si);
709 return (uint32_t)si.dwPageSize;
710#else
711#error "JIT supports POSIX and Windows only for now"
712#endif
713}
714
715#if defined(_WIN32) || (defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE))
716# define JIT_PROBE_NEAR_TEXT 1
717
718// Round `ptr` up to the next multiple of `multiple` bytes. Shared with zjit.c.
719uint8_t *
720rb_jit_align_ptr(uint8_t *ptr, uint32_t multiple)
721{
722 // Compute the pointer modulo the given alignment boundary
723 uint32_t rem = ((uint32_t)(uintptr_t)ptr) % multiple;
724
725 // If the pointer is already aligned, stop
726 if (rem == 0)
727 return ptr;
728
729 // Pad the pointer by the necessary amount to align it
730 uint32_t pad = multiple - rem;
731
732 return ptr + pad;
733}
734#endif
735
736// Address space reservation. Memory pages are mapped on an as needed basis.
737// See the Rust mm module for details.
738uint8_t *
739rb_jit_reserve_addr_space(uint32_t mem_size)
740{
741#ifdef JIT_PROBE_NEAR_TEXT
742 uint8_t *const cfunc_sample_addr = (void *)(uintptr_t)&rb_jit_reserve_addr_space;
743 // 64MiB: balancing space probed and time spent probing.
744 const uintptr_t probe_stride = 64 * 1024 * 1024;
745 // Related to the stride. Any successful trial will be within INT32_MAX
746 // range with slack for the binary size.
747 const int max_probe_trials = 30;
748#endif
749#ifndef _WIN32
750 uint8_t *mem_block;
751
752 // On Linux
753 #if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
754 uint32_t const page_size = (uint32_t)sysconf(_SC_PAGESIZE);
755
756 // Probe for addresses close to this function using MAP_FIXED_NOREPLACE
757 // to improve odds of being in range for 32-bit relative call instructions.
758 uint8_t *req_addr = cfunc_sample_addr;
759 for (int i = 0; i < max_probe_trials; i++) {
760 // The address space on x86-64/A64 Linux tends to look like:
761 //
762 // high addr +---------------+
763 // | | [stack] |
764 // | | DSO text |
765 // | | [heap] |
766 // | | main exe text |
767 // v | 0 |
768 // low addr +---------------+
769 //
770 // We always probe downwards from one of the program text areas
771 // to avoid getting in the way of the stack's downwards growth.
772 // If we happen to start from the main text, we also avoid the heap.
773 req_addr -= probe_stride;
774 req_addr = rb_jit_align_ptr(req_addr, page_size);
775
776 mem_block = mmap(
777 req_addr,
778 mem_size,
779 PROT_NONE,
780 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE,
781 -1,
782 0
783 );
784
785 // If we succeeded, stop
786 if (mem_block != MAP_FAILED) {
787 ruby_annotate_mmap(mem_block, mem_size, "Ruby:rb_jit_reserve_addr_space");
788 break;
789 }
790 }
791
792 // On MacOS and other platforms
793 #else
794 // Try to map a chunk of memory as executable
795 mem_block = mmap(
796 (void *)rb_jit_reserve_addr_space,
797 mem_size,
798 PROT_NONE,
799 MAP_PRIVATE | MAP_ANONYMOUS,
800 -1,
801 0
802 );
803 #endif
804
805 // Fallback
806 if (mem_block == MAP_FAILED) {
807 // Try again without the address hint (e.g., valgrind)
808 mem_block = mmap(
809 NULL,
810 mem_size,
811 PROT_NONE,
812 MAP_PRIVATE | MAP_ANONYMOUS,
813 -1,
814 0
815 );
816
817 if (mem_block != MAP_FAILED) {
818 ruby_annotate_mmap(mem_block, mem_size, "Ruby:rb_jit_reserve_addr_space:fallback");
819 }
820 }
821
822 // Check that the memory mapping was successful
823 if (mem_block == MAP_FAILED) {
824 perror("ruby: jit: Fatal mmap failure:");
825 abort();
826 }
827
828 return mem_block;
829#else
830 // Only reserve the address space. rb_jit_mark_writable() commits pages.
831 // Probe below this function as on Linux, aligned to the allocation
832 // granularity that VirtualAlloc() rounds a reservation's address down to.
833 SYSTEM_INFO si;
834 GetSystemInfo(&si);
835 uint8_t *mem_block = NULL;
836 uint8_t *req_addr = cfunc_sample_addr;
837 for (int i = 0; i < max_probe_trials; i++) {
838 req_addr -= probe_stride;
839 req_addr = rb_jit_align_ptr(req_addr, si.dwAllocationGranularity);
840 mem_block = VirtualAlloc(req_addr, mem_size, MEM_RESERVE, PAGE_NOACCESS);
841 if (mem_block != NULL) break;
842 }
843 if (mem_block == NULL) {
844 mem_block = VirtualAlloc(NULL, mem_size, MEM_RESERVE, PAGE_NOACCESS);
845 }
846 if (mem_block == NULL) {
847 errno = rb_w32_map_errno(GetLastError());
848 perror("ruby: jit: Fatal VirtualAlloc failure:");
849 abort();
850 }
851 return mem_block;
852#endif
853}
854
855// Walk all ISEQs in the heap and invoke the callback - shared between YJIT and ZJIT
856void
857rb_jit_for_each_iseq(rb_iseq_callback callback, void *data)
858{
859 struct iseq_callback_data callback_data = { .callback = callback, .data = data };
860 rb_objspace_each_objects(for_each_iseq_i, (void *)&callback_data);
861}
862
863bool
864rb_jit_mark_writable(void *mem_block, uint32_t mem_size)
865{
866#ifdef _WIN32
867 // MEM_COMMIT also re-protects pages that are already committed.
868 return VirtualAlloc(mem_block, mem_size, MEM_COMMIT, PAGE_READWRITE) != NULL;
869#else
870 return mprotect(mem_block, mem_size, PROT_READ | PROT_WRITE) == 0;
871#endif
872}
873
874void
875rb_jit_mark_executable(void *mem_block, uint32_t mem_size)
876{
877 // Do not call mprotect when mem_size is zero. Some platforms may return
878 // an error for it. https://github.com/Shopify/ruby/issues/450
879 if (mem_size == 0) {
880 return;
881 }
882#ifdef _WIN32
883 DWORD old_protect;
884 if (!VirtualProtect(mem_block, mem_size, PAGE_EXECUTE_READ, &old_protect)) {
885 rb_bug("Couldn't make JIT page (%p, %lu bytes) executable, error: %lu",
886 mem_block, (unsigned long)mem_size, GetLastError());
887 }
888#else
889 if (mprotect(mem_block, mem_size, PROT_READ | PROT_EXEC)) {
890 rb_bug("Couldn't make JIT page (%p, %lu bytes) executable, errno: %s",
891 mem_block, (unsigned long)mem_size, strerror(errno));
892 }
893#endif
894}
895
896// Discard the contents of the specified memory block and make it inaccessible.
897bool
898rb_jit_mark_unused(void *mem_block, uint32_t mem_size)
899{
900#ifdef _WIN32
901 // Keep the pages committed, since rb_jit_mark_executable() covers the whole
902 // mapped region and VirtualProtect() fails on decommitted pages.
903 DWORD old_protect;
904 VirtualAlloc(mem_block, mem_size, MEM_RESET, PAGE_NOACCESS);
905 return VirtualProtect(mem_block, mem_size, PAGE_NOACCESS, &old_protect) != 0;
906#else
907 // On Linux, you need to use madvise MADV_DONTNEED to free memory.
908 // We might not need to call this on macOS, but it's not really documented.
909 // We generally prefer to do the same thing on both to ease testing too.
910 madvise(mem_block, mem_size, MADV_DONTNEED);
911
912 // On macOS, mprotect PROT_NONE seems to reduce RSS.
913 // We also call this on Linux to avoid executing unused pages.
914 return mprotect(mem_block, mem_size, PROT_NONE) == 0;
915#endif
916}
917
918// Invalidate icache for arm64.
919// `start` is inclusive and `end` is exclusive.
920void
921rb_jit_icache_invalidate(void *start, void *end)
922{
923 // Clear/invalidate the instruction cache. Compiles to nothing on x86_64
924 // but required on ARM before running freshly written code.
925 // On Darwin it's the same as calling sys_icache_invalidate().
926#ifdef __GNUC__
927 __builtin___clear_cache(start, end);
928#elif defined(__aarch64__)
929#error No instruction cache clear available with this compiler on Aarch64!
930#endif
931}
932
933VALUE
934rb_jit_fix_mod_fix(VALUE recv, VALUE obj)
935{
936 return rb_fix_mod_fix(recv, obj);
937}
938
939VALUE
940rb_jit_fix_div_fix(VALUE recv, VALUE obj)
941{
942 return rb_fix_div_fix(recv, obj);
943}
944
945// YJIT/ZJIT need this function to never allocate and never raise
946VALUE
947rb_yarv_str_eql_internal(VALUE str1, VALUE str2)
948{
949 // We wrap this since it's static inline
950 return rb_str_eql_internal(str1, str2);
951}
952
953VALUE
954rb_jit_str_simple_append(VALUE str1, VALUE str2)
955{
956 return rb_str_cat(str1, RSTRING_PTR(str2), RSTRING_LEN(str2));
957}
958
959void rb_jit_str_concat_codepoint(VALUE str, VALUE codepoint);
960
961attr_index_t
962rb_jit_shape_capacity(shape_id_t shape_id)
963{
964 return RSHAPE_CAPACITY(shape_id);
965}
#define RUBY_ASSERT_ALWAYS(expr,...)
A variant of RUBY_ASSERT that does not interface with RUBY_DEBUG.
Definition assert.h:199
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
static VALUE RB_FL_TEST(VALUE obj, VALUE flags)
Tests if the given flag(s) are set or not.
Definition fl_type.h:433
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
Definition fl_type.h:128
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition globals.h:174
Defines RBIMPL_HAS_BUILTIN.
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition string.c:3681
int len
Length of the buffer.
Definition io.h:8
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
static long rb_array_len(VALUE a)
Queries the length of the array.
Definition rarray.h:254
#define RARRAY_AREF(a, i)
Definition rarray.h:402
static long RSTRUCT_LEN(VALUE st)
Returns the number of struct members.
Definition rstruct.h:82
Defines struct RTypedData.
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
Definition hash.h:54
Ruby's ordinal objects.
Definition robject.h:51
VALUE extended
When an object slot is too small or too complex to store instance variables inline,...
Definition robject.h:73
Ruby's String.
Definition rstring.h:196
"Typed" user data.
Definition rtypeddata.h:397
Definition vm_core.h:258
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
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
ruby_value_type
C-level type of an object.
Definition value_type.h:113