Граф коммитов

615 Коммитов

Автор SHA1 Сообщение Дата
NARUSE, Yui f3abe5ba64 merge revision(s) 0700d0fd1c77b4fddf803dea3c10be654df600ff,62c2082f1f726cb90d8c332fbedbecf41d5d82ec: [Backport #19469]
Fix indentation in vm_setivar_default

	---
	 vm_insnhelper.c | 6 +++---
	 1 file changed, 3 insertions(+), 3 deletions(-)

	[Bug #19469] Fix crash when resizing generic iv list

	The following script can sometimes trigger a crash:

	```ruby
	GC.stress = true

	class Array
	  def foo(bool)
	    if bool
	      @a = 1
	      @b = 2
	      @c = 1
	    else
	      @c = 1
	    end
	  end
	end

	obj = []
	obj.foo(true)

	obj2 = []
	obj2.foo(false)

	obj3 = []
	obj3.foo(true)
	```

	This is because vm_setivar_default calls rb_ensure_generic_iv_list_size
	to resize the iv list. However, the call to gen_ivtbl_resize reallocs
	the iv list, and then inserts into the generic iv table. If the
	st_insert triggers a GC then the old iv list will be read during
	marking, causing a use-after-free bug.

	Co-Authored-By: Jemma Issroff <jemmaissroff@gmail.com>
	---
	 internal/variable.h |  2 +-
	 variable.c          | 23 ++++++++++++++++++-----
	 vm_insnhelper.c     |  4 ++--
	 3 files changed, 21 insertions(+), 8 deletions(-)
2023-03-17 13:40:04 +09:00
Peter Zhu d7388f720c Fix buffer overrun with auto-compact for shapes
The following script crashes:

```ruby
GC.auto_compact = true
GC.stress = true

class Foo
  def initialize
    @a = @b = @c = 0
  end

  def add_ivars
    @d = @e = @f = 0
  end
end

ary = 1_000.times.map { Foo.new }
ary.each { |f| f.add_ivars }
```

This is because in rb_grow_iv_list, it first calls
rb_ensure_iv_list_size to allocate the buffer (and also unsets the
embed bit) then rb_shape_transition_shape_capa to get the new shape.
However, auto-compact can trigger in rb_shape_transition_shape_capa
which would re-embed the object since it doesn't have the new shape yet.
This causes a crash as the object is now embedded but has a non-embed
shape which would cause the object to have a buffer overrun.
2022-12-22 09:23:40 -05:00
Jemma Issroff c1ab6ddc9a Transition complex objects to "too complex" shape
When an object becomes "too complex" (in other words it has too many
variations in the shape tree), we transition it to use a "too complex"
shape and use a hash for storing instance variables.

Without this patch, there were rare cases where shape tree growth could
"explode" and cause performance degradation on what would otherwise have
been cached fast paths.

This patch puts a limit on shape tree growth, and gracefully degrades in
the rare case where there could be a factorial growth in the shape tree.

For example:

```ruby
class NG; end

HUGE_NUMBER.times do
  NG.new.instance_variable_set(:"@unique_ivar_#{_1}", 1)
end
```

We consider objects to be "too complex" when the object's class has more
than SHAPE_MAX_VARIATIONS (currently 8) leaf nodes in the shape tree and
the object introduces a new variation (a new leaf node) associated with
that class.

For example, new variations on instances of the following class would be
considered "too complex" because those instances create more than 8
leaves in the shape tree:

```ruby
class Foo; end
9.times { Foo.new.instance_variable_set(":@uniq_#{_1}", 1) }
```

However, the following class is *not* too complex because it only has
one leaf in the shape tree:

```ruby
class Foo
  def initialize
    @a = @b = @c = @d = @e = @f = @g = @h = @i = nil
  end
end
9.times { Foo.new }
``

This case is rare, so we don't expect this change to impact performance
of most applications, but it needs to be handled.

Co-Authored-By: Aaron Patterson <tenderlove@ruby-lang.org>
2022-12-15 10:06:04 -08:00
Aaron Patterson edc7af48ac Stop transitioning to UNDEF when undefining an instance variable
Cases like this:

```ruby
obj = Object.new
loop do
  obj.instance_variable_set(:@foo, 1)
  obj.remove_instance_variable(:@foo)
end
```

can cause us to use many more shapes than we want (and even run out).
This commit changes the code such that when an instance variable is
removed, we'll walk up the shape tree, find the shape, then rebuild any
child nodes that happened to be below the "targetted for removal" IV.

This also requires moving any instance variables so that indexes derived
from the shape tree will work correctly.

Co-Authored-By: Jemma Issroff <jemmaissroff@gmail.com>
Co-authored-by: John Hawthorn <jhawthorn@github.com>
2022-12-07 09:57:11 -08:00
Nobuyoshi Nakada f28e79caaa
Use consistent style [ci skip] 2022-12-02 23:46:21 +09:00
Peter Zhu 1f0888ab3e Speed up shape transitions
This commit significantly speeds up shape transitions as it changes
get_next_shape_internal to not perform a lookup (and instead require
the caller to perform the lookup). This avoids double lookups during
shape transitions.

There is a significant (~2x) speedup in the following micro-benchmark:

    puts(Benchmark.measure do
      o = Object.new

      100_000.times do |i|
        o.instance_variable_set(:"@a#{i}", 0)
      end
    end)

Before:

    22.393194   0.201639  22.594833 ( 22.684237)

After:

    11.323086   0.022284  11.345370 ( 11.389346)
2022-11-21 10:22:29 -05:00
Peter Zhu 648927d71b Refactor obj_ivar_set and vm_setivar
obj_ivar_set and vm_setivar_slowpath is essentially doing the same thing,
but the code is duplicated and not quite implemented in the same way,
which could cause bugs. This commit refactors vm_setivar_slowpath to use
obj_ivar_set.
2022-11-21 09:58:53 -05:00
Aaron Patterson 10788166e7 Differentiate T_OBJECT shapes from other objects
We would like to differentiate types of objects via their shape.  This
commit adds a special T_OBJECT shape when we allocate an instance of
T_OBJECT.  This allows us to avoid testing whether an object is an
instance of a T_OBJECT or not, we can just check the shape.
2022-11-18 08:31:56 -08:00
S-H-GAMELINKS 1f4f6c9832 Using UNDEF_P macro 2022-11-16 18:58:33 +09:00
Jemma Issroff 7ee1cacb84 Extract `rb_shape_get_parent` helper
Extract an `rb_shape_get_parent` method instead of continually calling
`rb_shape_get_shape_by_id(shape->parent_id)`
2022-11-10 13:02:50 -05:00
Jemma Issroff c726c48a3d Remove numiv from RObject
Since object shapes store the capacity of an object, we no longer
need the numiv field on RObjects. This gives us one extra slot which
we can use to give embedded objects one more instance variable (for a
total of 3 ivs). This commit removes the concept of numiv from RObject.
2022-11-10 10:11:34 -05:00
Jemma Issroff 5246f4027e Transition shape when object's capacity changes
This commit adds a `capacity` field to shapes, and adds shape
transitions whenever an object's capacity changes. Objects which are
allocated out of a bigger size pool will also make a transition from the
root shape to the shape with the correct capacity for their size pool
when they are allocated.

This commit will allow us to remove numiv from objects completely, and
will also mean we can guarantee that if two objects share shapes, their
IVs are in the same positions (an embedded and extended object cannot
share shapes). This will enable us to implement ivar sets in YJIT using
object shapes.

Co-Authored-By: Aaron Patterson <tenderlove@ruby-lang.org>
2022-11-10 10:11:34 -05:00
Nobuyoshi Nakada 0ae5de1a5d
Adjust indents [ci skip] 2022-11-10 10:52:16 +09:00
Nobuyoshi Nakada 2d3ecc4d28
Adjust indents [ci skip] 2022-11-01 09:35:09 +09:00
Aaron Patterson 2d86e79fe6 Always lookup IV buffers when iterating
Always look up instance variable buffers when iterating.  It is possible
for the instance variable buffer to change out from under the object
during iteration, so we cannot cache the buffer on the stack.

In the case of Bug #19095, the transient heap moved the buffer during
iteration:

```
Watchpoint 1 hit:
old value: 0x0000000107c00df8
new value: 0x00000001032743c0
Process 31720 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
    frame #0: 0x00000001006e5178 miniruby`rb_obj_transient_heap_evacuate(obj=0x000000010d6b94b0, promote=1) at variable.c:1361:5
   1358	        }
   1359	        MEMCPY(new_ptr, old_ptr, VALUE, len);
   1360	        ROBJECT(obj)->as.heap.ivptr = new_ptr;
-> 1361	    }
   1362	}
   1363	#endif
   1364
