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