1554 Commits

Author SHA1 Message Date
Aaron Patterson
06abfa5be6
Revert this until we can figure out WB issues or remove shapes from GC
Revert "* expand tabs. [ci skip]"

This reverts commit 830b5b5c351c5c6efa5ad461ae4ec5085e5f0275.

Revert "This commit implements the Object Shapes technique in CRuby."

This reverts commit 9ddfd2ca004d1952be79cf1b84c52c79a55978f4.
2022-09-26 16:10:11 -07:00
git
830b5b5c35 * expand tabs. [ci skip]
Tabs were expanded because the file did not have any tab indentation in unedited lines.
Please update your editor config, and use misc/expand_tabs.rb in the pre-commit hook.
2022-09-27 01:21:58 +09:00
Jemma Issroff
9ddfd2ca00 This commit implements the Object Shapes technique in CRuby.
Object Shapes is used for accessing instance variables and representing the
"frozenness" of objects.  Object instances have a "shape" and the shape
represents some attributes of the object (currently which instance variables are
set and the "frozenness").  Shapes form a tree data structure, and when a new
instance variable is set on an object, that object "transitions" to a new shape
in the shape tree.  Each shape has an ID that is used for caching. The shape
structure is independent of class, so objects of different types can have the
same shape.

For example:

```ruby
class Foo
  def initialize
    # Starts with shape id 0
    @a = 1 # transitions to shape id 1
    @b = 1 # transitions to shape id 2
  end
end

class Bar
  def initialize
    # Starts with shape id 0
    @a = 1 # transitions to shape id 1
    @b = 1 # transitions to shape id 2
  end
end

foo = Foo.new # `foo` has shape id 2
bar = Bar.new # `bar` has shape id 2
```

Both `foo` and `bar` instances have the same shape because they both set
instance variables of the same name in the same order.

This technique can help to improve inline cache hits as well as generate more
efficient machine code in JIT compilers.

This commit also adds some methods for debugging shapes on objects.  See
`RubyVM::Shape` for more details.

For more context on Object Shapes, see [Feature: #18776]

Co-Authored-By: Aaron Patterson <tenderlove@ruby-lang.org>
Co-Authored-By: Eileen M. Uchitelle <eileencodes@gmail.com>
Co-Authored-By: John Hawthorn <john@hawthorn.email>
2022-09-26 09:21:30 -07:00
John Hawthorn
b361bdc200 [Bug #19021] Fix safe call w/ conditional assign
As of fbaac837cfba23a9d34dc7ee144d7940248222a2, when we were performing
a safe call (`o&.x=`) with a conditional assign (`||= 1`) and discarding
the result the stack would end up in a bad state due to a missing pop.

This commit fixes that by adjusting the target label of the branchnil to
be before a pop in that case (as was previously done in the
non-conditional assignment case).
2022-09-25 20:44:54 -07:00
Samuel Williams
85cc0ce5c8 Use int first_lineno for binary format. 2022-09-26 00:41:16 +13:00
Samuel Williams
22af2e9084 Rework vm_core to use int first_lineno struct member. 2022-09-26 00:41:16 +13:00
Samuel Williams
75cf29f60d Rework first_lineno to be int. 2022-09-26 00:41:16 +13:00
HParker
fbaac837cf avoid extra dup and pop in compile_op_asgn2
Co-authored-by: John Hawthorn <jhawthorn@github.com>
2022-09-22 09:47:13 -07:00
HParker
aafbc9068f avoid extra dup and pop in compile_op_log
Co-authored-by: John Hawthorn <jhawthorn@github.com>
2022-09-22 09:47:13 -07:00
Samuel Williams
9434a7333c Enable coverage for eval. 2022-09-22 22:19:12 +12:00
Maple Ong
89077b4c5a
Add comments for some peephole optimizations [ci skip] 2022-09-12 07:50:55 +09:00
Nobuyoshi Nakada
92d2476208
Adjust styles [ci skip] 2022-09-02 14:49:42 +09:00
John Hawthorn
fc2d9fedc2 Use getblockparamproxy with branch
A common pattern when the block is an explicit parameter is to branch
based on the block parameter instead of using `block_given?`, for
example `block.call if block`.

This commit checks in the peephole optimizer for that case and uses the
getblockparamproxy optimization, which avoids allocating a proc for
simple cases, whenever a getblockparam instruction is followed
immediately by branchif or branchunless.

    ./miniruby --dump=insns -e 'def foo(&block); 123 if block; end'

    == disasm: #<ISeq:foo@-e:1 (1,0)-(1,34)> (catch: FALSE)
    local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: 0, kw: -1@-1, kwrest: -1])
    [ 1] block@0<Block>
    0000 getblockparamproxy                     block@0, 0                (   1)[LiCa]
    0003 branchunless                           8
    0005 putobject                              123
    0007 leave                                  [Re]
    0008 putnil
    0009 leave                                  [Re]