miniruby`rb_obj_transient_heap_evacuate:
->  0x1006e5178 <+328>: b      0x1006e517c               ; <+332> at variable.c:1362:1
    0x1006e517c <+332>: ldp    x29, x30, [sp, #0x50]
    0x1006e5180 <+336>: add    sp, sp, #0x60
    0x1006e5184 <+340>: ret
Target 0: (miniruby) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
  * frame #0: 0x00000001006e5178 miniruby`rb_obj_transient_heap_evacuate(obj=0x000000010d6b94b0, promote=1) at variable.c:1361:5
    frame #1: 0x00000001006cb150 miniruby`transient_heap_block_evacuate(theap=0x0000000100b196c0, block=0x0000000107c00000) at transient_heap.c:734:17
    frame #2: 0x00000001006c854c miniruby`transient_heap_evacuate(dmy=0x0000000000000000) at transient_heap.c:808:17
    frame #3: 0x00000001007fe6c0 miniruby`rb_postponed_job_flush(vm=0x0000000104402900) at vm_trace.c:1773:21
    frame #4: 0x0000000100637a84 miniruby`rb_threadptr_execute_interrupts(th=0x0000000103803bc0, blocking_timing=0) at thread.c:2316:13
    frame #5: 0x000000010078b730 miniruby`rb_vm_check_ints(ec=0x00000001048038d0) at vm_core.h:2025:9
    frame #6: 0x00000001006fbd10 miniruby`vm_pop_frame(ec=0x00000001048038d0, cfp=0x0000000104a04440, ep=0x0000000104904a28) at vm_insnhelper.c:422:5
    frame #7: 0x00000001006fbca0 miniruby`rb_vm_pop_frame(ec=0x00000001048038d0) at vm_insnhelper.c:431:5
    frame #8: 0x00000001007d6420 miniruby`vm_call0_cfunc_with_frame(ec=0x00000001048038d0, calling=0x000000016fdcc6a0, argv=0x0000000000000000) at vm_eval.c:153:9
    frame #9: 0x00000001007d44cc miniruby`vm_call0_cfunc(ec=0x00000001048038d0, calling=0x000000016fdcc6a0, argv=0x0000000000000000) at vm_eval.c:164:12
    frame #10: 0x0000000100766e80 miniruby`vm_call0_body(ec=0x00000001048038d0, calling=0x000000016fdcc6a0, argv=0x0000000000000000) at vm_eval.c:210:15
    frame #11: 0x00000001007d76f0 miniruby`vm_call0_cc(ec=0x00000001048038d0, recv=0x000000010d6b49d8, id=2769, argc=0, argv=0x0000000000000000, cc=0x000000010d6b2e58, kw_splat=0) at vm_eval.c:87:12
    frame #12: 0x0000000100769e48 miniruby`rb_funcallv_scope(recv=0x000000010d6b49d8, mid=2769, argc=0, argv=0x0000000000000000, scope=CALL_FCALL) at vm_eval.c:1051:16
    frame #13: 0x0000000100760a54 miniruby`rb_funcallv(recv=0x000000010d6b49d8, mid=2769, argc=0, argv=0x0000000000000000) at vm_eval.c:1066:12
    frame #14: 0x000000010037513c miniruby`rb_inspect(obj=0x000000010d6b49d8) at object.c:633:34
    frame #15: 0x000000010002c950 miniruby`inspect_ary(ary=0x000000010d6b4938, dummy=0x0000000000000000, recur=0) at array.c:3091:13
    frame #16: 0x0000000100642020 miniruby`exec_recursive(func=(miniruby`inspect_ary at array.c:3084), obj=0x000000010d6b4938, pairid=0x0000000000000000, arg=0x0000000000000000, outer=0, mid=2769) at thread.c:5177:23
    frame #17: 0x00000001006412fc miniruby`rb_exec_recursive(func=(miniruby`inspect_ary at array.c:3084), obj=0x000000010d6b4938, arg=0x0000000000000000) at thread.c:5205:12
    frame #18: 0x00000001000127f0 miniruby`rb_ary_inspect(ary=0x000000010d6b4938) at array.c:3117:12
