17#include "internal/compile.h"
18#include "internal/fixnum.h"
19#include "internal/hash.h"
20#include "internal/sanitizers.h"
21#include "internal/gc.h"
22#include "internal/proc.h"
24#include "vm_insnhelper.h"
26#include "probes_helper.h"
29#include "insns_info.inc"
39#if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
42align_ptr(uint8_t *ptr, uint32_t multiple)
45 uint32_t rem = ((uint32_t)(uintptr_t)ptr) % multiple;
52 uint32_t pad = multiple - rem;
61rjit_reserve_addr_space(uint32_t mem_size)
67 #if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
68 uint32_t
const page_size = (uint32_t)sysconf(_SC_PAGESIZE);
69 uint8_t *
const cfunc_sample_addr = (
void *)(uintptr_t)&rjit_reserve_addr_space;
70 uint8_t *
const probe_region_end = cfunc_sample_addr + INT32_MAX;
72 uint8_t *req_addr = align_ptr(cfunc_sample_addr, page_size);
81 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE,
87 if (mem_block != MAP_FAILED) {
88 ruby_annotate_mmap(mem_block, mem_size,
"Ruby:rjit_reserve_addr_space");
93 req_addr += 4 * 1024 * 1024;
94 }
while (req_addr < probe_region_end);
100 (
void *)rjit_reserve_addr_space,
103 MAP_PRIVATE | MAP_ANONYMOUS,
110 if (mem_block == MAP_FAILED) {
116 MAP_PRIVATE | MAP_ANONYMOUS,
121 if (mem_block != MAP_FAILED) {
122 ruby_annotate_mmap(mem_block, mem_size,
"Ruby:rjit_reserve_addr_space:fallback");
127 if (mem_block == MAP_FAILED) {
128 perror(
"ruby: yjit: mmap:");
129 if(
errno == ENOMEM) {
133 rb_bug(
"mmap failed");
146 void *mem_block = (
void *)
NUM2SIZET(rb_mem_block);
147 uint32_t mem_size =
NUM2UINT(rb_mem_size);
148 return RBOOL(mprotect(mem_block, mem_size, PROT_READ | PROT_WRITE) == 0);
154 void *mem_block = (
void *)
NUM2SIZET(rb_mem_block);
155 uint32_t mem_size =
NUM2UINT(rb_mem_size);
156 if (mem_size == 0)
return Qfalse;
158 if (mprotect(mem_block, mem_size, PROT_READ | PROT_EXEC)) {
159 rb_bug(
"Couldn't make JIT page (%p, %lu bytes) executable, errno: %s",
160 mem_block, (
unsigned long)mem_size, strerror(
errno));
169 GetProcPtr(recv, proc);
170 return rb_vm_invoke_proc(ec, proc, argc, argv, kw_splat, block_handler);
182 return rb_str_cat(str1, RSTRING_PTR(str2), RSTRING_LEN(str2));
186rjit_rb_ary_subseq_length(
VALUE ary,
long beg)
189 return rb_ary_subseq(ary, beg,
len);
196 int kw_len = kw_arg->keyword_len;
197 VALUE hash = rb_hash_new_with_size(kw_len);
199 for (
int i = 0; i < kw_len; i++) {
200 VALUE key = kw_arg->keywords[i];
201 VALUE val = *(sp - kw_len + i);
202 rb_hash_aset(hash, key, val);
225 EXEC_EVENT_HOOK(ec,
RUBY_EVENT_C_RETURN, cfp->self, me->def->original_id, me->called_id, me->owner, return_value);
229 RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
233 ec->cfp->sp[0] = return_value;
238rjit_get_proc_ptr(
VALUE procv)
241 GetProcPtr(procv, proc);
248extern VALUE rb_rjit_raw_samples;
249extern VALUE rb_rjit_line_samples;
252rjit_record_exit_stack(
const VALUE *exit_pc)
255 if (!rb_rjit_call_p)
return;
258 int insn = rb_vm_insn_addr2opcode((
void *)*exit_pc);
261 VALUE frames_buffer[BUFF_LEN] = { 0 };
262 int lines_buffer[BUFF_LEN] = { 0 };
272 int samples_length = stack_length + 3;
276 int prev_stack_len_index = (int)
RARRAY_LEN(rb_rjit_raw_samples) - samples_length;
277 VALUE prev_stack_len_obj;
278 if (
RARRAY_LEN(rb_rjit_raw_samples) >= samples_length &&
FIXNUM_P(prev_stack_len_obj =
RARRAY_AREF(rb_rjit_raw_samples, prev_stack_len_index))) {
279 int prev_stack_len =
NUM2INT(prev_stack_len_obj);
280 int idx = stack_length - 1;
281 int prev_frame_idx = 0;
282 bool seen_already =
true;
287 if (prev_stack_len == stack_length) {
289 VALUE current_frame = frames_buffer[idx];
290 VALUE prev_frame =
RARRAY_AREF(rb_rjit_raw_samples, prev_stack_len_index + prev_frame_idx + 1);
294 if (current_frame != prev_frame) {
295 seen_already =
false;
305 int prev_idx = (int)
RARRAY_LEN(rb_rjit_raw_samples) - 1;
307 int new_count = prev_count + 1;
309 rb_ary_store(rb_rjit_raw_samples, prev_idx,
INT2NUM(new_count));
310 rb_ary_store(rb_rjit_line_samples, prev_idx,
INT2NUM(new_count));
316 rb_ary_push(rb_rjit_raw_samples,
INT2NUM(stack_length));
317 rb_ary_push(rb_rjit_line_samples,
INT2NUM(stack_length));
319 int idx = stack_length - 1;
322 VALUE frame = frames_buffer[idx];
323 int line = lines_buffer[idx];
325 rb_ary_push(rb_rjit_raw_samples, frame);
326 rb_ary_push(rb_rjit_line_samples,
INT2NUM(line));
332 rb_ary_push(rb_rjit_raw_samples,
INT2NUM(insn));
336 int line = (int)
RARRAY_LEN(rb_rjit_line_samples) - 1;
337 rb_ary_push(rb_rjit_line_samples,
INT2NUM(line));
341 rb_ary_push(rb_rjit_raw_samples,
INT2NUM(1));
342 rb_ary_push(rb_rjit_line_samples,
INT2NUM(1));
352 if (
RTEST(rb_hash_aref(hash, frame_id))) {
356 VALUE frame_info = rb_hash_new();
369 rb_hash_aset(frame_info,
ID2SYM(rb_intern(
"name")), name);
370 rb_hash_aset(frame_info,
ID2SYM(rb_intern(
"file")), file);
371 rb_hash_aset(frame_info,
ID2SYM(rb_intern(
"samples")),
INT2NUM(0));
372 rb_hash_aset(frame_info,
ID2SYM(rb_intern(
"total_samples")),
INT2NUM(0));
373 rb_hash_aset(frame_info,
ID2SYM(rb_intern(
"edges")), rb_hash_new());
374 rb_hash_aset(frame_info,
ID2SYM(rb_intern(
"lines")), rb_hash_new());
377 rb_hash_aset(frame_info,
ID2SYM(rb_intern(
"line")), line);
380 rb_hash_aset(hash, frame_id, frame_info);
385rjit_exit_traces(
void)
387 int samples_len = (int)
RARRAY_LEN(rb_rjit_raw_samples);
390 VALUE result = rb_hash_new();
391 VALUE raw_samples = rb_ary_new_capa(samples_len);
392 VALUE line_samples = rb_ary_new_capa(samples_len);
393 VALUE frames = rb_hash_new();
398 while (idx < samples_len) {
403 rb_ary_push(raw_samples,
SIZET2NUM(num));
404 rb_ary_push(line_samples,
INT2NUM(line_num));
409 for (
int o = 0; o < num; o++) {
410 rjit_add_frame(frames,
RARRAY_AREF(rb_rjit_raw_samples, idx));
412 rb_ary_push(line_samples,
RARRAY_AREF(rb_rjit_line_samples, idx));
417 rb_ary_push(raw_samples,
RARRAY_AREF(rb_rjit_raw_samples, idx));
418 rb_ary_push(line_samples,
RARRAY_AREF(rb_rjit_line_samples, idx));
422 rb_ary_push(raw_samples,
RARRAY_AREF(rb_rjit_raw_samples, idx));
423 rb_ary_push(line_samples,
RARRAY_AREF(rb_rjit_line_samples, idx));
429 rb_hash_aset(result,
ID2SYM(rb_intern(
"raw")), raw_samples);
430 rb_hash_aset(result,
ID2SYM(rb_intern(
"lines")), line_samples);
431 rb_hash_aset(result,
ID2SYM(rb_intern(
"frames")), frames);
438#define OFFSETOF(ptr, member) RB_SIZE2NUM(((char *)&ptr.member - (char*)&ptr) * 8)
440#define SIZEOF(type) RB_SIZE2NUM(sizeof(type))
441#define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(1))
444static size_t rjit_insn_exits[VM_INSTRUCTION_SIZE] = { 0 };
449#ifdef HAVE_LIBCAPSTONE
450#include <capstone/capstone.h>
457 VALUE result = rb_ary_new();
458#ifdef HAVE_LIBCAPSTONE
461 if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK) {
469 size_t base_addr =
RTEST(test) ? 0 : from_addr;
470 size_t count = cs_disasm(handle, (
const uint8_t *)from_addr, to_addr - from_addr, base_addr, 0, &insns);
471 for (
size_t i = 0; i < count; i++) {
473 rb_ary_push(result, vals);
477 cs_free(insns, count);
487 return RBOOL(rb_rjit_enabled);
491for_each_iseq_i(
void *vstart,
void *vend,
size_t stride,
void *data)
495 for (; v != (
VALUE)vend; v += stride) {
496 void *ptr = rb_asan_poisoned_object_p(v);
497 rb_asan_unpoison_object(v,
false);
499 if (rb_obj_is_iseq(v)) {
502 rb_funcall(block, rb_intern(
"call"), 1, rb_rjit_iseq_new(iseq));
505 asan_poison_object_if(ptr, v);
513 rb_objspace_each_objects(for_each_iseq_i, (
void *)block);
518extern ID rb_get_symbol_id(
VALUE name);
529extern bool rb_simple_iseq_p(
const rb_iseq_t *iseq);
531extern bool rb_vm_ic_hit_p(
IC ic,
const VALUE *reg_ep);
535extern VALUE rb_reg_new_ary(
VALUE ary,
int opt);
542extern void* rb_rjit_entry_stub_hit(
VALUE branch_stub);
543extern void* rb_rjit_branch_stub_hit(
VALUE branch_stub,
int sp_offset,
int target0_p);
544extern uint64_t rb_vm_insns_count;
546#include "rjit_c.rbinc"
#define RUBY_ASSERT_ALWAYS(expr,...)
A variant of RUBY_ASSERT that does not interface with RUBY_DEBUG.
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
int rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
Queries mysterious "frame"s of the given range.
VALUE rb_profile_frame_full_label(VALUE frame)
Identical to rb_profile_frame_label(), except it returns a qualified result.
VALUE rb_profile_frame_absolute_path(VALUE frame)
Identical to rb_profile_frame_path(), except it tries to expand the returning path.
VALUE rb_profile_frame_path(VALUE frame)
Queries the path of the passed backtrace.
VALUE rb_profile_frame_first_lineno(VALUE frame)
Queries the first line of the method of the passed frame pointer.
#define RUBY_EVENT_C_RETURN
Return from a method, written in C.
uint32_t rb_event_flag_t
Represents event(s).
#define rb_str_new2
Old name of rb_str_new_cstr.
#define INT2FIX
Old name of RB_INT2FIX.
#define ID2SYM
Old name of RB_ID2SYM.
#define SIZET2NUM
Old name of RB_SIZE2NUM.
#define NUM2UINT
Old name of RB_NUM2UINT.
#define LONG2NUM
Old name of RB_LONG2NUM.
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
#define INT2NUM
Old name of RB_INT2NUM.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define NUM2SIZET
Old name of RB_NUM2SIZE.
VALUE rb_eRuntimeError
RuntimeError exception.
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
int len
Length of the buffer.
#define RARRAY_LEN
Just another name of rb_array_len.
#define RARRAY_AREF(a, i)
#define errno
Ractor-aware version of errno.
#define RTEST
This is an old name of RB_TEST.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.