summaryrefslogtreecommitdiff
path: root/vm_insnhelper.c
AgeCommit message (Collapse)Author
2022-05-23Remove unnecessary module flag, add module assertions to other module flagsJemma Issroff
Notes: Merged: https://github.com/ruby/ruby/pull/5930
2022-04-27Rust YJITAlan Wu
In December 2021, we opened an [issue] to solicit feedback regarding the porting of the YJIT codebase from C99 to Rust. There were some reservations, but this project was given the go ahead by Ruby core developers and Matz. Since then, we have successfully completed the port of YJIT to Rust. The new Rust version of YJIT has reached parity with the C version, in that it passes all the CRuby tests, is able to run all of the YJIT benchmarks, and performs similarly to the C version (because it works the same way and largely generates the same machine code). We've even incorporated some design improvements, such as a more fine-grained constant invalidation mechanism which we expect will make a big difference in Ruby on Rails applications. Because we want to be careful, YJIT is guarded behind a configure option: ```shell ./configure --enable-yjit # Build YJIT in release mode ./configure --enable-yjit=dev # Build YJIT in dev/debug mode ``` By default, YJIT does not get compiled and cargo/rustc is not required. If YJIT is built in dev mode, then `cargo` is used to fetch development dependencies, but when building in release, `cargo` is not required, only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer. The YJIT command-line options remain mostly unchanged, and more details about the build process are documented in `doc/yjit/yjit.md`. The CI tests have been updated and do not take any more resources than before. The development history of the Rust port is available at the following commit for interested parties: https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be Our hope is that Rust YJIT will be compiled and included as a part of system packages and compiled binaries of the Ruby 3.2 release. We do not anticipate any major problems as Rust is well supported on every platform which YJIT supports, but to make sure that this process works smoothly, we would like to reach out to those who take care of building systems packages before the 3.2 release is shipped and resolve any issues that may come up. [issue]: https://bugs.ruby-lang.org/issues/18481 Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com> Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com> Co-authored-by: Kevin Newton <kddnewton@gmail.com> Notes: Merged: https://github.com/ruby/ruby/pull/5826
2022-04-08sync `vm->constant_cache`Koichi Sasada
On multi-ractors, `vm->constant_cache` (and so on) can be accessed in parallel so we need to synchronize the accesses to them. http://rubyci.s3.amazonaws.com/centos7/ruby-master/log/20220407T213003Z.log.html.gz#btest Notes: Merged: https://github.com/ruby/ruby/pull/5779
2022-04-07Fix strict aliasing issueAlan Wu
`rb_id_table_lookup()` writes to a `VALUE`, which is definitely a distinct type from `st_table *`. With LTO, the compiler is allowed by N1256 §6.5p7 to remove the output parameter write via type-based alias analysis. See also: a0a8f2abf53 Notes: Merged: https://github.com/ruby/ruby/pull/5773
2022-04-01Finer-grained constant cache invalidation (take 2)Kevin Newton
This commit reintroduces finer-grained constant cache invalidation. After 8008fb7 got merged, it was causing issues on token-threaded builds (such as on Windows). The issue was that when you're iterating through instruction sequences and using the translator functions to get back the instruction structs, you're either using `rb_vm_insn_null_translator` or `rb_vm_insn_addr2insn2` depending if it's a direct-threading build. `rb_vm_insn_addr2insn2` does some normalization to always return to you the non-trace version of whatever instruction you're looking at. `rb_vm_insn_null_translator` does not do that normalization. This means that when you're looping through the instructions if you're trying to do an opcode comparison, it can change depending on the type of threading that you're using. This can be very confusing. So, this commit creates a new translator function `rb_vm_insn_normalizing_translator` to always return the non-trace version so that opcode comparisons don't have to worry about different configurations. [Feature #18589] Notes: Merged: https://github.com/ruby/ruby/pull/5716
2022-03-25Revert "Finer-grained inline constant cache invalidation"Nobuyoshi Nakada
This reverts commits for [Feature #18589]: * 8008fb7352abc6fba433b99bf20763cf0d4adb38 "Update formatting per feedback" * 8f6eaca2e19828e92ecdb28b0fe693d606a03f96 "Delete ID from constant cache table if it becomes empty on ISEQ free" * 629908586b4bead1103267652f8b96b1083573a8 "Finer-grained inline constant cache invalidation" MSWin builds on AppVeyor have been crashing since the merger. Notes: Merged: https://github.com/ruby/ruby/pull/5715 Merged-By: nobu <nobu@ruby-lang.org>
2022-03-24Finer-grained inline constant cache invalidationKevin Newton
Current behavior - caches depend on a global counter. All constant mutations cause caches to be invalidated. ```ruby class A B = 1 end def foo A::B # inline cache depends on global counter end foo # populate inline cache foo # hit inline cache C = 1 # global counter increments, all caches are invalidated foo # misses inline cache due to `C = 1` ``` Proposed behavior - caches depend on name components. Only constant mutations with corresponding names will invalidate the cache. ```ruby class A B = 1 end def foo A::B # inline cache depends constants named "A" and "B" end foo # populate inline cache foo # hit inline cache C = 1 # caches that depend on the name "C" are invalidated foo # hits inline cache because IC only depends on "A" and "B" ``` Examples of breaking the new cache: ```ruby module C # Breaks `foo` cache because "A" constant is set and the cache in foo depends # on "A" and "B" class A; end end B = 1 ``` We expect the new cache scheme to be invalidated less often because names aren't frequently reused. With the cache being invalidated less, we can rely on its stability more to keep our constant references fast and reduce the need to throw away generated code in YJIT. Notes: Merged: https://github.com/ruby/ruby/pull/5433
2022-03-24Add ISEQ_BODY macroPeter Zhu
Use ISEQ_BODY macro to get the rb_iseq_constant_body of the ISeq. Using this macro will make it easier for us to change the allocation strategy of rb_iseq_constant_body when using Variable Width Allocation. Notes: Merged: https://github.com/ruby/ruby/pull/5698
2022-03-10Small optimization for the opt_and instructionAaron Patterson
This change eagerly performs a bitwise and on the parameters. If both parameters are fixnums, then the result value should also be a fixnum. We can just test the bit on the result and return if it's a fixnum. Otherwise return Qundef. Notes: Merged: https://github.com/ruby/ruby/pull/5629
2022-01-26Streamline cached attr reader / writer indexesJemma Issroff
This commit removes the need to increment and decrement the indexes used by vm_cc_attr_index getters and setters. It also introduces a vm_cc_attr_index_p predicate function, and a vm_cc_attr_index_initalize function. Notes: Merged: https://github.com/ruby/ruby/pull/5485
2022-01-01Negative RBOOL usageNobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/5385
2021-12-24@@cv is not accessible from non-main ractorsKoichi Sasada
Class variables (@@cv) is not accessible from non-main ractors. But without this patch cached @@cv can be read. fix [Bug #18128] Notes: Merged: https://github.com/ruby/ruby/pull/5335
2021-12-21`mandatory_only_cme` should not be in `def`Koichi Sasada
`def` (`rb_method_definition_t`) is shared by multiple callable method entries (cme, `rb_callable_method_entry_t`). There are two issues: * old -> young reference: `cme1->def->mandatory_only_cme = monly_cme` if `cme1` is young and `monly_cme` is young, there is no problem. Howevr, another old `cme2` can refer `def`, in this case, old `cme2` points young `monly_cme` and it violates gengc assumption. * cme can have different `defined_class` but `monly_cme` only has one `defined_class`. It does not make sense and `monly_cme` should be created for a cme (not `def`). To solve these issues, this patch allocates `monly_cme` per `cme`. `cme` does not have another room to store a pointer to the `monly_cme`, so this patch introduces `overloaded_cme_table`, which is weak key map `[cme] -> [monly_cme]`. `def::body::iseqptr::monly_cme` is deleted. The first issue is reported by Alan Wu. Notes: Merged: https://github.com/ruby/ruby/pull/5311
2021-12-02Lazily create singletons on instance_{exec,eval} (#5146)John Hawthorn
* Lazily create singletons on instance_{exec,eval} Previously when instance_exec or instance_eval was called on an object, that object would be given a singleton class so that method definitions inside the block would be added to the object rather than its class. This commit aims to improve performance by delaying the creation of the singleton class unless/until one is needed for method definition. Most of the time instance_eval is used without any method definition. This was implemented by adding a flag to the cref indicating that it represents a singleton of the object rather than a class itself. In this case CREF_CLASS returns the object's existing class, but in cases that we are defining a method (either via definemethod or VM_SPECIAL_OBJECT_CBASE which is used for undef and alias). This also happens to fix what I believe is a bug. Previously instance_eval behaved differently with regards to constant access for true/false/nil than for all other objects. I don't think this was intentional. String::Foo = "foo" "".instance_eval("Foo") # => "foo" Integer::Foo = "foo" 123.instance_eval("Foo") # => "foo" TrueClass::Foo = "foo" true.instance_eval("Foo") # NameError: uninitialized constant Foo This also slightly changes the error message when trying to define a method through instance_eval on an object which can't have a singleton class. Before: $ ruby -e '123.instance_eval { def foo; end }' -e:1:in `block in <main>': no class/module to add method (TypeError) After: $ ./ruby -e '123.instance_eval { def foo; end }' -e:1:in `block in <main>': can't define singleton (TypeError) IMO this error is a small improvement on the original and better matches the (both old and new) message when definging a method using `def self.` $ ruby -e '123.instance_eval{ def self.foo; end }' -e:1:in `block in <main>': can't define singleton (TypeError) Co-authored-by: Matthew Draper <matthew@trebex.net> * Remove "under" argument from yield_under * Move CREF_SINGLETON_SET into vm_cref_new * Simplify vm_get_const_base * Fix leaf VM_SPECIAL_OBJECT_CONST_BASE Co-authored-by: Matthew Draper <matthew@trebex.net> Notes: Merged-By: jhawthorn <john@hawthorn.email>
2021-12-01Rework tracing for blocks running as methodsAlan Wu
The main impetus for this change is to fix [Bug #13392]. Previously, we fired the "return" TracePoint event after popping the stack frame for the block running as method (BMETHOD). This gave undesirable source location outputs as the return event normally fires right before the frame going away. The iseq for each block can run both as a block and as a method. To accommodate that, this commit makes vm_trace() fire call/return events for instructions that have b_call/b_return events attached when the iseq is running as a BMETHOD. The logic for rewriting to "trace_*" instruction is tweaked so that when the user listens to call/return events, instructions with b_call/b_return become trace variants. To continue to provide the return value for non-local returns done using the "return" or "break" keyword inside BMETHODs, the stack unwinding code is tweaked. b_return events now provide the same return value as return events for these non-local cases. A pre-existing test deemed not providing a return value for these b_return events as a limitation. This commit removes the checks for call/return TracePoint events that happen when calling into BMETHODs when no TracePoints are active. Technically, migrating just the return event is enough to fix the bug, but migrating both call and return removes our reliance on `VM_FRAME_FLAG_FINISH` and re-entering the interpreter when the caller is already in the interpreter. Notes: Merged: https://github.com/ruby/ruby/pull/4637
2021-11-23Add setclassvariable to yjit (#5127)Eileen M. Uchitelle
Implements setclassvariable in yjit. Note that this version is not faster than the standard version because we aren't handling the inline cache in assembly. This is still important to implement because it will prevent yjit from exiting in methods that call both a cvar setter and other code that yjit can compile. Co-authored-by: Aaron Patterson tenderlove@ruby-lang.org Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
2021-11-21Fix setting struct member by public_sendNobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/5152
2021-11-19optimize `Struct` getter/setterKoichi Sasada
Introduce new optimized method type `OPTIMIZED_METHOD_TYPE_STRUCT_AREF/ASET` with index information. Notes: Merged: https://github.com/ruby/ruby/pull/5131
2021-11-19`rb_method_optimized_t` for further extensionKoichi Sasada
Now `rb_method_optimized_t optimized` field is added to represent optimized method type. Notes: Merged: https://github.com/ruby/ruby/pull/5131
2021-11-18Optimize dynamic string interpolation for symbol/true/false/nil/0-9Jeremy Evans
This provides a significant speedup for symbol, true, false, nil, and 0-9, class/module, and a small speedup in most other cases. Speedups (using included benchmarks): :symbol :: 60% 0-9 :: 50% Class/Module :: 50% nil/true/false :: 20% integer :: 10% [] :: 10% "" :: 3% One reason this approach is faster is it reduces the number of VM instructions for each interpolated value. Initial idea, approach, and benchmarks from Eric Wong. I applied the same approach against the master branch, updating it to handle the significant internal changes since this was first proposed 4 years ago (such as CALL_INFO/CALL_CACHE -> CALL_DATA). I also expanded it to optimize true/false/nil/0-9/class/module, and added handling of missing methods, refined methods, and RUBY_DEBUG. This renames the tostring insn to anytostring, and adds an objtostring insn that implements the optimization. This requires making a few functions non-static, and adding some non-static functions. This disables 4 YJIT tests. Those tests should be reenabled after YJIT optimizes the new objtostring insn. Implements [Feature #13715] Co-authored-by: Eric Wong <e@80x24.org> Co-authored-by: Alan Wu <XrXr@users.noreply.github.com> Co-authored-by: Yusuke Endoh <mame@ruby-lang.org> Co-authored-by: Koichi Sasada <ko1@atdot.net> Notes: Merged: https://github.com/ruby/ruby/pull/5002 Merged-By: jeremyevans <code@jeremyevans.net>
2021-11-18Refactor setclassvariable (#5143)Eileen M. Uchitelle
We only need the cref when we have a cache miss so don't look it up until we need it. This likely speeds up class variable writes in the interpreter but also simplifies the jit code. Before ``` Warming up -------------------------------------- write a cvar 192.280k i/100ms Calculating ------------------------------------- write a cvar 1.915M (± 3.5%) i/s - 9.614M in 5.026694s ``` After ``` Warming up -------------------------------------- write a cvar 216.308k i/100ms Calculating ------------------------------------- write a cvar 2.140M (± 3.1%) i/s - 10.815M in 5.058079s ``` Followup to ruby/ruby#5137 Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
2021-11-18Refactor getclassvariable (#5137)Eileen M. Uchitelle
* Refactor getclassvariable We only need the cref when we have a cache miss so don't look it up until we need it. This speeds up class variable reads in the interpreter but also simplifies the jit code. Benchmarks for master vs this branch (without yjit): Before: ``` Warming up -------------------------------------- read a cvar 1.276M i/100ms Calculating ------------------------------------- read a cvar 12.596M (± 1.7%) i/s - 63.781M in 5.064902s ``` After: ``` Warming up -------------------------------------- read a cvar 1.336M i/100ms Calculating ------------------------------------- read a cvar 13.114M (± 3.6%) i/s - 65.488M in 5.000584s ``` Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org> * Clean up function signatures / remove dead code rb_vm_getclassvariable signature has changed and we don't need rb_vm_get_cref. Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org> Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
2021-11-17no need to check `cme == NULL`Koichi Sasada
Now `cc->cme_` is not NULL. Notes: Merged: https://github.com/ruby/ruby/pull/5122
2021-11-17`vm_empty_cc_for_super`Koichi Sasada
Same as `vm_empty_cc`, introduce a global variable which has `.call_ = vm_call_super_method`. Use it if the `cme == NULL` on `vm_search_super_method`. Notes: Merged: https://github.com/ruby/ruby/pull/5122
2021-11-17a variable is not needed.Koichi Sasada
Notes: Merged: https://github.com/ruby/ruby/pull/5122
2021-11-17`Primitive.mandatory_only?` consider splat argsJean Boussier
`vm_ci_argc` gives the number of arguments, but `*[1, 2, 3]` only counts for one. Notes: Merged: https://github.com/ruby/ruby/pull/5124
2021-11-15`Primitive.mandatory_only?` for fast pathKoichi Sasada
Compare with the C methods, A built-in methods written in Ruby is slower if only mandatory parameters are given because it needs to check the argumens and fill default values for optional and keyword parameters (C methods can check the number of parameters with `argc`, so there are no overhead). Passing mandatory arguments are common (optional arguments are exceptional, in many cases) so it is important to provide the fast path for such common cases. `Primitive.mandatory_only?` is a special builtin function used with `if` expression like that: ```ruby def self.at(time, subsec = false, unit = :microsecond, in: nil) if Primitive.mandatory_only? Primitive.time_s_at1(time) else Primitive.time_s_at(time, subsec, unit, Primitive.arg!(:in)) end end ``` and it makes two ISeq, ``` def self.at(time, subsec = false, unit = :microsecond, in: nil) Primitive.time_s_at(time, subsec, unit, Primitive.arg!(:in)) end def self.at(time) Primitive.time_s_at1(time) end ``` and (2) is pointed by (1). Note that `Primitive.mandatory_only?` should be used only in a condition of an `if` statement and the `if` statement should be equal to the methdo body (you can not put any expression before and after the `if` statement). A method entry with `mandatory_only?` (`Time.at` on the above case) is marked as `iseq_overload`. When the method will be dispatch only with mandatory arguments (`Time.at(0)` for example), make another method entry with ISeq (2) as mandatory only method entry and it will be cached in an inline method cache. The idea is similar discussed in https://bugs.ruby-lang.org/issues/16254 but it only checks mandatory parameters or more, because many cases only mandatory parameters are given. If we find other cases (optional or keyword parameters are used frequently and it hurts performance), we can extend the feature. Notes: Merged: https://github.com/ruby/ruby/pull/5112
2021-11-11[Bug #18329] Fix crash when calling non-existent super methodPeter Zhu
The cme is NULL when a method does not exist, so check it before accessing the callcache. Notes: Merged: https://github.com/ruby/ruby/pull/5108
2021-10-29vm_core.h: Avoid unaligned access to ic_serial on 32-bit machineYusuke Endoh
This caused Bus error on 32 bit Solaris Notes: Merged: https://github.com/ruby/ruby/pull/5049
2021-10-27the core problem is the Proc is not shareableSatoshi Moris Tagomori
Notes: Merged: https://github.com/ruby/ruby/pull/4771
2021-10-20Extract yjit_force_iv_index and make it work when object is frozenAlan Wu
In an effort to simplify the logic YJIT generates for accessing instance variable, YJIT ensures that a given name-to-index mapping exists at compile time. In the case that the mapping doesn't exist, it was created by using rb_ivar_set() with Qundef on the sample object we see at compile time. This hack isn't fine if the sample object happens to be frozen, in which case YJIT would raise a FrozenError unexpectedly. To deal with this, make a new function that only reserves the mapping but doesn't touch the object. This is rb_obj_ensure_iv_index_mapping(). This new function superceeds the functionality of rb_iv_index_tbl_lookup() so it was removed. Reported by and includes a test case from John Hawthorn <john@hawthorn.email> Fixes: GH-282
2021-10-20Add comments about special runtime routines YJIT callsAlan Wu
When YJIT make calls to routines without reconstructing interpreter state through jit_prepare_routine_call(), it relies on the routine to never allocate, raise, and push/pop control frames. Comment about this on the routines that YJTI calls. This is probably something we should dynamically verify on debug builds. It's hard to statically verify this as it requires verifying all functions in the call tree. Maybe something to look at in the future.
2021-10-20Cleanup diff against upstream. Add commentsAlan Wu
I did a `git diff --stat` against upstream and looked at all the files that are outside of YJIT to come up with these minor changes.
2021-10-20Put YJIT into a single compilation unitAlan Wu
For upstreaming, we want functions we export either prefixed with "rb_" or made static. Historically we haven't been following this rule, so we were "leaking" a lot of symbols as `make leak-globals` would tell us. This change unifies everything YJIT into a single compilation unit, yjit.o, and makes everything unprefixed static to pass `make leak-globals`. This manual "unified build" setup is similar to that of vm.o. Having everything in one compilation unit allows static functions to be visible across YJIT files and removes the need for declarations in headers in some cases. Unnecessary declarations were removed. Other changes of note: - switched to MJIT_SYMBOL_EXPORT_BEGIN which indicates stuff as being off limits for native extensions - the first include of each YJIT file is change to be "internal.h" - undefined MAP_STACK before explicitly redefining it since it collide's with a definition in system headers. Consider renaming?
2021-10-20Implement getclassvariable in yjiteileencodes
Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
2021-10-20Add a slowpath for opt_getinlinecacheAlan Wu
Before this change, when we encounter a constant cache that is specific to a lexical scope, we unconditionally exit. This change falls back to the interpreter's cache in this situation. This should help constant expressions in `class << self`, which is popular at Shopify due to the style guide. This change relies on the cache being warm while compiling to detect the need for checking the lexical scope for simplicity.
2021-10-20Remove vm_opt_asetJohn Hawthorn
2021-10-20Add comments for new functionAaron Patterson
2021-10-20Refactor attrset to use a functionAaron Patterson
This new function will do the write barrier / resize the object / check frozen for us
2021-10-20Remove rb_opt_equality_specializedJohn Hawthorn
2021-10-20Implement splatarrayKevin Newton
2021-10-20Try to fix MJIT symbol clash with cargo cultMaxime Chevalier-Boisvert
2021-10-20Implement defined bytecode (#39)Maxime Chevalier-Boisvert
2021-10-20Implement setivar with a plain old function call (#34)Maxime Chevalier-Boisvert
* Implement setivar with a plain old function call * Remove return
2021-10-20Implement opt_aset as interpreter handler callMaxime Chevalier-Boisvert
2021-10-20Implement opt_mod as call to interpreter function (#29)Maxime Chevalier-Boisvert
2021-10-20Implement opt_eq by calling interpreter function (#28)Maxime Chevalier-Boisvert
2021-10-20Implement send with alias method (#23)Maxime Chevalier-Boisvert
* Implement send with alias method * Add alias_method tests
2021-10-20Implement calls to methods with simple optional paramsAlan Wu
* Implement calls to methods with simple optional params * Remove unnecessary MJIT_STATIC See comment for MJIT_STATIC. I added it not knowing whether it's required because the function next to it has it. Don't use it and wait for problems to come up instead. * Better naming, some comments * Count bailing on kw only iseqs On railsbench: ``` opt_send_without_block exit reasons: bmethod 59729 (27.7%) optimized_method 59137 (27.5%) iseq_complex_callee 41362 (19.2%) alias_method 33346 (15.5%) callsite_not_simple 19170 ( 8.9%) iseq_only_keywords 1300 ( 0.6%) kw_splat 1299 ( 0.6%) cfunc_ruby_array_varg 18 ( 0.0%) ```
2021-10-20YJIT: Fancier opt_getinlinecacheAlan Wu
Make sure `opt_getinlinecache` is in a block all on its own, and invalidate it from the interpreter when `opt_setinlinecache`. It will recompile with a filled cache the second time around. This lets YJIT runs well when the IC for constant is cold.