summaryrefslogtreecommitdiff
path: root/iseq.c
Commit message (Collapse)AuthorAgeFilesLines
* Reuse an interned stringNobuyoshi Nakada2022-06-171-3/+2
| | | | | | Repeating to intern the same string is just redundant, as interned strings for the same content are always the same object until it gets collected.
* Rust YJITAlan Wu2022-04-271-3/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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>
* Adjust indent [ci skip]Nobuyoshi Nakada2022-04-021-29/+29
|
* Finer-grained constant cache invalidation (take 2)Kevin Newton2022-04-011-0/+114
| | | | | | | | | | | | | | | | | | | | | | | | 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]
* Revert "Finer-grained inline constant cache invalidation"Nobuyoshi Nakada2022-03-251-90/+0
| | | | | | | | | | | | 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.
* Delete ID from constant cache table if it becomes empty on ISEQ freeKevin Newton2022-03-241-0/+5
| | | | Co-authored-by: John Hawthorn <john@hawthorn.email>
* Finer-grained inline constant cache invalidationKevin Newton2022-03-241-0/+85
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* Add ISEQ_BODY macroPeter Zhu2022-03-241-52/+52
| | | | | | 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.
* Fix indents [ci skip]Nobuyoshi Nakada2022-02-031-1/+1
|
* Treat TS_ICVARC cache as separate from TS_IVC cacheJemma Issroff2022-02-021-0/+3
|
* `rb_iseq_update_references()` cares `script_lines`Koichi Sasada2021-12-191-0/+1
| | | | | and it fixes compaction issue: http://rubyci.s3.amazonaws.com/freebsd12/ruby-master/log/20211218T203001Z.fail.html.gz
* `iseq_type_sym()` -> `iseq_type_id()`Koichi Sasada2021-12-191-40/+40
| | | | | `iseq_type_sym()` returns `ID` (surprisingly!) so rename it to `iseq_type_id()`.
* add `rb_iseq_type()` to return iseq type in SymbolKoichi Sasada2021-12-191-36/+53
| | | | It is shorthand `ISeq#to_a[9]`.
* fix local TP memory leakKoichi Sasada2021-12-151-3/+2
| | | | | | | It free `rb_hook_list_t` itself if needed. To recognize the need, this patch introduced `rb_hook_list_t::is_local` flag. This patch is succession of https://github.com/ruby/ruby/pull/4652
* Introduce an option "--dump=insns_without_opt" for debugging purposesYusuke Endoh2021-12-131-2/+2
|
* Rework tracing for blocks running as methodsAlan Wu2021-12-011-2/+23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* add `VM_CALLCACHE_ON_STACK`Koichi Sasada2021-11-171-6/+11
| | | | check if iseq refers to on stack CC (it shouldn't).
* `Primitive.mandatory_only?` for fast pathKoichi Sasada2021-11-151-0/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* need to mark script_linesKoichi Sasada2021-10-291-0/+1
|
* freeze (make shareable) script_linesKoichi Sasada2021-10-211-2/+7
|
* `RubyVM.keep_script_lines`Koichi Sasada2021-10-211-4/+40
| | | | | | | | | | | | | | `RubyVM.keep_script_lines` enables to keep script lines for each ISeq and AST. This feature is for debugger/REPL support. ```ruby RubyVM.keep_script_lines = true RubyVM::keep_script_lines = true eval("def foo = nil\ndef bar = nil") pp RubyVM::InstructionSequence.of(method(:foo)).script_lines ```
* Cleanup iseq.c minimize diff with upstreamAlan Wu2021-10-201-21/+8
| | | | | Most of these are vestiges of our old setup where we hack into the interpreter loop.
* TracePoint supportAlan Wu2021-10-201-16/+0
| | | | | | | | | | | | | | | | | | | | | | This change fixes some cases where YJIT fails to fire tracing events. Most of the situations YJIT did not handle correctly involves enabling tracing while running inside generated code. A new operation to invalidate all generated code is added, which uses patching to make generated code exit at the next VM instruction boundary. A new routine called `jit_prepare_routine_call()` is introduced to facilitate this and should be used when generating code that could allocate, or could otherwise use `RB_VM_LOCK_ENTER()`. The `c_return` event is fired in the middle of an instruction as opposed to at an instruction boundary, so it requires special handling. C method call return points are patched to go to a fucntion which does everything the interpreter does, including firing the `c_return` event. The generated code for C method calls normally does not fire the event. Invalided code should not change after patching so the exits are not clobbered. A new variable is introduced to track the region of code that should not change.
* Clear JIT code when tracepoints get enabledAaron Patterson2021-10-201-0/+11
| | | | | Clear out any JIT code on iseqs when tracepoints get enabled. We can't handle tracepoints right now, so we'll just try to recompile later.
* Yet Another Ruby JIT!Jose Narvaez2021-10-201-6/+6
| | | | Renaming uJIT to YJIT. AKA s/ujit/yjit/g.
* Should fix builds without mjit supportAlan Wu2021-10-201-0/+1
|
* Tie lifetime of uJIT blocks to iseqsAlan Wu2021-10-201-0/+3
| | | | | | | | | | | | | | | | | | | | * Tie lifetime of uJIT blocks to iseqs Blocks weren't being freed when iseqs are collected. * Add rb_dary. Use it for method dependency table * Keep track of blocks per iseq Remove global version_tbl * Block version bookkeeping fix * dary -> darray * free ujit_blocks * comment about size of ujit_blocks
* Restore interpreter regs in ujit hook. Implement leave bytecode.Maxime Chevalier-Boisvert2021-10-201-11/+2
|
* Add to the MicroJIT scraper an example that passes ecAlan Wu2021-10-201-0/+7
|
* MicroJIT: Don't compile trace instructionsAlan Wu2021-10-201-0/+19
|
* Small build fixesAlan Wu2021-10-201-0/+2
|
* Try to fix Windows buildAlan Wu2021-10-201-1/+0
|
* Avoid triggering GC while translating threaded codeAlan Wu2021-10-201-0/+1
|
* Remove PC argument from ujit instructionsMaxime Chevalier-Boisvert2021-10-201-1/+1
|
* Removed native_pop_code, ported call with labelMaxime Chevalier-Boisvert2021-10-201-61/+0
|
* Generate multiple copies of native code for `pop`Alan Wu2021-10-201-6/+6
| | | | | | Insert generated addresses into st_table for mapping native code addresses back to info about VM instructions. Export `encoded_insn_data` to do this. Also some style fixes.
* Reimplement Alan's pop instruction with the new assemblerMaxime Chevalier-Boisvert2021-10-201-0/+26
|
* Added sub instruction, 32-bit registers, more testsMaxime Chevalier-Boisvert2021-10-201-0/+12
|
* Yeah, this actually works!Alan Wu2021-10-201-1/+33
|
* Add example handler for ujit and scrape it from vm.oAlan Wu2021-10-201-1/+10
|
* Using NIL_P macro instead of `== Qnil`S.H2021-10-031-4/+4
|
* Replace RBOOL macroS-H-GAMELINKS2021-09-051-1/+1
|
* Support tracing of attr_reader and attr_writerJeremy Evans2021-08-291-0/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | In vm_call_method_each_type, check for c_call and c_return events before dispatching to vm_call_ivar and vm_call_attrset. With this approach, the call cache will still dispatch directly to those functions, so this change will only decrease performance for the first (uncached) call, and even then, the performance decrease is very minimal. This approach requires that we clear the call caches when tracing is enabled or disabled. The approach currently switches all vm_call_ivar and vm_call_attrset call caches to vm_call_general any time tracing is enabled or disabled. So it could theoretically result in a slowdown for code that constantly enables or disables tracing. This approach does not handle targeted tracepoints, but from my testing, c_call and c_return events are not supported for targeted tracepoints, so that shouldn't matter. This includes a benchmark showing the performance decrease is minimal if detectable at all. Fixes [Bug #16383] Fixes [Bug #10470] Co-authored-by: Takashi Kokubun <takashikkbn@gmail.com>
* Add keyrest to ruby2_keywords parameters [Bug #18011]Nobuyoshi Nakada2021-08-031-2/+11
|
* Make RubyVM::AbstractSyntaxTree.of raise for method/proc created in evalJeremy Evans2021-07-291-0/+6
| | | | | | | | | | | This changes Thread::Location::Backtrace#absolute_path to return nil for methods/procs defined in eval. If the realpath of an iseq is nil, that indicates it was defined in eval, in which case you cannot use RubyVM::AbstractSyntaxTree.of. Fixes [Bug #16983] Co-authored-by: Koichi Sasada <ko1@atdot.net>
* iseq.c: Make ast_line_count return 0 when syntax error occurredYusuke Endoh2021-06-261-0/+4
| | | | | | | | | | | | This broke coverage CI ``` 1) Failure: TestRequire#test_load_syntax_error [/home/runner/work/actions/actions/ruby/test/ruby/test_require.rb:228]: Exception(SyntaxError) with message matches to /unexpected/. [SyntaxError] exception expected, not #<TypeError: no implicit conversion of false into Integer>. ``` https://github.com/ruby/actions/runs/2914743968?check_suite_focus=true
* Enable USE_ISEQ_NODE_ID by defaultYusuke Endoh2021-06-181-4/+4
| | | | | | | | ... which is formally called EXPERIMENTAL_ISEQ_NODE_ID. See also ff69ef27b06eed1ba750e7d9cab8322f351ed245. https://bugs.ruby-lang.org/issues/17930
* Make it possible to get AST::Node from Thread::Backtrace::LocationYusuke Endoh2021-06-181-1/+1
| | | | | | | | | RubyVM::AST.of(Thread::Backtrace::Location) returns a node that corresponds to the location. Typically, the node is a method call, but not always. This change also includes iseq's dump/load support of node_ids for each instructions.
* node.h: Reduce struct size to fit with Ruby object size (five VALUEs)Yusuke Endoh2021-06-181-2/+12
| | | | | | | | by merging `rb_ast_body_t#line_count` and `#script_lines`. Fortunately `line_count == RARRAY_LEN(script_lines)` was always satisfied. When script_lines is saved, it has an array of lines, and when not saved, it has a Fixnum that represents the old line_count.
* Refactor rb_vm_insn_addr2insn callsTakashi Kokubun2021-06-021-0/+12
| | | | It's been a way too much amount of ifdefs.