Ruby 4.1.0dev (2026-10-06 revision 186ebd63daaf628ba31d29793ae0bebf23697d39)
jit.c (186ebd63daaf628ba31d29793ae0bebf23697d39)
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#else
707#error "JIT supports POSIX only for now"
708#endif
709}
710
711#if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
712// Round `ptr` up to the next multiple of `multiple` bytes. Shared with zjit.c.
713uint8_t *
714rb_jit_align_ptr(uint8_t *ptr, uint32_t multiple)
715{
716 // Compute the pointer modulo the given alignment boundary
717 uint32_t rem = ((uint32_t)(uintptr_t)ptr) % multiple;
718
719 // If the pointer is already aligned, stop
720 if (rem == 0)
721 return ptr;
722
723 // Pad the pointer by the necessary amount to align it
724 uint32_t pad = multiple - rem;
725
726 return ptr + pad;
727}
728#endif
729
730// Address space reservation. Memory pages are mapped on an as needed basis.
731// See the Rust mm module for details.
732uint8_t *
733rb_jit_reserve_addr_space(uint32_t mem_size)
734{
735#ifndef _WIN32
736 uint8_t *mem_block;
737
738 // On Linux
739 #if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
740 uint32_t const page_size = (uint32_t)sysconf(_SC_PAGESIZE);
741 uint8_t *const cfunc_sample_addr = (void *)(uintptr_t)&rb_jit_reserve_addr_space;
742 // 64MiB: balancing space probed and time spent probing.
743 const uintptr_t probe_stride = 64 * 1024 * 1024;
744 // Related to the stride. Any successful trial will be within INT32_MAX
745 // range with slack for the binary size.
746 const int max_probe_trials = 30;
747
748 // Probe for addresses close to this function using MAP_FIXED_NOREPLACE
749 // to improve odds of being in range for 32-bit relative call instructions.
750 uint8_t *req_addr = cfunc_sample_addr;
751 for (int i = 0; i < max_probe_trials; i++) {
752 // The address space on x86-64/A64 Linux tends to look like:
753 //
754 // high addr +---------------+
755 // | | [stack] |
756 // | | DSO text |
757 // | | [heap] |
758 // | | main exe text |
759 // v | 0 |
760 // low addr +---------------+
761 //
762 // We always probe downwards from one of the program text areas
763 // to avoid getting in the way of the stack's downwards growth.
764 // If we happen to start from the main text, we also avoid the heap.
765 req_addr -= probe_stride;
766 req_addr = rb_jit_align_ptr(req_addr, page_size);
767
768 mem_block = mmap(
769 req_addr,
770 mem_size,
771 PROT_NONE,
772 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE,
773 -1,
774 0
775 );
776
777 // If we succeeded, stop
778 if (mem_block != MAP_FAILED) {
779 ruby_annotate_mmap(mem_block, mem_size, "Ruby:rb_jit_reserve_addr_space");
780 break;
781 }
782 }
783
784 // On MacOS and other platforms
785 #else
786 // Try to map a chunk of memory as executable
787 mem_block = mmap(
788 (void *)rb_jit_reserve_addr_space,
789 mem_size,
790 PROT_NONE,
791 MAP_PRIVATE | MAP_ANONYMOUS,
792 -1,
793 0
794 );
795 #endif
796
797 // Fallback
798 if (mem_block == MAP_FAILED) {
799 // Try again without the address hint (e.g., valgrind)
800 mem_block = mmap(
801 NULL,
802 mem_size,
803 PROT_NONE,
804 MAP_PRIVATE | MAP_ANONYMOUS,
805 -1,
806 0
807 );
808
809 if (mem_block != MAP_FAILED) {
810 ruby_annotate_mmap(mem_block, mem_size, "Ruby:rb_jit_reserve_addr_space:fallback");
811 }
812 }
813
814 // Check that the memory mapping was successful
815 if (mem_block == MAP_FAILED) {
816 perror("ruby: jit: Fatal mmap failure:");
817 abort();
818 }
819
820 return mem_block;
821#else
822 // Windows not supported for now
823 return NULL;
824#endif
825}
826
827// Walk all ISEQs in the heap and invoke the callback - shared between YJIT and ZJIT
828void
829rb_jit_for_each_iseq(rb_iseq_callback callback, void *data)
830{
831 struct iseq_callback_data callback_data = { .callback = callback, .data = data };
832 rb_objspace_each_objects(for_each_iseq_i, (void *)&callback_data);
833}
834
835bool
836rb_jit_mark_writable(void *mem_block, uint32_t mem_size)
837{
838 return mprotect(mem_block, mem_size, PROT_READ | PROT_WRITE) == 0;
839}
840
841void
842rb_jit_mark_executable(void *mem_block, uint32_t mem_size)
843{
844 // Do not call mprotect when mem_size is zero. Some platforms may return
845 // an error for it. https://github.com/Shopify/ruby/issues/450
846 if (mem_size == 0) {
847 return;
848 }
849 if (mprotect(mem_block, mem_size, PROT_READ | PROT_EXEC)) {
850 rb_bug("Couldn't make JIT page (%p, %lu bytes) executable, errno: %s",
851 mem_block, (unsigned long)mem_size, strerror(errno));
852 }
853}
854
855// Free the specified memory block.
856bool
857rb_jit_mark_unused(void *mem_block, uint32_t mem_size)
858{
859 // On Linux, you need to use madvise MADV_DONTNEED to free memory.
860 // We might not need to call this on macOS, but it's not really documented.
861 // We generally prefer to do the same thing on both to ease testing too.
862 madvise(mem_block, mem_size, MADV_DONTNEED);
863
864 // On macOS, mprotect PROT_NONE seems to reduce RSS.
865 // We also call this on Linux to avoid executing unused pages.
866 return mprotect(mem_block, mem_size, PROT_NONE) == 0;
867}
868
869// Invalidate icache for arm64.
870// `start` is inclusive and `end` is exclusive.
871void
872rb_jit_icache_invalidate(void *start, void *end)
873{
874 // Clear/invalidate the instruction cache. Compiles to nothing on x86_64
875 // but required on ARM before running freshly written code.
876 // On Darwin it's the same as calling sys_icache_invalidate().
877#ifdef __GNUC__
878 __builtin___clear_cache(start, end);
879#elif defined(__aarch64__)
880#error No instruction cache clear available with this compiler on Aarch64!
881#endif
882}
883
884VALUE
885rb_jit_fix_mod_fix(VALUE recv, VALUE obj)
886{
887 return rb_fix_mod_fix(recv, obj);
888}
889
890VALUE
891rb_jit_fix_div_fix(VALUE recv, VALUE obj)
892{
893 return rb_fix_div_fix(recv, obj);
894}
895
896// YJIT/ZJIT need this function to never allocate and never raise
897VALUE
898rb_yarv_str_eql_internal(VALUE str1, VALUE str2)
899{
900 // We wrap this since it's static inline
901 return rb_str_eql_internal(str1, str2);
902}
903
904VALUE
905rb_jit_str_simple_append(VALUE str1, VALUE str2)
906{
907 return rb_str_cat(str1, RSTRING_PTR(str2), RSTRING_LEN(str2));
908}
909
910void rb_jit_str_concat_codepoint(VALUE str, VALUE codepoint);
911
912attr_index_t
913rb_jit_shape_capacity(shape_id_t shape_id)
914{
915 return RSHAPE_CAPACITY(shape_id);
916}
#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