summaryrefslogtreecommitdiff
path: root/vm.c
AgeCommit message (Collapse)Author
2025-10-09Backport WASI setjmp handler memory leak fixes to Ruby 3.4 (#14788)刘皓
[Bug #21626]
2025-06-20thread_cleanup: set CFP to NULL before clearing ec's stackLuke Gruber
We clear the CFP first so that if a sampling profiler interrupts the current thread during `rb_ec_set_vm_stack`, `thread_profile_frames` returns early instead of trying to walk the stack that's no longer set on the ec. The early return in `thread_profile_frames` was introduced at eab7f4623fb. Fixes [Bug #21441]
2025-03-14Push a real iseq in rb_vm_push_frame_fname()Alan Wu
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-02-13merge revision(s) f7059af50a31a4d27a04ace0beadb60616f3f971: [Backport #21046]Takashi Kokubun
Use no-inline version `rb_current_ec` on Arm64 The TLS across .so issue seems related to Arm64, but not Darwin.
2024-12-17Don't reference update frames with VM_FRAME_MAGIC_DUMMYPeter Zhu
Frames with VM_FRAME_MAGIC_DUMMY pushed by rb_vm_push_frame_fname have allocated iseq, so we should not reference update it. Notes: Merged: https://github.com/ruby/ruby/pull/12371
2024-12-13Fix use-after-free in ep in Proc#dup for ifunc procsPeter Zhu
[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. Notes: Merged: https://github.com/ruby/ruby/pull/12319
2024-12-05Use symbol.h in vm.c to get macro for faster ID to sym (#12272)Randy Stauner
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> Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
2024-12-04Remove meaningless NULL checksYusuke Endoh
In this context, `th` must not be NULL Notes: Merged: https://github.com/ruby/ruby/pull/12253
2024-11-26Optimize instructions when creating an array just to call `include?` (#12123)Randy Stauner
* 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> Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
2024-11-17Make `rb_ec_set_vm_stack` conformant to the C++11 requirementNobuyoshi Nakada
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. Notes: Merged: https://github.com/ruby/ruby/pull/12107
2024-11-11Fix false-positive memory leak using Valgrind in YJIT (#12057)Peter Zhu
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::hash::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::hash::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 Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
2024-11-08support `require` in non-main RactorsKoichi Sasada
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] Notes: Merged: https://github.com/ruby/ruby/pull/11142
2024-11-08`interrupt_exec`Koichi Sasada
introduce - rb_threadptr_interrupt_exec - rb_ractor_interrupt_exec to intercept the thread/ractor execution. Notes: Merged: https://github.com/ruby/ruby/pull/11142
2024-11-06`Warning[:strict_unused_block]`Koichi Sasada
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] Notes: Merged: https://github.com/ruby/ruby/pull/12005
2024-11-04YJIT: Replace Array#each only when YJIT is enabled (#11955)Takashi Kokubun
* 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 Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
2024-10-31Remove vm_assert_envPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11970
2024-10-31Define `VM_ASSERT_TYPE` macrosNobuyoshi Nakada
2024-10-25Make builtin init ifdefs consistentTakashi Kokubun
2024-10-02Make default parser enum and define getter/setterNobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/11761
2024-09-05Optimized instruction for Hash#freezeÉtienne Barrié
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> Notes: Merged: https://github.com/ruby/ruby/pull/11406
2024-09-05Optimized instruction for Array#freezeÉtienne Barrié
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> Notes: Merged: https://github.com/ruby/ruby/pull/11406
2024-09-03Move responsibility of heap walking into RubyPeter Zhu
This commit removes the need for the GC implementation to implement heap walking and instead Ruby will implement it. Notes: Merged: https://github.com/ruby/ruby/pull/11511
2024-08-27Add RB_DEFAULT_PARSER preprocessor macroAlan Wu
This way there is one place to change for switching the default. This also allows for building the same commit with different cppflags. Notes: Merged: https://github.com/ruby/ruby/pull/11473 Merged-By: XrXr
2024-08-07Make rb_vm_invoke_bmethod() staticAlan Wu
Notes: Merged: https://github.com/ruby/ruby/pull/11331
2024-08-07Tune codegen for rb_yield() calls landing in ISeqsYour Name
Unlike in older revisions in the year, GCC 11 isn't inlining the call to vm_push_frame() inside invoke_iseq_block_from_c() anymore. We do want it to be inlined since rb_yield() speed is fairly important. Logs from -fopt-info-optimized-inline reveal that GCC was blowing its code size budget inlining invoke_block_from_c_bh() into its various callers, leaving suboptimal code for its body. Take away some uses of the `inline` keyword and merge a common tail call to vm_exec() for overall better code. This tweak gives about 18% on a micro benchmark and 1% on the chunky-png benchmark from yjit-bench. I tested on a Skylake server. ``` $ cat c-to-ruby-call.yml benchmark: - 0.upto(10_000_000) {} $ benchmark-driver --chruby '+patch;master' c-to-ruby-call.yml Warming up -------------------------------------- 0.upto(10_000_000) {} 2.299 i/s - 3.000 times in 1.304689s (434.90ms/i) Calculating ------------------------------------- +patch master 0.upto(10_000_000) {} 2.299 1.943 i/s - 6.000 times in 2.609393s 3.088353s Comparison: 0.upto(10_000_000) {} +patch: 2.3 i/s master: 1.9 i/s - 1.18x slower $ ruby run_benchmarks.rb --chruby 'master;+patch' chunky-png <snip> ---------- ----------- ---------- ----------- ---------- -------------- ------------- bench master (ms) stddev (%) +patch (ms) stddev (%) +patch 1st itr master/+patch chunky-png 1156.1 0.1 1142.2 0.2 1.01 1.01 ---------- ----------- ---------- ----------- ---------- -------------- ------------- ``` Notes: Merged: https://github.com/ruby/ruby/pull/11321
2024-07-30Revert 28a1c4f33e3349a98c04b8e068d9c674eb936064Yusuke Endoh
28a1c4f33e3349a98c04b8e068d9c674eb936064 seems to call an improper ensure clause. [Bug #20655] Than fixing it properly, I bet it would be much better to simply revert that commit. It reduces the unneeded complexity. Jumping into a block called by a C function like Hash#each with callcc is user's fault. It does not need serious support. Notes: Merged: https://github.com/ruby/ruby/pull/11254
2024-06-18Optimized forwarding callers and calleesAaron Patterson
This patch optimizes forwarding callers and callees. It only optimizes methods that only take `...` as their parameter, and then pass `...` to other calls. Calls it optimizes look like this: ```ruby def bar(a) = a def foo(...) = bar(...) # optimized foo(123) ``` ```ruby def bar(a) = a def foo(...) = bar(1, 2, ...) # optimized foo(123) ``` ```ruby def bar(*a) = a def foo(...) list = [1, 2] bar(*list, ...) # optimized end foo(123) ``` All variants of the above but using `super` are also optimized, including a bare super like this: ```ruby def foo(...) super end ``` This patch eliminates intermediate allocations made when calling methods that accept `...`. We can observe allocation elimination like this: ```ruby def m x = GC.stat(:total_allocated_objects) yield GC.stat(:total_allocated_objects) - x end def bar(a) = a def foo(...) = bar(...) def test m { foo(123) } end test p test # allocates 1 object on master, but 0 objects with this patch ``` ```ruby def bar(a, b:) = a + b def foo(...) = bar(...) def test m { foo(1, b: 2) } end test p test # allocates 2 objects on master, but 0 objects with this patch ``` How does it work? ----------------- This patch works by using a dynamic stack size when passing forwarded parameters to callees. The caller's info object (known as the "CI") contains the stack size of the parameters, so we pass the CI object itself as a parameter to the callee. When forwarding parameters, the forwarding ISeq uses the caller's CI to determine how much stack to copy, then copies the caller's stack before calling the callee. The CI at the forwarded call site is adjusted using information from the caller's CI. I think this description is kind of confusing, so let's walk through an example with code. ```ruby def delegatee(a, b) = a + b def delegator(...) delegatee(...) # CI2 (FORWARDING) end def caller delegator(1, 2) # CI1 (argc: 2) end ``` Before we call the delegator method, the stack looks like this: ``` Executing Line | Code | Stack ---------------+---------------------------------------+-------- 1| def delegatee(a, b) = a + b | self 2| | 1 3| def delegator(...) | 2 4| # | 5| delegatee(...) # CI2 (FORWARDING) | 6| end | 7| | 8| def caller | -> 9| delegator(1, 2) # CI1 (argc: 2) | 10| end | ``` The ISeq for `delegator` is tagged as "forwardable", so when `caller` calls in to `delegator`, it writes `CI1` on to the stack as a local variable for the `delegator` method. The `delegator` method has a special local called `...` that holds the caller's CI object. Here is the ISeq disasm fo `delegator`: ``` == disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)> local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) [ 1] "..."@0 0000 putself ( 1)[LiCa] 0001 getlocal_WC_0 "..."@0 0003 send <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil 0006 leave [Re] ``` The local called `...` will contain the caller's CI: CI1. Here is the stack when we enter `delegator`: ``` Executing Line | Code | Stack ---------------+---------------------------------------+-------- 1| def delegatee(a, b) = a + b | self 2| | 1 3| def delegator(...) | 2 -> 4| # | CI1 (argc: 2) 5| delegatee(...) # CI2 (FORWARDING) | cref_or_me 6| end | specval 7| | type 8| def caller | 9| delegator(1, 2) # CI1 (argc: 2) | 10| end | ``` The CI at `delegatee` on line 5 is tagged as "FORWARDING", so it knows to memcopy the caller's stack before calling `delegatee`. In this case, it will memcopy self, 1, and 2 to the stack before calling `delegatee`. It knows how much memory to copy from the caller because `CI1` contains stack size information (argc: 2). Before executing the `send` instruction, we push `...` on the stack. The `send` instruction pops `...`, and because it is tagged with `FORWARDING`, it knows to memcopy (using the information in the CI it just popped): ``` == disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)> local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) [ 1] "..."@0 0000 putself ( 1)[LiCa] 0001 getlocal_WC_0 "..."@0 0003 send <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil 0006 leave [Re] ``` Instruction 001 puts the caller's CI on the stack. `send` is tagged with FORWARDING, so it reads the CI and _copies_ the callers stack to this stack: ``` Executing Line | Code | Stack ---------------+---------------------------------------+-------- 1| def delegatee(a, b) = a + b | self 2| | 1 3| def delegator(...) | 2 4| # | CI1 (argc: 2) -> 5| delegatee(...) # CI2 (FORWARDING) | cref_or_me 6| end | specval 7| | type 8| def caller | self 9| delegator(1, 2) # CI1 (argc: 2) | 1 10| end | 2 ``` The "FORWARDING" call site combines information from CI1 with CI2 in order to support passing other values in addition to the `...` value, as well as perfectly forward splat args, kwargs, etc. Since we're able to copy the stack from `caller` in to `delegator`'s stack, we can avoid allocating objects. I want to do this to eliminate object allocations for delegate methods. My long term goal is to implement `Class#new` in Ruby and it uses `...`. I was able to implement `Class#new` in Ruby [here](https://github.com/ruby/ruby/pull/9289). If we adopt the technique in this patch, then we can optimize allocating objects that take keyword parameters for `initialize`. For example, this code will allocate 2 objects: one for `SomeObject`, and one for the kwargs: ```ruby SomeObject.new(foo: 1) ``` If we combine this technique, plus implement `Class#new` in Ruby, then we can reduce allocations for this common operation. Co-Authored-By: John Hawthorn <john@hawthorn.email> Co-Authored-By: Alan Wu <XrXr@users.noreply.github.com>
2024-05-23Introduce a specialize instruction for Array#packNobuyoshi Nakada
Instructions for this code: ```ruby # frozen_string_literal: true [a].pack("C") ``` Before this commit: ``` == disasm: #<ISeq:<main>@test.rb:1 (1,0)-(3,13)> 0000 putself ( 3)[Li] 0001 opt_send_without_block <calldata!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0003 newarray 1 0005 putobject "C" 0007 opt_send_without_block <calldata!mid:pack, argc:1, ARGS_SIMPLE> 0009 leave ``` After this commit: ``` == disasm: #<ISeq:<main>@test.rb:1 (1,0)-(3,13)> 0000 putself ( 3)[Li] 0001 opt_send_without_block <calldata!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0003 putobject "C" 0005 opt_newarray_send 2, :pack 0008 leave ``` Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
2024-05-03Rename `vast` to `ast_value`yui-knk
There is an English word "vast". This commit changes the name to be more clear name to avoid confusion.
2024-04-30Free unused_block_warning_table when RUBY_FREE_AT_EXITPeter Zhu
2024-04-27Remove unnecessary assignment to ast->body.line_countHASUMI Hitoshi
This patch removes a code that assigns `-1` to `ast->body.line_count` because, at least as of now, it looks not necessary. I made this commit atomically revertable if I was wrong. ## Relevant commits - The preparation for this PR: https://github.com/ruby/ruby/pull/10655/files#diff-2af2e7f2e1c28da5e9d99ad117cba1c4dabd8b0bc3081da88e414c55c6aa9549R1484-R1493 - The original commit that introduced the code: https://github.com/ruby/ruby/commit/d65f7458bc8b4fa4404c41713cfa1ece5260fc8a
2024-04-27Add line_count field to rb_ast_body_tHASUMI Hitoshi
This patch adds `int line_count` field to `rb_ast_body_t` structure. Instead, we no longer cast `script_lines` to Fixnum. ## Background Ref https://github.com/ruby/ruby/pull/10618 In the PR above, we have decoupled IMEMO from `rb_ast_t`. This means we could lift the five-words-restriction of the structure that forced us to unionize `rb_ast_t *` and `FIXNUM` in one field. ## Relating refactor - Remove the second parameter of `rb_ruby_ast_new()` function ## Attention I will remove a code that assigns -1 to line_count, in `rb_binding_add_dynavars()` of vm.c, because I don't think it is necessary. But I will make another PR for this so that we can atomically revert in case I was wrong (See the comment on the code)
2024-04-26[Universal parser] Decouple IMEMO from rb_ast_tHASUMI Hitoshi
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object. ## Background We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby. To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE. ## Summary (file by file) - `rubyparser.h` - Remove the `VALUE flags` member from `rb_ast_t` - `ruby_parser.c` and `internal/ruby_parser.h` - Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it - You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()` - Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE` - rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c - `iseq.c` and `vm_core.h` - Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE` - This keeps the VALUE of AST on the machine stack to prevent being removed by GC - `ast.c` - Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff) - Fix `node_memsize()` - Now it includes `rb_ast_local_table_link`, `tokens` and script_lines - `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c` - Follow-up due to the above changes - `imemo.{c|h}` - If an object with `imemo_ast` appears, considers it a bug Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-25YJIT: Optimize local variables when EP == BP (take 2) (#10607)Takashi Kokubun
* Revert "Revert "YJIT: Optimize local variables when EP == BP" (#10584)" This reverts commit c8783441952217c18e523749c821f82cd7e5d222. * YJIT: Take care of GC references in ISEQ invariants Co-authored-by: Alan Wu <alansi.xingwu@shopify.com> --------- Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
2024-04-24Add ruby_mimcallocPeter Zhu
Many places call ruby_mimmalloc then MEMZERO. This can be reduced by using ruby_mimcalloc instead.
2024-04-19Revert "YJIT: Optimize local variables when EP == BP" (#10584)Alan Wu
This reverts commit 4cc58ea0b865f2fd20f1e881ddbd4c4fab0b072c. Since the change landed call-threshold=1 CI runs have been timing out. There has also been `verify-ctx` violations. Revert for now while we debug.
2024-04-19`RUBY_TRY_UNUSED_BLOCK_WARNING_STRICT`Koichi Sasada
`RUBY_TRY_UNUSED_BLOCK_WARNING_STRICT=1 ruby ...` will enable strict check for unused block warning. This option is only for trial to compare the results so the envname is not considered well. Should be removed before Ruby 3.4.0 release.
2024-04-17YJIT: Optimize local variables when EP == BP (#10487)Takashi Kokubun
2024-04-17relax unused block warning for duck typingKoichi Sasada
if a method `foo` uses a block, other (unrelated) method `foo` can receives a block. So try to relax the unused block warning condition. ```ruby class C0 def f = yield end class C1 < C0 def f = nil end [C0, C1].f{ block } # do not warn ```
2024-04-15Emit a performance warning when redefining specially optimized methodsJean Boussier
This makes it easier to notice a dependency is causing interpreter or JIT deoptimization. ```ruby Warning[:performance] = true class String def freeze super end end ``` ``` ./test.rb:4: warning: Redefining 'String#freeze' disable multiple interpreter and JIT optimizations ```
2024-04-15[Universal parser] DeVALUE of p->debug_lines and ast->body.script_linesHASUMI Hitoshi
This patch is part of universal parser work. ## Summary - Decouple VALUE from members below: - `(struct parser_params *)->debug_lines` - `(rb_ast_t *)->body.script_lines` - Instead, they are now `rb_parser_ary_t *` - They can also be a `(VALUE)FIXNUM` as before to hold line count - `ISEQ_BODY(iseq)->variable.script_lines` remains VALUE - In order to do this, - Add `VALUE script_lines` param to `rb_iseq_new_with_opt()` - Introduce `rb_parser_build_script_lines_from()` to convert `rb_parser_ary_t *` into `VALUE` ## Other details - Extend `rb_parser_ary_t *`. It previously could only store `rb_parser_ast_token *`, now can store script_lines, too - Change tactics of building the top-level `SCRIPT_LINES__` in `yycompile0()` - Before: While parsing, each line of the script is added to `SCRIPT_LINES__[path]` - After: After `yyparse(p)`, `SCRIPT_LINES__[path]` will be built from `p->debug_lines` - Remove the second parameter of `rb_parser_set_script_lines()` to make it simple - Introduce `script_lines_free()` to be called from `rb_ast_free()` because the GC no longer takes care of the script_lines - Introduce `rb_parser_string_deep_copy()` in parse.y to maintain script_lines when `rb_ruby_parser_free()` called - With regard to this, please see *Future tasks* below ## Future tasks - Decouple IMEMO from `rb_ast_t *` - This lifts the five-members-restriction of Ruby object, - So we will be able to move the ownership of the `lex.string_buffer` from parser to AST - Then we remove `rb_parser_string_deep_copy()` to make the whole thing simple
2024-04-04Merge rb_objspace_alloc and Init_heap.Matt Valentine-House
Co-Authored-By: Peter Zhu <peter@peterzhu.ca>
2024-03-27Turn GC off at boot on WindowsPeter Zhu
This is to stop crashes like: .\miniruby.exe: [BUG] Segmentation fault ruby 3.4.0dev (2024-03-26T15:38:26Z pull/10370/merge 040ea2ae2f) [x64-mswin64_140] -- Control frame information ----------------------------------------------- c:0001 p:0000 s:0003 E:000d00 DUMMY [FINISH] -- Threading information --------------------------------------------------- Total ractor count: 1 Ruby thread count for this ractor: 1 -- C level backtrace information ------------------------------------------- C:\Windows\SYSTEM32\ntdll.dll(NtWaitForSingleObject+0x14) [0x00007FFA091AFC74] C:\Windows\System32\KERNELBASE.dll(WaitForSingleObjectEx+0x93) [0x00007FFA05BB4513] D:\a\ruby\ruby\build\miniruby.exe(rb_print_backtrace+0x3e) [0x00007FF64E536EFE] d:\a\ruby\ruby\src\vm_dump.c:844 D:\a\ruby\ruby\build\miniruby.exe(rb_vm_bugreport+0x1ae) [0x00007FF64E5370B2] d:\a\ruby\ruby\src\vm_dump.c:1154 D:\a\ruby\ruby\build\miniruby.exe(rb_bug_for_fatal_signal+0x77) [0x00007FF64E3FF357] d:\a\ruby\ruby\src\error.c:1087 D:\a\ruby\ruby\build\miniruby.exe(sigsegv+0x71) [0x00007FF64E4C79E5] d:\a\ruby\ruby\src\signal.c:926 C:\Windows\System32\ucrtbase.dll(seh_filter_exe+0x233) [0x00007FFA0521CE03] D:\a\ruby\ruby\build\miniruby.exe(`__scrt_common_main_seh'::`1'::filt$0+0x16) [0x00007FF64E594DA0] f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:269 C:\Windows\SYSTEM32\VCRUNTIME140.dll(_C_specific_handler+0x9f) [0x00007FF9E54AF73F] C:\Windows\SYSTEM32\ntdll.dll(_chkstk+0x11f) [0x00007FFA091B4C2F] C:\Windows\SYSTEM32\ntdll.dll(RtlWalkFrameChain+0x14bf) [0x00007FFA09114CEF] C:\Windows\SYSTEM32\ntdll.dll(KiUserExceptionDispatcher+0x2e) [0x00007FFA091B399E] D:\a\ruby\ruby\build\miniruby.exe(newobj_of+0x6d) [0x00007FF64E418615] d:\a\ruby\ruby\src\gc.c:2949 D:\a\ruby\ruby\build\miniruby.exe(rb_wb_protected_newobj_of+0x32) [0x00007FF64E41C7DA] d:\a\ruby\ruby\src\gc.c:2974 D:\a\ruby\ruby\build\miniruby.exe(str_new0+0x64) [0x00007FF64E4E7F48] d:\a\ruby\ruby\src\string.c:887 D:\a\ruby\ruby\build\miniruby.exe(rb_enc_str_new+0x40) [0x00007FF64E4D89B8] d:\a\ruby\ruby\src\string.c:945 D:\a\ruby\ruby\build\miniruby.exe(iseq_compile_each0+0xdd7) [0x00007FF64E3B4A23] d:\a\ruby\ruby\src\compile.c:10368 D:\a\ruby\ruby\build\miniruby.exe(iseq_compile_each+0x74) [0x00007FF64E3B3C40] d:\a\ruby\ruby\src\compile.c:9971
2024-03-27Register classpath of FrozenCore before converting to ICLASSPeter Zhu
Since ICLASS do not mark the classpath, we need to register it as a global object before we convert RubyVM::FrozenCore as a ICLASS.
2024-03-25Check ASAN fake stacks when marking non-current threadsKJ Tsanaktsidis
Currently, we check the values on the machine stack & register state to see if they're actually a pointer to an ASAN fake stack, and mark the values on the fake stack too if required. However, we are only doing that for the _current_ thread (the one actually running the GC), not for any other thread in the program. Make rb_gc_mark_machine_context (which is called for marking non-current threads) perform the same ASAN fake stack handling that mark_current_machine_context performs. [Bug #20310]
2024-03-17Prefer `enum ruby_tag_type` over `int`Nobuyoshi Nakada
2024-03-15Initialize VM stack if VM_CHECK_MODEAlan Wu
Lately there has been a few flaky YJIT CI failures where a new Ruby thread is finding the canary on the VM stack. For example: https://github.com/ruby/ruby/actions/runs/8287357784/job/22679508482#step:14:109 After checking a local rr recording, it's clear that the canary was written there when YJIT was using a temporary malloc region, and then later handed to the new Ruby thread. Previously, the VM stack was uninitialized, so it can have stale values in it, like the canary. Though unlikely, this can happen without YJIT too. Initialize the stack if we're spawning canaries.
2024-03-14Fix typo from gloabl_object_list to global_object_listPeter Zhu
2024-03-14Move gloabl_object_list from objspace to VMPeter Zhu
This is to be consistent with the mark_object_ary that is in the VM.
2024-03-14[Feature #20306] Implement ruby_free_at_exit_pPeter Zhu
ruby_free_at_exit_p is a way for extensions to determine whether they should free all memory at shutdown.