2022-09-01 17:36:20 -07:00
git
3401e58f23 * expand tabs. [ci skip]
Tabs were expanded because the file did not have any tab indentation in unedited lines.
Please update your editor config, and use misc/expand_tabs.rb in the pre-commit hook.
2022-09-02 07:21:12 +09:00
John Hawthorn
679ef34586 New constant caching insn: opt_getconstant_path
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.
2022-09-01 15:20:49 -07:00
Takashi Kokubun
d6f21b308b
Convert catch_except_t to stdbool
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.
2022-08-25 23:00:19 -07:00
Jeremy Evans
9363b0423a Optimize duparray/expandarray -> putobject/expandarray
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.
2022-08-09 22:19:46 -07:00
Jeremy Evans
fc4b4f2e8d Expand newarray/expandarray optimization for unequal operands
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).
2022-08-09 22:19:46 -07:00
Jeremy Evans
5089b6acc7 Add peephole optimizer for newarray(X)/expandarray(X, 0) -> opt_reverse(X)
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.
2022-08-09 22:19:46 -07:00
Jeremy Evans
9f8abd28ba Add peephole optimizer for newarray(2)/expandarray(2, 0) -> swap
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.
2022-08-09 22:19:46 -07:00
Nobuyoshi Nakada
f42230ff22
Adjust styles [ci skip] 2022-07-27 18:42:27 +09:00
Peter Zhu
efb91ff19b Rename rb_ary_tmp_new to rb_ary_hidden_new
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.
2022-07-26 09:12:09 -04:00
Nobuyoshi Nakada
721d154e2f
Remove duplicate code for internal arrays
Internal arrays are now created hidden from the start.
2022-07-23 21:42:05 +09:00
Peter Zhu
98a8a496ba Use rb_ary_tmp_new only for internal arrays
rb_ary_tmp_new sets the klass to 0, so it should only be used for
internal arrays.
2022-07-22 15:44:32 -04:00
Peter Zhu
e199ae3edc Remove reference counting for all frozen arrays
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.
2022-07-22 13:29:21 -04:00
Yusuke Endoh
8f7e188822 Add "rb_" prefixes to toplevel enum definitions
... as per ko1's request.
2022-07-22 23:10:24 +09:00
Takashi Kokubun
5b21e94beb Expand tabs [ci skip]
[Misc #18891]
2022-07-21 09:42:04 -07:00
Peter Zhu
5871ecf956 Add RARRAY_LITERAL_FLAG for array literals
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>
2022-07-20 13:13:56 -04:00
Jemma Issroff
85ea46730d Separate TS_IVC and TS_ICVARC in is_entries buffers
This allows us to treat cvar caches differently than ivar caches.
2022-07-18 14:06:30 -07:00
Nobuyoshi Nakada
8b98b9e274
Check only whether RUBY_DEVEL is defined 2022-07-12 17:13:57 +09:00
Yusuke Endoh
a871fc4d86 Fix a regression of b2e58b02aec73f9c350bf109c021c180fc699ccc
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]
2022-07-11 23:38:37 +09:00
Aaron Patterson
3cf2c2e4a1 Remove ISEQ_MARKABLE_ISEQ flag
We don't need this flag anymore.  We have all the info we need via the
bitmap and the is_entries list.
2022-07-07 11:56:25 -07:00
Aaron Patterson
e3ab525f69 Fix ISeq dump / load in array cases
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>
2022-06-29 16:21:48 -07:00
Aaron Patterson
87e2e3f383 Dump inline storage partition information to binary format
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
2022-06-24 15:04:00 -07:00
Aaron Patterson
0b58059f15 Free bitmap buffer if it's not used
If the iseqs don't have any objects in them that need marking, then
immediately free the bitmap buffer
2022-06-23 16:52:00 -07:00
Aaron Patterson
8d63a04703 Flatten bitmap when there is only one element
We can avoid allocating a bitmap when the number of elements in the iseq
is fewer than the size of an iseq_bits_t
2022-06-23 16:52:00 -07:00
Aaron Patterson
1ccdb1a251 Update vm_core.h
Co-authored-by: Tomás Coêlho <36938811+tomascco@users.noreply.github.com>
2022-06-23 14:01:46 -07:00
Aaron Patterson
e23540e566 Speed up ISeq by marking via bitmaps and IC rearranging
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]
2022-06-23 14:01:46 -07:00
Peter Zhu
2790bddda6 Remove unused function declaration
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.
2022-06-17 09:44:17 -04:00
Yusuke Endoh
b2e58b02ae compile.c (add_adjust_info): Remove insns_info_index > 0
... 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) {
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
```
2022-06-13 15:22:32 +09:00
Peter Zhu
5f10bd634f Add ISEQ_BODY macro
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.
2022-03-24 10:03:51 -04:00
S.H
f7491e89b9
Using macros to check iseq element 2022-03-02 09:27:30 +09:00
Nobuyoshi Nakada
8f3a36fb6e
Fix indents [ci skip] 2022-02-03 11:21:41 +09:00
Jemma Issroff
2913a2f5cf Treat TS_ICVARC cache as separate from TS_IVC cache 2022-02-02 09:20:34 -08:00
Jeremy Evans
ca3d405242 Fix constant assignment evaluation order
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]
2022-01-14 11:00:26 -08:00
Nobuyoshi Nakada
54f0e63a8c Remove NODE_DASGN_CURR [Feature #18406]
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.
2021-12-13 12:53:03 +09:00
John Hawthorn
4a3e7984bf
Avoid Array allocation when appending to args array (#5211)
* 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]).
2021-12-07 15:18:11 -08:00
S.H
ec7f14d9fa
Add nd_type_p macro 2021-12-04 00:01:24 +09:00
Nobuyoshi Nakada
c14f230b26 Assign temporary ID to anonymous ID [Bug #18250]
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.
2021-11-23 21:03:19 +09:00
Yusuke Endoh
feda058531 Refactor hacky ID tables to struct rb_ast_id_table_t
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.
2021-11-21 08:59:24 +09:00