```

In general though, any calls back out to the interpreter could change
the IV buffer, so it's not safe to cache.

[Bug #19095]
2022-10-31 17:05:37 -07:00
John Hawthorn 02f1554224
Implement object shapes for T_CLASS and T_MODULE (#6637)
* Avoid RCLASS_IV_TBL in marshal.c
* Avoid RCLASS_IV_TBL for class names
* Avoid RCLASS_IV_TBL for autoload
* Avoid RCLASS_IV_TBL for class variables
* Avoid copying RCLASS_IV_TBL onto ICLASSes
* Use object shapes for Class and Module IVs
2022-10-31 14:05:37 -07:00
Nobuyoshi Nakada 3af373285b
Adjust indents [ci skip] 2022-10-24 17:42:29 +09:00
S-H-GAMELINKS 298221dfe5 Reuse RBOOL macro in rb_ivar_defined function 2022-10-24 10:25:01 +02:00
Jemma Issroff a11952dac1 Rename `iv_count` on shapes to `next_iv_index`
`iv_count` is a misleading name because when IVs are unset, the new
shape doesn't decrement this value. `next_iv_count` is an accurate, and
more descriptive name.
2022-10-21 14:57:34 -07:00
Aaron Patterson eeea633eb2 Stop zeroing memory on allocation / copy
Shapes gives us an almost exact count of instance variables on an
object.  Since we know the number of instance variables that have been
set, we will never access slots that haven't been initialized with an
IV.
2022-10-19 07:54:46 -07:00
Aaron Patterson f0654b1027 More precisely iterate over Object instance variables
Shapes provides us with an (almost) exact count of instance variables.
We only need to check for Qundef when an IV has been "undefined"
Prefer to use ROBJECT_IV_COUNT when iterating IVs
2022-10-15 10:44:10 -07:00
Nobuyoshi Nakada 1e3fa634f7
Explicitly cast to uint32_t and suppress warnings by VC
```
../src/variable.c(1440): warning C4244: 'initializing': conversion from 'double' to 'uint32_t', possible loss of data
242
../src/variable.c(1470): warning C4244: 'initializing': conversion from 'double' to 'uint32_t', possible loss of data
243
```

TODO: check for `newsize` overflow
2022-10-16 00:26:51 +09:00
Aaron Patterson 1acc1a5c6d YJIT doesn't need rb_obj_ensure_iv_index_mapping
We should make this function static and remove it from YJIT bindings.
2022-10-14 17:14:41 -07:00
Jemma Issroff 913979bede
Make inline cache reads / writes atomic with object shapes
Prior to this commit, we were reading and writing ivar index and
shape ID in inline caches in two separate instructions when
getting and setting ivars. This meant there was a race condition
with ractors and these caches where one ractor could change
a value in the cache while another was still reading from it.

This commit instead reads and writes shape ID and ivar index to
inline caches atomically so there is no longer a race condition.

Co-Authored-By: Aaron Patterson <tenderlove@ruby-lang.org>
Co-Authored-By: John Hawthorn <john@hawthorn.email>
2022-10-11 08:40:56 -07:00
Jemma Issroff ad63b668e2
Revert "Revert "This commit implements the Object Shapes technique in CRuby.""
This reverts commit 9a6803c90b.
2022-10-11 08:40:56 -07:00
Aaron Patterson 9a6803c90b
Revert "This commit implements the Object Shapes technique in CRuby."
This reverts commit 68bc9e2e97d12f80df0d113e284864e225f771c2.
2022-09-30 16:01:50 -07:00
git 414f1066b1 * 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-29 00:27:12 +09:00
Jemma Issroff d594a5a8bd
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-28 08:26:21 -07:00
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 830b5b5c35.

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

This reverts commit 9ddfd2ca00.
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
S-H-GAMELINKS f095361758 Repalce to NIL_P macro 2022-08-19 09:47:43 +09:00
Takashi Kokubun 5b21e94beb Expand tabs [ci skip]
[Misc #18891]
2022-07-21 09:42:04 -07:00
Peter Zhu 7424ea184f Implement Objects on VWA
This commit implements Objects on Variable Width Allocation. This allows
Objects with more ivars to be embedded (i.e. contents directly follow the
object header) which improves performance through better cache locality.
2022-07-15 09:21:07 -04:00
Nobuyoshi Nakada 2361a1a53e
Get rid of a nasal demon
On platforms not having `typeof`, `ccan_container_off_var()` macro
subtracts the pointer variable from the member address pointed by that
variable.
2022-06-23 16:42:57 +09:00
Jean Boussier eca31d24d6 [Bug #18813] Warn when autoload has to lookup in parent namespace
This is a verbose mode only warning.
2022-06-18 14:49:02 +02:00
Jeremy Evans c85d1cda86 Fix Module#const_source_location for autoload constants with direct requires
If an autoload exists for a constant, but the path for the autoload
was required, const_source_location would return [false, 0] instead
of the actual file and line. This fixes it by setting the appropriate
file and line in rb_const_set, and saving the file and line in
const_tbl_update before they get reset by current_autoload_data.

Fixes [Bug #18624]
2022-06-06 11:12:55 -07:00
Samuel Williams 946e3bbdc2 Simplify the autoload require logic. 2022-05-26 14:20:59 +12:00
Samuel Williams d875445e8a Fix GC race condition in autoload. 2022-05-26 00:17:30 +12:00
Samuel Williams 9a8fc4e04c Tidy up redundant returns. 2022-05-25 15:24:24 +12:00
Samuel Williams 42bcc629fb Retain reference to blocking fibers. 2022-05-25 15:24:24 +12:00
Nobuyoshi Nakada 72fc3b1bbb
Fold too long lines, etc [ci skip] 2022-05-19 16:46:08 +09:00
Nobuyoshi Nakada a72529182d
Suppress unused-function [ci skip] 2022-05-19 16:46:08 +09:00
Samuel Williams 11af23ee92
Move feature deletion from GC mark to `autoload_delete`. (#5912) 2022-05-17 23:44:14 +12:00
Samuel Williams 60d45b2ee8
Restore implicit relationship between `autoload_const` and `autoload_data` during GC. (#5911) 2022-05-17 19:12:36 +12:00
Nobuyoshi Nakada 8a907da0f6
Suppress an unused-but-set-variable warning [ci skip] 2022-05-17 13:55:56 +09:00
Samuel Williams f626998c4f
Delete autoload data from global features after autoload has completed. (#5910)
* Update naming of critical section assertions macros.

* Improved locking for autoload.
2022-05-17 00:50:02 +12:00
Samuel Williams 32de6097b2
Fix various autoload race conditions. (#5898)
* Add RUBY_VM_CRITICAL_SECTION for detecting unexpected context switch.

* Prevent race between GC mark and autoload setup.

* Protect race on autoload state.

* Avoid potential race condition when allocating `autoload_featuremap`.

* Add NEWS entry for autoload fixes.
2022-05-15 16:07:12 +12:00
Samuel Williams fd6cef79f5
Use a proper mutex for autoloading features. (#5788)
Object#autoload implements a custom per-thread "mutex" for blocking
threads waiting on autoloading a feature. This causes problems when used
with the fiber scheduler. We swap the implementation to use a Ruby mutex
which is fiber aware.
2022-05-08 10:22:58 +12:00
Koichi Sasada c77a31df5e call `const_added` after `autoload`
When calling `const_added` while process in `autoload`, it can
cause synchronization issue because of a thread swithcing.

http://ci.rvm.jp/logfiles/brlog.trunk.20220407-152213#L489

```
1)
Module#autoload (concurrently) raises a LoadError in each thread if the file does not exist ERROR
NameError: uninitialized constant ModuleSpecs::Autoload::FileDoesNotExist

            ModuleSpecs::Autoload::FileDoesNotExist
                                 ^^^^^^^^^^^^^^^^^^
/tmp/ruby/v3/src/trunk/spec/ruby/core/module/autoload_spec.rb:965:in `block (5 levels) in <top (required)>'
```
2022-04-08 21:30:32 +09:00