1237 Commits

Author SHA1 Message Date
Nobuyoshi Nakada
aad9fa2853
Use RB_VM_LOCKING 2025-05-25 15:22:43 +09:00
Jean Boussier
83d636f2d0 Free shapes last
[Bug #21352]

`rb_objspace_free_objects` may need to check objects shapes
to know how to free them.
2025-05-19 15:06:08 +02:00
Alan Wu
92b218fbc3 YJIT: ZJIT: Allow both JITs in the same build
This commit allows building YJIT and ZJIT simultaneously, a "combo
build". Previously, `./configure --enable-yjit --enable-zjit` failed. At
runtime, though, only one of the two can be enabled at a time.

Add a root Cargo workspace that contains both the yjit and zjit crate.
The common Rust build integration mechanisms are factored out into
defs/jit.mk.

Combo YJIT+ZJIT dev builds are supported; if either JIT uses
`--enable-*=dev`, both of them are built in dev mode.

The combo build requires Cargo, but building one JIT at a time with only
rustc in release build remains supported.
2025-05-15 00:39:03 +09:00
Luke Gruber
1d4822a175 Get ractor message passing working with > 1 thread sending/receiving values in same ractor
Rework ractors so that any ractor action (Ractor.receive, Ractor#send, Ractor.yield, Ractor#take,
Ractor.select) will operate on the thread that called the action. It will put that thread to sleep if
it's a blocking function and it needs to put it to sleep, and the awakening action (Ractor.yield,
Ractor#send) will wake up the blocked thread.

Before this change every blocking ractor action was associated with the ractor struct and its fields.
If a ractor called Ractor.receive, its wait status was wait_receiving, and when another ractor calls
r.send on it, it will look for that status in the ractor struct fields and wake it up. The problem was that
what if 2 threads call blocking ractor actions in the same ractor. Imagine if 1 thread has called Ractor.receive
and another r.take. Then, when a different ractor calls r.send on it, it doesn't know which ruby thread is associated
to which ractor action, so what ruby thread should it schedule? This change moves some fields onto the ruby thread
itself so that ruby threads are the ones that have ractor blocking statuses, and threads are then specifically scheduled
when unblocked.

Fixes [#17624]
Fixes [#21037]
2025-05-13 13:23:57 -07:00
Samuel Williams
425fa0aeb5
Make waiting_fd behaviour per-IO. (#13127)
- `rb_thread_fd_close` is deprecated and now a no-op.
- IO operations (including close) no longer take a vm-wide lock.
2025-05-13 19:02:03 +09:00
Satoshi Tagomori
382645d440 namespace on read 2025-05-11 23:32:50 +09:00
Jean Boussier
7116b0a7f1 Extract rb_shape_free_all 2025-05-09 10:22:51 +02:00
Jean Boussier
0ea210d1ea Rename ivptr -> fields, next_iv_index -> next_field_index
Ivars will longer be the only thing stored inline
via shapes, so keeping the `iv_index` and `ivptr` names
would be confusing.

Instance variables won't be the only thing stored inline
via shapes, so keeping the `ivptr` name would be confusing.

`field` encompass anything that can be stored in a VALUE array.

Similarly, `gen_ivtbl` becomes `gen_fields_tbl`.
2025-05-08 07:58:05 +02:00
Jean Boussier
3ec7bfff2e Use a set_table for rb_vm_struct.unused_block_warning_table
Now that we have a hash-set implementation we can use that
instead of a hash-table with a static value.
2025-04-27 11:59:28 +02:00
刘皓
45e814d116 Fix jump buffer leak in WASI builds 2025-04-27 15:47:30 +09:00
Takashi Kokubun
8b72e07359 Disable ZJIT profiling at call-threshold (https://github.com/Shopify/zjit/pull/99)
* Disable ZJIT profiling at call-threshold

* Stop referencing ZJIT instructions in codegen
2025-04-18 21:53:01 +09:00
Takashi Kokubun
2915806820 Add --zjit-num-profiles option (https://github.com/Shopify/zjit/pull/98)
* Add --zjit-profile-interval option

* Fix min to max

* Avoid rewriting instructions for --zjit-call-threshold=1

* Rename the option to --zjit-num-profiles
2025-04-18 21:53:01 +09:00
Takashi Kokubun
bb46bb781c Stub Init_builtin_zjit for --disable-zjit 2025-04-18 21:53:00 +09:00
Takashi Kokubun
14253e7d12 Implement Insn::Param using the SP register (https://github.com/Shopify/zjit/pull/39) 2025-04-18 21:52:59 +09:00
Takashi Kokubun
22c73f1ccb Implement FixnumAdd and stub PatchPoint/GuardType (https://github.com/Shopify/zjit/pull/30)
* Implement FixnumAdd and stub PatchPoint/GuardType

Co-authored-by: Max Bernstein <max.bernstein@shopify.com>
Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>

* Clone Target for arm64

* Use $create instead of use create

Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>

* Fix misindentation from suggested changes

* Drop an unneeded variable for mut

* Load operand into a register only if necessary

---------

Co-authored-by: Max Bernstein <max.bernstein@shopify.com>
Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
2025-04-18 21:52:59 +09:00
Takashi Kokubun
0a543daf15 Add zjit_* instructions to profile the interpreter (https://github.com/Shopify/zjit/pull/16)
* Add zjit_* instructions to profile the interpreter

* Rename FixnumPlus to FixnumAdd

* Update a comment about Invalidate

* Rename Guard to GuardType

* Rename Invalidate to PatchPoint

* Drop unneeded debug!()

* Plan on profiling the types

* Use the output of GuardType as type refined outputs
2025-04-18 21:52:59 +09:00
Takashi Kokubun
53bee25068 Implement --zjit-call-threshold
As a preparation for introducing a profiling layer, we need to be able
to raise the threshold to run a few cycles for profiling.
2025-04-18 21:52:58 +09:00
Takashi Kokubun
06d875b979 Backport the latest jit_compile() 2025-04-18 21:52:58 +09:00
Alan Wu
1d95139bf6 miniruby --zjit -e nil runs through iseq_to_ssa 2025-04-18 21:52:56 +09:00
Takashi Kokubun
0bb709718b Hook ZJIT compilation 2025-04-18 21:52:56 +09:00
John Hawthorn
57b6a7503f Lock-free hash set for fstrings [Feature #21268]
This implements a hash set which is wait-free for lookup and lock-free
for insert (unless resizing) to use for fstring de-duplication.

As highlighted in https://bugs.ruby-lang.org/issues/19288, heavy use of
fstrings (frozen interned strings) can significantly reduce the
parallelism of Ractors.

I tried a few other approaches first: using an RWLock, striping a series
of RWlocks (partitioning the hash N-ways to reduce lock contention), and
putting a cache in front of it. All of these improved the situation, but
were unsatisfying as all still required locks for writes (and granular
locks are awkward, since we run the risk of needing to reach a vm
barrier) and this table is somewhat write-heavy.

My main reference for this was Cliff Click's talk on a lock free
hash-table for java https://www.youtube.com/watch?v=HJ-719EGIts. It
turns out this lock-free hash set is made easier to implement by a few
properties:

 * We only need a hash set rather than a hash table (we only need keys,
   not values), and so the full entry can be written as a single VALUE
 * As a set we only need lookup/insert/delete, no update
 * Delete is only run inside GC so does not need to be atomic (It could
   be made concurrent)
 * I use rb_vm_barrier for the (rare) table rebuilds (It could be made
   concurrent) We VM lock (but don't require other threads to stop) for
   table rebuilds, as those are rare
 * The conservative garbage collector makes deferred replication easy,
   using a T_DATA object

Another benefits of having a table specific to fstrings is that we
compare by value on lookup/insert, but by identity on delete, as we only
want to remove the exact string which is being freed. This is faster and
provides a second way to avoid the race condition in
https://bugs.ruby-lang.org/issues/21172.

This is a pretty standard open-addressing hash table with quadratic
probing. Similar to our existing st_table or id_table. Deletes (which
happen on GC) replace existing keys with a tombstone, which is the only
type of update which can occur. Tombstones are only cleared out on
resize.

Unlike st_table, the VALUEs are stored in the hash table itself
(st_table's bins) rather than as a compact index. This avoids an extra
pointer dereference and is possible because we don't need to preserve
insertion order. The table targets a load factor of 2 (it is enlarged
once it is half full).
2025-04-18 13:03:54 +09:00
Aaron Patterson
3628e9e30d Remove unused field on Thread struct
It looks like stat_insn_usage was introduced with YARV, but as far as I
can tell the field has never been used.  I think we should remove the
field since we don't use it.
2025-04-11 10:28:26 -07:00
lukeg
d80f3a287c Ractor.make_shareable(proc_obj) makes inner structure shareable
Proc objects are now traversed like other objects when making them
shareable.

Fixes [Bug #19372]
Fixes [Bug #19374]
2025-03-26 16:05:02 -07:00
Alan Wu
08b3a45bc9 Push a real iseq in rb_vm_push_frame_fname()
Previously, vm_make_env_each() (used during proc
creation and for the debug inspector C API) picked up the
non-GC-allocated iseq that rb_vm_push_frame_fname() creates,
which led to a SEGV when the GC tried to mark the non GC object.

Put a real iseq imemo instead. Speed should be about the same since
the old code also did a imemo allocation and a malloc allocation.

Real iseq allows ironing out the special-casing of dummy frames in
rb_execution_context_mark() and rb_execution_context_update(). A check
is added to RubyVM::ISeq#eval, though, to stop attempts to run dummy
iseqs.

[Bug #21180]

Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
2025-03-12 15:00:26 -04:00
Yusuke Endoh
993fd96ce6 reject numbered parameters from Binding#local_variables
Also, Binding#local_variable_get and #local_variable_set rejects an
access to numbered parameters.

[Bug #20965] [Bug #21049]
2025-02-18 16:23:24 +09:00
Takashi Kokubun
c1ce3d719d Streamline YJIT checks on jit_compile() 2025-02-14 10:40:10 -08:00
Nobuyoshi Nakada
4a67ef09cc
[Feature #21116] Extract RJIT as a third-party gem 2025-02-13 18:01:03 +09:00
Aaron Patterson
d680a13ad0 Always return jit_entry even if NULL
We can just always return the jit_entry since it will be initialized to
NULL.  There is no reason to specifically return NULL if yjit / rjit are
disabled
2025-02-10 15:50:23 -05:00
Peter Zhu
5032791330 Fix conversion of RubyVM::FrozenCore to T_ICLASS
We shouldn't directly set the flags of an object because there could be
other flags set that would be erased. Instead, we can unset T_MASK and
set T_ICLASS isntead.
2025-01-30 10:10:48 -05:00
Peter Zhu
98b36f6f36 Use rb_gc_vm_weak_table_foreach for reference updating
We can use rb_gc_vm_weak_table_foreach for reference updating of weak tables
in the default GC.
2025-01-27 10:28:36 -05:00
Nobuyoshi Nakada
f7059af50a
Use no-inline version rb_current_ec on Arm64
The TLS across .so issue seems related to Arm64, but not Darwin.
2025-01-17 22:48:10 +09:00
Peter Zhu
707c6420b1 Don't reference update frames with VM_FRAME_MAGIC_DUMMY
Frames with VM_FRAME_MAGIC_DUMMY pushed by rb_vm_push_frame_fname have
allocated iseq, so we should not reference update it.
2024-12-17 11:03:38 -05:00
Peter Zhu
92dd9734a9 Fix use-after-free in ep in Proc#dup for ifunc procs
[Bug #20950]

ifunc proc has the ep allocated in the cfunc_proc_t which is the data of
the TypedData object. If an ifunc proc is duplicated, the ep points to
the ep of the source object. If the source object is freed, then the ep
of the duplicated object now points to a freed memory region. If we try
to use the ep we could crash.

For example, the following script crashes:

    p = { a: 1 }.to_proc
    100.times do
      p = p.dup
      GC.start
      p.call
    rescue ArgumentError
    end

This commit changes ifunc proc to also duplicate the ep when it is duplicated.
2024-12-13 10:10:03 -05:00
Randy Stauner
b021f6f8a7
Use symbol.h in vm.c to get macro for faster ID to sym (#12272)
The macro provided by symbol.h uses STATIC_ID2SYM
when it can which speeds up methods that declare keyword args.

Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>
Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
2024-12-05 17:51:32 -05:00
Yusuke Endoh
59f7a5d336 Remove meaningless NULL checks
In this context, `th` must not be NULL
2024-12-04 12:15:23 +09:00
Randy Stauner
1dd40ec18a
Optimize instructions when creating an array just to call include? (#12123)
* Add opt_duparray_send insn to skip the allocation on `#include?`

If the method isn't going to modify the array we don't need to copy it.
This avoids the allocation / array copy for things like `[:a, :b].include?(x)`.

This adds a BOP for include? and tracks redefinition for it on Array.

Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>

* YJIT: Implement opt_duparray_send include_p

Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>

* Update opt_newarray_send to support simple forms of include?(arg)

Similar to opt_duparray_send but for non-static arrays.

* YJIT: Implement opt_newarray_send include_p

---------

Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>
2024-11-26 14:31:08 -05:00
Nobuyoshi Nakada
c129e01125
Make rb_ec_set_vm_stack conformant to the C++11 requirement
Https://learn.microsoft.com/en-us/cpp/build/reference/zc-inline-remove-unreferenced-comdat?view=msvc-140
> If `/Zc:inline` is specified, the compiler enforces the C++11
> requirement that all functions declared inline must have a definition
> available in the same translation unit if they're used.
2024-11-17 19:56:02 +09:00
Peter Zhu
1d1c80e644
Fix false-positive memory leak using Valgrind in YJIT (#12057)
When we run with RUBY_FREE_AT_EXIT, there's a false-positive memory leak
reported in YJIT because the METHOD_CODEGEN_TABLE is never freed. This
commit adds rb_yjit_free_at_exit that is called at shutdown when
RUBY_FREE_AT_EXIT is set.

Reported memory leak:

    ==699816== 1,104 bytes in 1 blocks are possibly lost in loss record 1 of 1
    ==699816==    at 0x484680F: malloc (vg_replace_malloc.c:446)
    ==699816==    by 0x155B3E: UnknownInlinedFun (unix.rs:14)
    ==699816==    by 0x155B3E: UnknownInlinedFun (stats.rs:36)
    ==699816==    by 0x155B3E: UnknownInlinedFun (stats.rs:27)
    ==699816==    by 0x155B3E: alloc (alloc.rs:98)
    ==699816==    by 0x155B3E: alloc_impl (alloc.rs:181)
    ==699816==    by 0x155B3E: allocate (alloc.rs:241)
    ==699816==    by 0x155B3E: do_alloc<alloc::alloc::Global> (alloc.rs:15)
    ==699816==    by 0x155B3E: new_uninitialized<alloc::alloc::Global> (mod.rs:1750)
    ==699816==    by 0x155B3E: fallible_with_capacity<alloc::alloc::Global> (mod.rs:1788)
    ==699816==    by 0x155B3E: prepare_resize<alloc::alloc::Global> (mod.rs:2864)
    ==699816==    by 0x155B3E: resize_inner<alloc::alloc::Global> (mod.rs:3060)
    ==699816==    by 0x155B3E: reserve_rehash_inner<alloc::alloc::Global> (mod.rs:2950)
    ==699816==    by 0x155B3E: hashbrown::raw::RawTable<T,A>::reserve_rehash (mod.rs:1231)
    ==699816==    by 0x5BC39F: UnknownInlinedFun (mod.rs:1179)
    ==699816==    by 0x5BC39F: find_or_find_insert_slot<(usize, fn(&mut yjit::codegen::JITState, &mut yjit::backend::ir::Assembler, *const yjit::cruby::autogened::rb_callinfo, *const yjit::cruby::autogened::rb_callable_method_entry_struct, core::option::Option<yjit::codegen::BlockHandler>, i32, core::option::Option<yjit::cruby::VALUE>) -> bool), alloc::alloc::Global, hashbrown::map::equivalent_key::{closure_env#0}<usize, usize, fn(&mut yjit::codegen::JITState, &mut yjit::backend::ir::Assembler, *const yjit::cruby::autogened::rb_callinfo, *const yjit::cruby::autogened::rb_callable_method_entry_struct, core::option::Option<yjit::codegen::BlockHandler>, i32, core::option::Option<yjit::cruby::VALUE>) -> bool>, hashbrown::map::make_hasher::{closure_env#0}<usize, fn(&mut yjit::codegen::JITState, &mut yjit::backend::ir::Assembler, *const yjit::cruby::autogened::rb_callinfo, *const yjit::cruby::autogened::rb_callable_method_entry_struct, core::option::Option<yjit::codegen::BlockHandler>, i32, core::option::Option<yjit::cruby::VALUE>) -> bool, std:#️⃣:random::RandomState>> (mod.rs:1413)
    ==699816==    by 0x5BC39F: hashbrown::map::HashMap<K,V,S,A>::insert (map.rs:1754)
    ==699816==    by 0x57C5C6: insert<usize, fn(&mut yjit::codegen::JITState, &mut yjit::backend::ir::Assembler, *const yjit::cruby::autogened::rb_callinfo, *const yjit::cruby::autogened::rb_callable_method_entry_struct, core::option::Option<yjit::codegen::BlockHandler>, i32, core::option::Option<yjit::cruby::VALUE>) -> bool, std:#️⃣:random::RandomState> (map.rs:1104)
    ==699816==    by 0x57C5C6: yjit::codegen::reg_method_codegen (codegen.rs:10521)
    ==699816==    by 0x57C295: yjit::codegen::yjit_reg_method_codegen_fns (codegen.rs:10464)
    ==699816==    by 0x5C6B07: rb_yjit_init (yjit.rs:40)
    ==699816==    by 0x393723: ruby_opt_init (ruby.c:1820)
    ==699816==    by 0x393723: ruby_opt_init (ruby.c:1767)
    ==699816==    by 0x3957D4: prism_script (ruby.c:2215)
    ==699816==    by 0x3957D4: process_options (ruby.c:2538)
    ==699816==    by 0x396065: ruby_process_options (ruby.c:3166)
    ==699816==    by 0x236E56: ruby_options (eval.c:117)
    ==699816==    by 0x15BAED: rb_main (main.c:43)
    ==699816==    by 0x15BAED: main (main.c:62)

After this patch, there are no more memory leaks reported when running
RUBY_FREE_AT_EXIT with Valgrind on an empty Ruby script:

    $ RUBY_FREE_AT_EXIT=1 valgrind --leak-check=full ruby -e ""
    ...
    ==700357== HEAP SUMMARY:
    ==700357==     in use at exit: 0 bytes in 0 blocks
    ==700357==   total heap usage: 36,559 allocs, 36,559 frees, 6,064,783 bytes allocated
    ==700357==
    ==700357== All heap blocks were freed -- no leaks are possible
2024-11-11 20:45:11 +00:00
Koichi Sasada
aa63699d10 support require in non-main Ractors
Many libraries should be loaded on the main ractor because of
setting constants with unshareable objects and so on.

This patch allows to call `requore` on non-main Ractors by
asking the main ractor to call `require` on it. The calling ractor
waits for the result of `require` from the main ractor.

If the `require` call failed with some reasons, an exception
objects will be deliverred from the main ractor to the calling ractor
if it is copy-able.

Same on `require_relative` and `require` by `autoload`.

Now `Ractor.new{pp obj}` works well (the first call of `pp` requires
`pp` library implicitly).

[Feature #20627]
2024-11-08 18:02:46 +09:00
Koichi Sasada
c8297c3eed interrupt_exec
introduce
- rb_threadptr_interrupt_exec
- rb_ractor_interrupt_exec

to intercept the thread/ractor execution.
2024-11-08 18:02:46 +09:00
Koichi Sasada
ab7ab9e450 Warning[:strict_unused_block]
to show unused block warning strictly.

```ruby
class C
  def f = nil
end

class D
  def f = yield
end

[C.new, D.new].each{|obj| obj.f{}}
```

In this case, `D#f` accepts a block. However `C#f` doesn't
accept a block. There are some cases passing a block with
`obj.f{}` where `obj` is `C` or `D`. To avoid warnings on
such cases, "unused block warning" will be warned only if
there is not same name which accepts a block.
On the above example, `C.new.f{}` doesn't show any warnings
because there is a same name `D#f` which accepts a block.

We call this default behavior as "relax mode".

`strict_unused_block` new warning category changes from
"relax mode" to "strict mode", we don't check same name
methods and `C.new.f{}` will be warned.

[Feature #15554]
2024-11-06 11:06:18 +09:00
Takashi Kokubun
478e0fc710
YJIT: Replace Array#each only when YJIT is enabled (#11955)
* YJIT: Replace Array#each only when YJIT is enabled

* Add comments about BUILTIN_ATTR_C_TRACE

* Make Ruby Array#each available with --yjit as well

* Fix all paths that expect a C location

* Use method_basic_definition_p to detect patches

* Copy a comment about C_TRACE flag to compilers

* Rephrase a comment about add_yjit_hook

* Give METHOD_ENTRY_BASIC flag to Array#each

* Add --yjit-c-builtin option

* Allow inconsistent source_location in test-spec

* Refactor a check of BUILTIN_ATTR_C_TRACE

* Set METHOD_ENTRY_BASIC without touching vm->running
2024-11-04 11:14:28 -05:00
Peter Zhu
645a0c9ea7 Remove vm_assert_env 2024-10-31 13:52:24 -04:00
Nobuyoshi Nakada
abfefd8e0c
Define VM_ASSERT_TYPE macros 2024-10-31 22:12:16 +09:00
Takashi Kokubun
9838c443c4 Make builtin init ifdefs consistent 2024-10-25 17:46:49 -07:00
Nobuyoshi Nakada
3e1021b144 Make default parser enum and define getter/setter 2024-10-02 20:43:40 +09:00
Étienne Barrié
bf9879791a Optimized instruction for Hash#freeze
If a Hash which is empty or only using literals is frozen, we detect
this as a peephole optimization and change the instructions to be
`opt_hash_freeze`.

[Feature #20684]

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-09-05 12:46:02 +02:00
Étienne Barrié
a99707cd9c Optimized instruction for Array#freeze
If an Array which is empty or only using literals is frozen, we detect
this as a peephole optimization and change the instructions to be
`opt_ary_freeze`.

[Feature #20684]

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-09-05 12:46:02 +02:00
Peter Zhu
3c63a01295 Move responsibility of heap walking into Ruby
This commit removes the need for the GC implementation to implement heap
walking and instead Ruby will implement it.
2024-09-03 10:05:38 -04:00
Alan Wu
f2ac013009
Add RB_DEFAULT_PARSER preprocessor macro
This way there is one place to change for switching the default.
This also allows for building the same commit with different cppflags.
2024-08-27 23:15:37 +00:00