summaryrefslogtreecommitdiff
path: root/compile.c
Commit message (Collapse)AuthorAgeFilesLines
...
* New constant caching insn: opt_getconstant_pathJohn Hawthorn2022-09-011-68/+137
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Previously YARV bytecode implemented constant caching by having a pair of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a series of getconstant calls (with putobject providing supporting arguments). This commit replaces that pattern with a new instruction, opt_getconstant_path, handling both getting/setting the inline cache and fetching the constant on a cache miss. This is implemented by storing the full constant path as a null-terminated array of IDs inside of the IC structure. idNULL is used to signal an absolute constant reference. $ ./miniruby --dump=insns -e '::Foo::Bar::Baz' == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE) 0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li] 0002 leave The motivation for this is that we had increasingly found the need to disassemble the instructions between the opt_getinlinecache and opt_setinlinecache in order to determine the constant we are fetching, or otherwise store metadata. This disassembly was done: * In opt_setinlinecache, to register the IC against the constant names it is using for granular invalidation. * In rb_iseq_free, to unregister the IC from the invalidation table. * In YJIT to find the position of a opt_getinlinecache instruction to invalidate it when the cache is populated * In YJIT to register the constant names being used for invalidation. With this change we no longe need disassemly for these (in fact rb_iseq_each is now unused), as the list of constant names being referenced is held in the IC. This should also make it possible to make more optimizations in the future. This may also reduce the size of iseqs, as previously each segment required 32 bytes (on 64-bit platforms) for each constant segment. This implementation only stores one ID per-segment. There should be no significant performance change between this and the previous implementation. Previously opt_getinlinecache was a "leaf" instruction, but it included a jump (almost always to a separate cache line). Now opt_getconstant_path is a non-leaf (it may raise/autoload/call const_missing) but it does not jump. These seem to even out.
* Convert catch_except_t to stdboolTakashi Kokubun2022-08-251-5/+5
| | | | | | | catch_excep_t is a field that exists for MJIT. In the process of rewriting MJIT in Ruby, I added API to convert 1/0 of _Bool to true/false, and it seemed confusing and hard to maintain if you don't use _Bool for *_p fields.
* Optimize duparray/expandarray -> putobject/expandarrayJeremy Evans2022-08-091-0/+14
| | | | | | There's no point in making a copy of an array just to expand it. Saves an unnecessary array allocation in the multiple assignment case, with a 35-84% improvement in affected cases in benchmark/masgn.yml.
* Expand newarray/expandarray optimization for unequal operandsJeremy Evans2022-08-091-3/+39
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This optimizes unbalanced multiple assignment cases such as: ```ruby a.b, c.d = e, f, g a.b, c.d, e.f = g, h ``` Previously, this would use: ``` newarray(3) expandarray(2, 0) newarray(2) expandarray(3, 0) ``` These would both allocate arrays. This switches to opt_reverse with either pop or putnil: ``` pop opt_reverse(2) putnil opt_reverse(3) ``` This avoids an unnecessary array allocation, and results in a 35-76% performance increase in these types of unbalanced cases (tested with benchmark/masgn.yml).
* Add peephole optimizer for newarray(X)/expandarray(X, 0) -> opt_reverse(X)Jeremy Evans2022-08-091-1/+10
| | | | | | | This renames the reverse instruction to opt_reverse, since now it is only added by the optimizer. Then it uses as a more general form of swap. This optimizes multiple assignment in the popped case with more than two elements.
* Add peephole optimizer for newarray(2)/expandarray(2, 0) -> swapJeremy Evans2022-08-091-0/+19
| | | | | | | | | | | | | | | | | | | An optimization for multiple assignment in the popped case to avoid array allocation was lost in my fix to make multiple assignment follow left-to-right evaluation (50c54d40a81bb2a4794a6be5f1861152900b4fed). Before, in the two element case, swap was used. Afterward, newarray(2) and expandarray(2, 0) were used, which is the same as swap, with the addition of an unnecessary allocation. Because this issue is not specific to multiple assignment, and the multiple assignment code is complex enough as it is, this updates the peephole optimizer to do the newarray(2)/expandarray(2, 0) -> swap conversion. A more general optimization pass for newarray(X)/expandarray(X, 0) -> reverse(X) will follow, but that requires readding the reverse instruction.
* Adjust styles [ci skip]Nobuyoshi Nakada2022-07-271-1/+2
|
* Rename rb_ary_tmp_new to rb_ary_hidden_newPeter Zhu2022-07-261-11/+11
| | | | | | rb_ary_tmp_new suggests that the array is temporary in some way, but that's not true, it just creates an array that's hidden and not on the transient heap. This commit renames it to rb_ary_hidden_new.
* Remove duplicate code for internal arraysNobuyoshi Nakada2022-07-231-2/+1
| | | | Internal arrays are now created hidden from the start.
* Use rb_ary_tmp_new only for internal arraysPeter Zhu2022-07-221-1/+1
| | | | | rb_ary_tmp_new sets the klass to 0, so it should only be used for internal arrays.
* Remove reference counting for all frozen arraysPeter Zhu2022-07-221-2/+2
| | | | | | | | | | | The RARRAY_LITERAL_FLAG was added in commit 5871ecf956711fcacad7c03f2aef95115ed25bc4 to improve CoW performance for array literals by not keeping track of reference counts. This commit reverts that commit and has an alternate implementation that is more generic for all frozen arrays. Since frozen arrays cannot be modified, we don't need to set the RARRAY_SHARED_ROOT_FLAG and we don't need to do reference counting.
* Add "rb_" prefixes to toplevel enum definitionsYusuke Endoh2022-07-221-7/+7
| | | | ... as per ko1's request.
* Expand tabs [ci skip]Takashi Kokubun2022-07-211-3252/+3252
| | | | [Misc #18891]
* Add RARRAY_LITERAL_FLAG for array literalsPeter Zhu2022-07-201-2/+2
| | | | | | | | | | | | | Array created as literals during iseq compilation don't need a reference count since they can never be modified. The previous implementation would mutate the hidden array's reference count, causing copy-on-write invalidation. This commit adds a RARRAY_LITERAL_FLAG for arrays created through rb_ary_literal_new. Arrays created with this flag do not have reference count stored and just assume they have infinite number of references. Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
* Separate TS_IVC and TS_ICVARC in is_entries buffersJemma Issroff2022-07-181-4/+31
| | | | This allows us to treat cvar caches differently than ivar caches.
* Check only whether `RUBY_DEVEL` is definedNobuyoshi Nakada2022-07-121-1/+1
|
* Fix a regression of b2e58b02aec73f9c350bf109c021c180fc699cccYusuke Endoh2022-07-111-7/+4
| | | | | | | | | | At that commit, I fixed a wrong conditional expression that was always true. However, that seemed to have caused a regression. [Bug #18906] This change removes the condition to make the code always enabled. It had been enabled until that commit, albeit unintentionally, and even if it is enabled it only consumes a tiny bit of memory, so I believe it is harmless. [Bug #18906]
* Remove ISEQ_MARKABLE_ISEQ flagAaron Patterson2022-07-071-11/+0
| | | | | We don't need this flag anymore. We have all the info we need via the bitmap and the is_entries list.
* Fix ISeq dump / load in array casesAaron Patterson2022-06-291-15/+7
| | | | | | | | | | | We need to dump relative offsets for inline storage entries so that loading iseqs as an array works as well. This commit also has some minor refactoring to make computing relative ISE information easier. This should fix the iseq dump / load as array tests we're seeing fail in CI. Co-Authored-By: John Hawthorn <john@hawthorn.email>
* Dump inline storage partition information to binary formatAaron Patterson2022-06-241-41/+17
| | | | | | | | | | | ISeqs loaded from binary were breaking because the storage partition calculation had bugs in it. Specifically it couldn't take in to account the case when inline storage was overallocated (for example when we allocate inline storage for an instruction but peephole optimization eliminates that instruction). `RUBY_ISEQ_DUMP_DEBUG=to_binary make test-all` would break, and this patch fixes it
* Free bitmap buffer if it's not usedAaron Patterson2022-06-231-2/+21
| | | | | If the iseqs don't have any objects in them that need marking, then immediately free the bitmap buffer
* Flatten bitmap when there is only one elementAaron Patterson2022-06-231-5/+38
| | | | | We can avoid allocating a bitmap when the number of elements in the iseq is fewer than the size of an iseq_bits_t
* Update vm_core.hAaron Patterson2022-06-231-1/+1
| | | Co-authored-by: Tomás Coêlho <36938811+tomascco@users.noreply.github.com>
* Speed up ISeq by marking via bitmaps and IC rearrangingAaron Patterson2022-06-231-25/+93
| | | | | | | | | | | | | | | | This commit adds a bitfield to the iseq body that stores offsets inside the iseq buffer that contain values we need to mark. We can use this bitfield to mark objects instead of disassembling the instructions. This commit also groups inline storage entries and adds a counter for each entry. This allows us to iterate and mark each entry without disassembling instructions Since we have a bitfield and grouped inline caches, we can mark all VALUE objects associated with instructions without actually disassembling the instructions at mark time. [Feature #18875] [ruby-core:109042]
* Remove unused function declarationPeter Zhu2022-06-171-2/+0
| | | | | iseq_alloc is not used in compile.c. It is also a static function declared in iseq.c so it's not accessible in compile.c.
* compile.c (add_adjust_info): Remove `insns_info_index > 0`Yusuke Endoh2022-06-131-2/+5
| | | | | | | | | | | | | | ... because insns_info_index could not be zero here. Also it adds an invariant check for that. This change will prevent the following warning of GCC 12.1 http://rubyci.s3.amazonaws.com/arch/ruby-master/log/20220613T000004Z.log.html.gz ``` compile.c:2230:39: warning: array subscript 2147483647 is outside array bounds of ‘struct iseq_insn_info_entry[2147483647]’ [-Warray-bounds] 2230 | insns_info[insns_info_index-1].line_no != adjust->line_no) { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ ```
* Add ISEQ_BODY macroPeter Zhu2022-03-241-168/+166
| | | | | | 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.
* Using macros to check iseq elementS.H2022-03-021-3/+3
|
* Fix indents [ci skip]Nobuyoshi Nakada2022-02-031-1/+1
|
* Treat TS_ICVARC cache as separate from TS_IVC cacheJemma Issroff2022-02-021-0/+5
|
* Fix constant assignment evaluation orderJeremy Evans2022-01-141-36/+77
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Previously, the right hand side was always evaluated before the left hand side for constant assignments. For the following: ```ruby lhs::C = rhs ``` rhs was evaluated before lhs, which is inconsistant with attribute assignment (lhs.m = rhs), and apparently also does not conform to JIS 3017:2013 11.4.2.2.3. Fix this by changing evaluation order. Previously, the above compiled to: ``` 0000 putself ( 1)[Li] 0001 opt_send_without_block <calldata!mid:rhs, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0003 dup 0004 putself 0005 opt_send_without_block <calldata!mid:lhs, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0007 setconstant :C 0009 leave ``` After this change: ``` 0000 putself ( 1)[Li] 0001 opt_send_without_block <calldata!mid:lhs, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0003 putself 0004 opt_send_without_block <calldata!mid:rhs, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0006 swap 0007 topn 1 0009 swap 0010 setconstant :C 0012 leave ``` Note that if expr is not a module/class, then a TypeError is not raised until after the evaluation of rhs. This is because that error is raised by setconstant. If we wanted to raise TypeError before evaluation of rhs, we would have to add a VM instruction for calling vm_check_if_namespace. Changing assignment order for single assignments caused problems in the multiple assignment code, revealing that the issue also affected multiple assignment. Fix the multiple assignment code so left-to-right evaluation also works for constant assignments. Do some refactoring of the multiple assignment code to reduce duplication after adding support for constants. Rename struct masgn_attrasgn to masgn_lhs_node, since it now handles both constants and attributes. Add add_masgn_lhs_node static function for adding data for lhs attribute and constant setting. Fixes [Bug #15928]
* Remove `NODE_DASGN_CURR` [Feature #18406]Nobuyoshi Nakada2021-12-131-8/+4
| | | | | | | This `NODE` type was used in pre-YARV implementation, to improve the performance of assignment to dynamic local variable defined at the innermost scope. It has no longer any actual difference with `NODE_DASGN`, except for the node dump.
* Avoid Array allocation when appending to args array (#5211)John Hawthorn2021-12-071-12/+44
| | | | | | | | | | | | | | | | | | | | | | | * Use duparray when possible for argspush ARGSPUSH is the node we see with a single value pushed to the end of a splatted array. ARGSCAT is similar, but is used when multiple values are being concatenated to the list. Previously only ARGSCAT had an optimization where when all the values were static it would use duparray instead of newarray to create the intermediate array. This commit adds similar behaviour for ARGSPUSH, using duparray instead of putobject/newarray. * Replace duparray with putobject before concatarray When performing duparray/concatarray we know we'll never use the intermediate array being created by duparray, so we should be able to use it as a temporary object. This avoids an extra array allocation for NODE_ARGSPUSH (ex. [*foo, 1]) and NODE_ARGSCAT (ex. [*foo, 1, 2]).
* Add `nd_type_p` macroS.H2021-12-041-45/+45
|
* Assign temporary ID to anonymous ID [Bug #18250]Nobuyoshi Nakada2021-11-231-0/+1
| | | | | | | | Dumped iseq binary can not have unnamed symbols/IDs, and ID 0 is stored instead. As `struct rb_id_table` disallows ID 0, also for the distinction, re-assign a new temporary ID based on the local variable table index when loading from the binary, as well as the parser.
* Refactor hacky ID tables to struct rb_ast_id_table_tYusuke Endoh2021-11-211-16/+12
| | | | | | | | | The implementation of a local variable tables was represented as `ID*`, but it was very hacky: the first element is not an ID but the size of the table, and, the last element is (sometimes) a link to the next local table only when the id tables are a linked list. This change converts the hacky implementation to a normal struct.
* optimize `Struct` getter/setterKoichi Sasada2021-11-191-95/+0
| | | | | Introduce new optimized method type `OPTIMIZED_METHOD_TYPE_STRUCT_AREF/ASET` with index information.
* Optimize dynamic string interpolation for symbol/true/false/nil/0-9Jeremy Evans2021-11-181-12/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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>
* compile.c: remove dead codeYusuke Endoh2021-11-181-1/+0
|
* compile.c: Fix typoYusuke Endoh2021-11-181-1/+1
|
* `Primitive.mandatory_only?` for fast pathKoichi Sasada2021-11-151-0/+77
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* Fix script_lines in loaded iseq as nilNobuyoshi Nakada2021-10-291-1/+2
|
* suppress warnings for probable NULL dererefencesNobuyoshi Nakada2021-10-241-0/+1
|
* `RubyVM.keep_script_lines`Koichi Sasada2021-10-211-1/+1
| | | | | | | | | | | | | | `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 ```
* Simplify code for YJIT const cache in compile.cAlan Wu2021-10-201-24/+11
| | | | | | | | | | Since opt_getinlinecache and opt_setinlinecache point to the same cache struct, there is no need to track the index of the get instruction and then store it on the cache struct later when processing the set instruction. Setting it when processing the get instruction works just as well. This change reduces our diff.
* Fix changes from rebaseNoah Gibbs2021-10-201-1/+1
|
* Simpler fix for -DUSE_EMBED_CI=0Alan Wu2021-10-201-2/+2
| | | | | Nobu pointed out that saving the old ci to a local is enough to keep it reachable.
* Revert "Fix use-after-free on USE_EMBED_CI=0"Alan Wu2021-10-201-12/+7
| | | | This reverts commit 1e0f2e4b09ca9443524acf4b50ffd50a80f330f3.
* Fix use-after-free on USE_EMBED_CI=0Alan Wu2021-10-201-7/+11
| | | | | The old code didn't keep old_operands[0] reachable while allocating. You can crash it by requiring erb under GC stress mode.
* YJIT: Fancier opt_getinlinecacheAlan Wu2021-10-201-2/+32
| | | | | | | 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.